Merge SP1A.211205.008
Bug: 205056467
Merged-In: Ic0dabce4beb09bdd966152c059730677ca5bf5aa
Change-Id: Ic9d5fe40d265ef8002ccbcf3182a2fb8b6239135
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 47fd53a..957da92 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -757,6 +757,22 @@
# vendor-ramdisk renamed to vendor_ramdisk
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor-ramdisk)
+# Common R directory has been removed.
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R)
+
+# Most of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES
+$(call add-clean-step, rm -rf $(SOONG_HOST_OUT))
+
+# More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES
+$(call add-clean-step, rm -rf $(SOONG_HOST_OUT))
+
+# More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES
+$(call add-clean-step, rm -rf $(SOONG_HOST_OUT))
+
+# Last of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES
+# Don't use SOONG_HOST_OUT, it is now an alias for HOST_OUT.
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/host)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/common/math.mk b/common/math.mk
index ec15f88..0271ea8 100644
--- a/common/math.mk
+++ b/common/math.mk
@@ -121,14 +121,26 @@
$(lastword $(filter $(1) $(2),$(__MATH_NUMBERS))))
endef
+# Returns the lesser of $1 or $2.
+define math_min
+$(strip $(call _math_check_valid,$(1)) $(call _math_check_valid,$(2)) \
+ $(firstword $(filter $(1) $(2),$(__MATH_NUMBERS))))
+endef
+
$(call math-expect-error,(call math_max),Argument missing)
$(call math-expect-error,(call math_max,1),Argument missing)
$(call math-expect-error,(call math_max,1 2,3),Multiple words in a single argument: 1 2)
+$(call math-expect-error,(call math_min,1,2 3),Multiple words in a single argument: 2 3)
$(call math-expect,(call math_max,0,1),1)
$(call math-expect,(call math_max,1,0),1)
$(call math-expect,(call math_max,1,1),1)
$(call math-expect,(call math_max,5,42),42)
$(call math-expect,(call math_max,42,5),42)
+$(call math-expect,(call math_min,0,1),0)
+$(call math-expect,(call math_min,1,0),0)
+$(call math-expect,(call math_min,1,1),1)
+$(call math-expect,(call math_min,7,32),7)
+$(call math-expect,(call math_min,32,7),7)
define math_gt_or_eq
$(if $(filter $(1),$(call math_max,$(1),$(2))),true)
diff --git a/common/strings.mk b/common/strings.mk
index e560bf0..768d061 100644
--- a/common/strings.mk
+++ b/common/strings.mk
@@ -88,6 +88,24 @@
endef
###########################################################
+## Read a colon-separated sublist out of a colon-separated
+## list of words.
+## This has similar behavior to the built-in function
+## $(wordlist s,e,str) except both the input and output
+## word lists are colon-separated.
+##
+## The individual words may not contain spaces.
+##
+## $(1): 1 based index start
+## $(2): 1 based index end (can be 0)
+## $(3): value of the form a:b:c...
+###########################################################
+
+define wordlist-colon
+$(subst $(space),:,$(wordlist $(1),$(2),$(subst :,$(space),$(3))))
+endef
+
+###########################################################
## Convert "a=b c= d e = f = g h=" into "a=b c=d e= f=g h="
##
## $(1): list to collapse
diff --git a/core/Makefile b/core/Makefile
index 2d56edb..a580ac8 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -163,7 +163,9 @@
.PHONY: ndk-docs
endif
+ifeq ($(HOST_OS),linux)
$(call dist-for-goals,sdk,$(API_FINGERPRINT))
+endif
INSTALLED_RECOVERYIMAGE_TARGET :=
# Build recovery image if
@@ -206,7 +208,7 @@
@mkdir -p $(dir $@)
$(hide) grep -v "$(subst $(space),\|,$(strip \
$(sdk_build_prop_remove)))" $< > $@.tmp
- $(hide) for x in $(sdk_build_prop_remove); do \
+ $(hide) for x in $(strip $(sdk_build_prop_remove)); do \
echo "$$x"generic >> $@.tmp; done
$(hide) mv $@.tmp $@
@@ -320,11 +322,11 @@
define build-image-kernel-modules-depmod
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: .KATI_IMPLICIT_OUTPUTS := $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.alias $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5)
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(DEPMOD)
-$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(1)
+$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(strip $(1))
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MOUNT_POINT := $(2)
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules/$(8)
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_STAGING_DIR := $(3)
-$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(4)
+$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(strip $(4))
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_FILE := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5)
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_ARCHIVE := $(6)
$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_OUTPUT_DIR := $(7)
@@ -487,6 +489,12 @@
endif
endif
+ifneq ($(BOARD_DO_NOT_STRIP_RECOVERY_MODULES),true)
+ RECOVERY_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_recovery_stripped)
+else
+ RECOVERY_STRIPPED_MODULE_STAGING_DIR :=
+endif
+
ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_MODULES),true)
VENDOR_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_stripped)
else
@@ -501,7 +509,7 @@
BOARD_KERNEL_MODULE_DIRS += top
$(foreach kmd,$(BOARD_KERNEL_MODULE_DIRS), \
- $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,,$(kmd))) \
+ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,$(RECOVERY_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \
$(eval vendor_ramdisk_fragment := $(KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd))) \
$(if $(vendor_ramdisk_fragment), \
$(eval output_dir := $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR)) \
@@ -519,6 +527,16 @@
$(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,GENERIC_RAMDISK,$(TARGET_RAMDISK_OUT),,modules.load,,$(kmd)))))
# -----------------------------------------------------------------
+# FSVerity metadata generation
+ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
+
+FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)
+FSVERITY_APK_OUT := system/etc/security/fsverity/BuildManifest.apk
+FSVERITY_APK_MANIFEST_PATH := system/security/fsverity/AndroidManifest.xml
+
+endif # PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA
+
+# -----------------------------------------------------------------
# Cert-to-package mapping. Used by the post-build signing tools.
# Use a macro to add newline to each echo command
# $1 stem name of the package
@@ -567,6 +585,8 @@
$(if $(PACKAGES.$(p).EXTERNAL_KEY),\
$(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),EXTERNAL,,$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@),\
$(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),$(PACKAGES.$(p).CERTIFICATE),$(PACKAGES.$(p).PRIVATE_KEY),$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@))))
+ $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),\
+ $(call _apkcerts_write_line,$(notdir $(basename $(FSVERITY_APK_OUT))),$(FSVERITY_APK_KEY_PATH).x509.pem,$(FSVERITY_APK_KEY_PATH).pk8,,system,$@))
# In case value of PACKAGES is empty.
$(hide) touch $@
@@ -769,7 +789,9 @@
$(FILESLIST) $(TARGET_ROOT_OUT) > $(@:.txt=.json)
$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
-$(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE_ROOT))
+ifeq ($(HOST_OS),linux)
+$(call dist-for-goals, sdk sdk_addon, $(INSTALLED_FILES_FILE_ROOT))
+endif
#------------------------------------------------------------------
# dtb
@@ -799,7 +821,9 @@
$(FILESLIST) $(TARGET_RAMDISK_OUT) > $(@:.txt=.json)
$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
+ifeq ($(HOST_OS),linux)
$(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE_RAMDISK))
+endif
BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
@@ -815,8 +839,13 @@
# We just build this directly to the install location.
INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)
+$(INSTALLED_RAMDISK_TARGET): PRIVATE_DIRS := debug_ramdisk dev metadata mnt proc second_stage_resources sys
$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES) $(INSTALLED_FILES_FILE_RAMDISK) | $(COMPRESSION_COMMAND_DEPS)
$(call pretty,"Target ramdisk: $@")
+ $(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(PRIVATE_DIRS))
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
+ $(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(PRIVATE_DIRS))
+endif
$(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@
.PHONY: ramdisk-nodeps
@@ -1085,12 +1114,10 @@
$(INTERNAL_VENDOR_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
-ifeq (true,$(BOARD_BUILD_VENDOR_RAMDISK_IMAGE))
INSTALLED_VENDOR_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk.img
$(INSTALLED_VENDOR_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET)
- $(call pretty,"Target vendor ramdisk: $@")
+ @echo "Target vendor ramdisk: $@"
$(copy-file-to-target)
-endif
INSTALLED_FILES_FILE_VENDOR_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk.txt
INSTALLED_FILES_JSON_VENDOR_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_RAMDISK:.txt=.json)
@@ -1276,6 +1303,9 @@
$(hide) $(MINIGZIP) -9 < $< > $@
$(installed_notice_html_or_xml_gz): $(target_notice_file_html_gz)
$(copy-file-to-target)
+
+$(call declare-0p-target,$(target_notice_file_html_gz))
+$(call declare-0p-target,$(installed_notice_html_or_xml_gz))
else
target_notice_file_xml := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml
target_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml.gz
@@ -1459,6 +1489,28 @@
$(installed_odm_dlkm_notice_xml_gz): $(target_odm_dlkm_notice_file_xml_gz)
$(copy-file-to-target)
+$(call declare-0p-target,$(target_notice_file_xml))
+$(call declare-0p-target,$(target_notice_file_xml_gz))
+$(call declare-0p-target,$(target_vendor_notice_file_xml))
+$(call declare-0p-target,$(target_vendor_notice_file_xml_gz))
+$(call declare-0p-target,$(target_product_notice_file_xml))
+$(call declare-0p-target,$(target_product_notice_file_xml_gz))
+$(call declare-0p-target,$(target_system_ext_notice_file_xml))
+$(call declare-0p-target,$(target_system_ext_notice_file_xml_gz))
+$(call declare-0p-target,$(target_odm_notice_file_xml))
+$(call declare-0p-target,$(target_odm_notice_file_xml_gz))
+$(call declare-0p-target,$(target_vendor_dlkm_notice_file_xml))
+$(call declare-0p-target,$(target_vendor_dlkm_notice_file_xml_gz))
+$(call declare-0p-target,$(target_odm_dlkm_notice_file_xml))
+$(call declare-0p-target,$(target_odm_dlkm_notice_file_xml_gz))
+$(call declare-0p-target,$(installed_notice_html_or_xml_gz))
+$(call declare-0p-target,$(installed_vendor_notice_xml_gz))
+$(call declare-0p-target,$(installed_product_notice_xml_gz))
+$(call declare-0p-target,$(installed_system_ext_notice_xml_gz))
+$(call declare-0p-target,$(installed_odm_notice_xml_gz))
+$(call declare-0p-target,$(installed_vendor_dlkm_notice_xml_gz))
+$(call declare-0p-target,$(installed_odm_dlkm_notice_xml_gz))
+
ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_or_xml_gz)
ALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_notice_xml_gz)
ALL_DEFAULT_INSTALLED_MODULES += $(installed_product_notice_xml_gz)
@@ -1545,6 +1597,7 @@
$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \
,erofs),)
INTERNAL_USERIMAGES_DEPS += $(MKEROFSUSERIMG)
+BOARD_EROFS_COMPRESSOR ?= "lz4hc,9"
endif
ifneq ($(filter \
@@ -1590,31 +1643,59 @@
endif # PRODUCT_USE_DYNAMIC_PARTITIONS
+# $(1) the partition name (eg system)
+# $(2) the image prop file
+define add-common-flags-to-image-props
+$(eval _var := $(call to-upper,$(1)))
+$(hide) echo "$(1)_selinux_fc=$(SELINUX_FC)" >> $(2)
+$(hide) echo "building_$(1)_image=$(BUILDING_$(_var)_IMAGE)" >> $(2)
+endef
+
+# $(1) the partition name (eg system)
+# $(2) the image prop file
+define add-common-ro-flags-to-image-props
+$(eval _var := $(call to-upper,$(1)))
+$(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR),$(hide) echo "$(1)_erofs_compressor=$(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE),$(hide) echo "$(1)_erofs_pcluster_size=$(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT),$(hide) echo "$(1)_extfs_inode_count=$(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT),$(hide) echo "$(1)_extfs_rsv_pct=$(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "$(1)_f2fs_sldc_flags=$(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "$(1)_f2fs_compress=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE),$(hide) echo "$(1)_fs_type=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_JOURNAL_SIZE),$(hide) echo "$(1)_journal_size=$(BOARD_$(_var)IMAGE_JOURNAL_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "$(1)_reserved_size=$(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_PARTITION_SIZE),$(hide) echo "$(1)_size=$(BOARD_$(_var)IMAGE_PARTITION_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "$(1)_squashfs_block_size=$(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "$(1)_squashfs_compressor=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "$(1)_squashfs_compressor_opt=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "$(1)_squashfs_disable_4k_align=$(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(2))
+$(if $(PRODUCT_$(_var)_BASE_FS_PATH),$(hide) echo "$(1)_base_fs_file=$(PRODUCT_$(_var)_BASE_FS_PATH)" >> $(2))
+$(eval _size := $(BOARD_$(_var)IMAGE_PARTITION_SIZE))
+$(eval _reserved := $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE))
+$(eval _headroom := $(PRODUCT_$(_var)_HEADROOM))
+$(if $(or $(_size), $(_reserved), $(_headroom)),,
+ $(hide) echo "$(1)_disable_sparse=true" >> $(2))
+$(call add-common-flags-to-image-props,$(1),$(2))
+endef
+
# $(1): the path of the output dictionary file
# $(2): a subset of "system vendor cache userdata product system_ext oem odm vendor_dlkm odm_dlkm"
# $(3): additional "key=value" pairs to append to the dictionary file.
define generate-image-prop-dictionary
$(if $(filter $(2),system),\
- $(if $(BOARD_SYSTEMIMAGE_PARTITION_SIZE),$(hide) echo "system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo "system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_fs_type=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "system_f2fs_compress=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "system_f2fs_sldc_flags=$(BOARD_SYSTEMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "system_extfs_inode_count=$(BOARD_SYSTEMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_EXTFS_RSV_PCT),$(hide) echo "system_extfs_rsv_pct=$(BOARD_SYSTEMIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_JOURNAL_SIZE),$(hide) echo "system_journal_size=$(BOARD_SYSTEMIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_squashfs_compressor=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_squashfs_compressor_opt=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "system_squashfs_block_size=$(BOARD_SYSTEMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "system_squashfs_disable_4k_align=$(BOARD_SYSTEMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(PRODUCT_SYSTEM_BASE_FS_PATH),$(hide) echo "system_base_fs_file=$(PRODUCT_SYSTEM_BASE_FS_PATH)" >> $(1))
$(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo "system_headroom=$(PRODUCT_SYSTEM_HEADROOM)" >> $(1))
- $(if $(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "system_reserved_size=$(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "system_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_system_image=$(BUILDING_SYSTEM_IMAGE)" >> $(1)
+ $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity=$(HOST_OUT_EXECUTABLES)/fsverity" >> $(1))
+ $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_generate_metadata=true" >> $(1))
+ $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_key=$(FSVERITY_APK_KEY_PATH)" >> $(1))
+ $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_manifest=$(FSVERITY_APK_MANIFEST_PATH)" >> $(1))
+ $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_out=$(FSVERITY_APK_OUT)" >> $(1))
+ $(call add-common-ro-flags-to-image-props,system,$(1))
)
$(if $(filter $(2),system_other),\
$(hide) echo "building_system_other_image=$(BUILDING_SYSTEM_OTHER_IMAGE)" >> $(1)
+ $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),,
+ $(hide) echo "system_other_disable_sparse=true" >> $(1))
)
$(if $(filter $(2),userdata),\
$(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1622,116 +1703,37 @@
$(if $(PRODUCT_FS_CASEFOLD),$(hide) echo "needs_casefold=$(PRODUCT_FS_CASEFOLD)" >> $(1))
$(if $(PRODUCT_QUOTA_PROJID),$(hide) echo "needs_projid=$(PRODUCT_QUOTA_PROJID)" >> $(1))
$(if $(PRODUCT_FS_COMPRESSION),$(hide) echo "needs_compress=$(PRODUCT_FS_COMPRESSION)" >> $(1))
- $(hide) echo "userdata_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_userdata_image=$(BUILDING_USERDATA_IMAGE)" >> $(1)
+ $(call add-common-flags-to-image-props,userdata,$(1))
)
$(if $(filter $(2),cache),\
$(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1))
- $(hide) echo "cache_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_cache_image=$(BUILDING_CACHE_IMAGE)" >> $(1)
+ $(call add-common-flags-to-image-props,cache,$(1))
)
$(if $(filter $(2),vendor),\
- $(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_fs_type=$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "vendor_f2fs_compress=$(BOARD_VENDORIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "vendor_f2fs_sldc_flags=$(BOARD_VENDORIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_EXTFS_INODE_COUNT),$(hide) echo "vendor_extfs_inode_count=$(BOARD_VENDORIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_EXTFS_RSV_PCT),$(hide) echo "vendor_extfs_rsv_pct=$(BOARD_VENDORIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_PARTITION_SIZE),$(hide) echo "vendor_size=$(BOARD_VENDORIMAGE_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_JOURNAL_SIZE),$(hide) echo "vendor_journal_size=$(BOARD_VENDORIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "vendor_squashfs_compressor=$(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "vendor_squashfs_compressor_opt=$(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "vendor_squashfs_block_size=$(BOARD_VENDORIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "vendor_squashfs_disable_4k_align=$(BOARD_VENDORIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(PRODUCT_VENDOR_BASE_FS_PATH),$(hide) echo "vendor_base_fs_file=$(PRODUCT_VENDOR_BASE_FS_PATH)" >> $(1))
- $(if $(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "vendor_reserved_size=$(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "vendor_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_vendor_image=$(BUILDING_VENDOR_IMAGE)" >> $(1)
+ $(call add-common-ro-flags-to-image-props,vendor,$(1))
)
$(if $(filter $(2),product),\
- $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "product_fs_type=$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "product_f2fs_compress=$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "product_f2fs_sldc_flags=$(BOARD_PRODUCTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_EXTFS_INODE_COUNT),$(hide) echo "product_extfs_inode_count=$(BOARD_PRODUCTIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_EXTFS_RSV_PCT),$(hide) echo "product_extfs_rsv_pct=$(BOARD_PRODUCTIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_PARTITION_SIZE),$(hide) echo "product_size=$(BOARD_PRODUCTIMAGE_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_JOURNAL_SIZE),$(hide) echo "product_journal_size=$(BOARD_PRODUCTIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "product_squashfs_compressor=$(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "product_squashfs_compressor_opt=$(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "product_squashfs_block_size=$(BOARD_PRODUCTIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "product_squashfs_disable_4k_align=$(BOARD_PRODUCTIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(PRODUCT_PRODUCT_BASE_FS_PATH),$(hide) echo "product_base_fs_file=$(PRODUCT_PRODUCT_BASE_FS_PATH)" >> $(1))
- $(if $(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "product_reserved_size=$(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "product_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_product_image=$(BUILDING_PRODUCT_IMAGE)" >> $(1)
+ $(call add-common-ro-flags-to-image-props,product,$(1))
)
$(if $(filter $(2),system_ext),\
- $(if $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_ext_fs_type=$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "system_ext_f2fs_compress=$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "system_ext_f2fs_sldc_flags=$(BOARD_SYSTEM_EXTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_EXTFS_INODE_COUNT),$(hide) echo "system_ext_extfs_inode_count=$(BOARD_SYSTEM_EXTIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_EXTFS_RSV_PCT),$(hide) echo "system_ext_extfs_rsv_pct=$(BOARD_SYSTEM_EXTIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE),$(hide) echo "system_ext_size=$(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_JOURNAL_SIZE),$(hide) echo "system_ext_journal_size=$(BOARD_SYSTEM_EXTIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_ext_squashfs_compressor=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_ext_squashfs_compressor_opt=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "system_ext_squashfs_block_size=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "system_ext_squashfs_disable_4k_align=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "system_ext_reserved_size=$(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "system_ext_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_system_ext_image=$(BUILDING_SYSTEM_EXT_IMAGE)" >> $(1)
+ $(call add-common-ro-flags-to-image-props,system_ext,$(1))
)
$(if $(filter $(2),odm),\
- $(if $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "odm_fs_type=$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_ODMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "odm_extfs_inode_count=$(BOARD_ODMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_ODMIMAGE_EXTFS_RSV_PCT),$(hide) echo "odm_extfs_rsv_pct=$(BOARD_ODMIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_ODMIMAGE_PARTITION_SIZE),$(hide) echo "odm_size=$(BOARD_ODMIMAGE_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_ODMIMAGE_JOURNAL_SIZE),$(hide) echo "odm_journal_size=$(BOARD_ODMIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "odm_squashfs_compressor=$(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "odm_squashfs_compressor_opt=$(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_ODMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "odm_squashfs_block_size=$(BOARD_ODMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_ODMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "odm_squashfs_disable_4k_align=$(BOARD_ODMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(PRODUCT_ODM_BASE_FS_PATH),$(hide) echo "odm_base_fs_file=$(PRODUCT_ODM_BASE_FS_PATH)" >> $(1))
- $(if $(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "odm_reserved_size=$(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "odm_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_odm_image=$(BUILDING_ODM_IMAGE)" >> $(1)
+ $(call add-common-ro-flags-to-image-props,odm,$(1))
)
$(if $(filter $(2),vendor_dlkm),\
- $(if $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_dlkm_fs_type=$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "vendor_dlkm_f2fs_compress=$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "vendor_dlkm_f2fs_sldc_flags=$(BOARD_VENDOR_DLKMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "vendor_dlkm_extfs_inode_count=$(BOARD_VENDOR_DLKMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_EXTFS_RSV_PCT),$(hide) echo "vendor_dlkm_extfs_rsv_pct=$(BOARD_VENDOR_DLKMIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE),$(hide) echo "vendor_dlkm_size=$(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_JOURNAL_SIZE),$(hide) echo "vendor_dlkm_journal_size=$(BOARD_VENDOR_DLKMIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "vendor_dlkm_squashfs_compressor=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "vendor_dlkm_squashfs_compressor_opt=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "vendor_dlkm_squashfs_block_size=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "vendor_dlkm_squashfs_disable_4k_align=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "vendor_dlkm_reserved_size=$(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "vendor_dlkm_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_vendor_dlkm_image=$(BUILDING_VENDOR_DLKM_IMAGE)" >> $(1)
+ $(call add-common-ro-flags-to-image-props,vendor_dlkm,$(1))
)
$(if $(filter $(2),odm_dlkm),\
- $(if $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "odm_dlkm_fs_type=$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "odm_dlkm_extfs_inode_count=$(BOARD_ODM_DLKMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_EXTFS_RSV_PCT),$(hide) echo "odm_dlkm_extfs_rsv_pct=$(BOARD_ODM_DLKMIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE),$(hide) echo "odm_dlkm_size=$(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_JOURNAL_SIZE),$(hide) echo "odm_dlkm_journal_size=$(BOARD_ODM_DLKMIMAGE_JOURNAL_SIZE)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "odm_dlkm_squashfs_compressor=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "odm_dlkm_squashfs_compressor_opt=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "odm_dlkm_squashfs_block_size=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "odm_dlkm_squashfs_disable_4k_align=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
- $(if $(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "odm_dlkm_reserved_size=$(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
- $(hide) echo "odm_dlkm_selinux_fc=$(SELINUX_FC)" >> $(1)
- $(hide) echo "building_odm_dlkm_image=$(BUILDING_ODM_DLKM_IMAGE)" >> $(1)
+ $(call add-common-ro-flags-to-image-props,odm_dlkm,$(1))
)
$(if $(filter $(2),oem),\
$(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(BOARD_OEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "oem_extfs_inode_count=$(BOARD_OEMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
$(if $(BOARD_OEMIMAGE_EXTFS_RSV_PCT),$(hide) echo "oem_extfs_rsv_pct=$(BOARD_OEMIMAGE_EXTFS_RSV_PCT)" >> $(1))
- $(hide) echo "oem_selinux_fc=$(SELINUX_FC)" >> $(1)
+ $(call add-common-flags-to-image-props,oem,$(1))
)
$(hide) echo "ext_mkuserimg=$(notdir $(MKEXTUSERIMG))" >> $(1)
@@ -1740,6 +1742,9 @@
$(if $(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG),$(hide) echo "erofs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG)" >> $(1))
$(if $(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG),$(hide) echo "squashfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG)" >> $(1))
$(if $(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG),$(hide) echo "f2fs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG)" >> $(1))
+$(if $(BOARD_EROFS_COMPRESSOR),$(hide) echo "erofs_default_compressor=$(BOARD_EROFS_COMPRESSOR)" >> $(1))
+$(if $(BOARD_EROFS_PCLUSTER_SIZE),$(hide) echo "erofs_pcluster_size=$(BOARD_EROFS_PCLUSTER_SIZE)" >> $(1))
+$(if $(BOARD_EROFS_SHARE_DUP_BLOCKS),$(hide) echo "erofs_share_dup_blocks=$(BOARD_EROFS_SHARE_DUP_BLOCKS)" >> $(1))
$(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo "ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)" >> $(1))
$(if $(BOARD_FLASH_LOGICAL_BLOCK_SIZE), $(hide) echo "flash_logical_block_size=$(BOARD_FLASH_LOGICAL_BLOCK_SIZE)" >> $(1))
$(if $(BOARD_FLASH_ERASE_BLOCK_SIZE), $(hide) echo "flash_erase_block_size=$(BOARD_FLASH_ERASE_BLOCK_SIZE)" >> $(1))
@@ -1905,14 +1910,18 @@
recovery_sepolicy := \
$(TARGET_RECOVERY_ROOT_OUT)/sepolicy \
$(TARGET_RECOVERY_ROOT_OUT)/plat_file_contexts \
+ $(TARGET_RECOVERY_ROOT_OUT)/plat_service_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/plat_property_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/system_ext_file_contexts \
+ $(TARGET_RECOVERY_ROOT_OUT)/system_ext_service_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/system_ext_property_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/vendor_file_contexts \
+ $(TARGET_RECOVERY_ROOT_OUT)/vendor_service_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/vendor_property_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/odm_file_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/odm_property_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/product_file_contexts \
+ $(TARGET_RECOVERY_ROOT_OUT)/product_service_contexts \
$(TARGET_RECOVERY_ROOT_OUT)/product_property_contexts
# Passed into rsync from non-recovery root to recovery root, to avoid overwriting recovery-specific
@@ -2327,79 +2336,75 @@
$(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.)
endif
-ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-# -----------------------------------------------------------------
-# the debug ramdisk, which is the original ramdisk plus additional
-# files: force_debuggable, adb_debug.prop and userdebug sepolicy.
-# When /force_debuggable is present, /init will load userdebug sepolicy
-# and property files to allow adb root, if the device is unlocked.
-ifdef BUILDING_RAMDISK_IMAGE
-BUILT_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img
-INSTALLED_DEBUG_RAMDISK_TARGET := $(BUILT_DEBUG_RAMDISK_TARGET)
+# -----------------------------------------------------------------
+# Build debug ramdisk and debug boot image.
+ifneq ($(BUILDING_DEBUG_BOOT_IMAGE)$(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),)
INTERNAL_DEBUG_RAMDISK_FILES := $(filter $(TARGET_DEBUG_RAMDISK_OUT)/%, \
$(ALL_GENERATED_SOURCES) \
$(ALL_DEFAULT_INSTALLED_MODULES))
-INSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt
-INSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json)
-$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK)
-
+# Directories to be picked into the debug ramdisk.
+# As these directories are all merged into one single cpio archive, the order
+# matters. If there are multiple files with the same pathname, then the last one
+# wins.
+#
# ramdisk-debug.img will merge the content from either ramdisk.img or
# ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT
# is set or not.
ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
- $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(recovery_ramdisk)
-else
- $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
- $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INSTALLED_RAMDISK_TARGET)
-endif # BOARD_USES_RECOVERY_AS_BOOT
+ INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RECOVERY_ROOT_OUT)
+ INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(recovery_ramdisk)
+else # BOARD_USES_RECOVERY_AS_BOOT == true
+ INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RAMDISK_OUT)
+ INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(INSTALLED_RAMDISK_TARGET)
+endif # BOARD_USES_RECOVERY_AS_BOOT != true
-$(INSTALLED_FILES_FILE_DEBUG_RAMDISK) : $(INTERNAL_DEBUG_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)
- @echo Installed file list: $@
- mkdir -p $(dir $@)
- rm -f $@
- $(FILESLIST) $(TARGET_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) > $(@:.txt=.json)
+INTERNAL_DEBUG_RAMDISK_SRC_DIRS += $(TARGET_DEBUG_RAMDISK_OUT)
+INTERNAL_DEBUG_RAMDISK_SRC_DEPS := $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET) $(INTERNAL_DEBUG_RAMDISK_FILES)
+
+# INSTALLED_FILES_FILE_DEBUG_RAMDISK would ensure TARGET_DEBUG_RAMDISK_OUT is created.
+INSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt
+INSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json)
+$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK)
+$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INTERNAL_DEBUG_RAMDISK_SRC_DEPS)
+$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL)
+ @echo "Installed file list: $@"
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@) $(TARGET_DEBUG_RAMDISK_OUT)
+ touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable
+ $(FILESLIST) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) > $(@:.txt=.json)
$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- $(INSTALLED_DEBUG_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
- $(INSTALLED_DEBUG_RAMDISK_TARGET): $(recovery_ramdisk)
-else
- $(INSTALLED_DEBUG_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
- $(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_RAMDISK_TARGET)
-endif # BOARD_USES_RECOVERY_AS_BOOT
+ifdef BUILDING_DEBUG_BOOT_IMAGE
+
+# -----------------------------------------------------------------
+# the debug ramdisk, which is the original ramdisk plus additional
+# files: force_debuggable, adb_debug.prop and userdebug sepolicy.
+# When /force_debuggable is present, /init will load userdebug sepolicy
+# and property files to allow adb root, if the device is unlocked.
+INSTALLED_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img
$(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_DEBUG_RAMDISK)
-$(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_DEBUG_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
- $(call pretty,"Target debug ramdisk: $@")
- mkdir -p $(TARGET_DEBUG_RAMDISK_OUT)
- touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable
- $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+$(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
+ @echo "Target debug ramdisk: $@"
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@)
+ $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
.PHONY: ramdisk_debug-nodeps
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- ramdisk_debug-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-else
- ramdisk_debug-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-endif # BOARD_USES_RECOVERY_AS_BOOT
ramdisk_debug-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
- echo "make $@: ignoring dependencies"
- mkdir -p $(TARGET_DEBUG_RAMDISK_OUT)
- touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable
- $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET)
-
-endif # BUILDING_RAMDISK_IMAGE
+ @echo "make $@: ignoring dependencies"
+ $(hide) rm -f $(INSTALLED_DEBUG_RAMDISK_TARGET)
+ $(hide) mkdir -p $(dir $(INSTALLED_DEBUG_RAMDISK_TARGET)) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS)
+ $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET)
# -----------------------------------------------------------------
# the boot-debug.img, which is the kernel plus ramdisk-debug.img
#
# Note: it's intentional to skip signing for boot-debug.img, because it
# can only be used if the device is unlocked with verification error.
-ifneq ($(INSTALLED_BOOTIMAGE_TARGET),)
-ifneq ($(strip $(TARGET_NO_KERNEL)),true)
ifneq ($(strip $(BOARD_KERNEL_BINARIES)),)
INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-debug,$(BOARD_KERNEL_BINARIES)), \
$(PRODUCT_OUT)/$(k).img)
@@ -2408,10 +2413,11 @@
endif
# Replace ramdisk.img in $(MKBOOTIMG) ARGS with ramdisk-debug.img to build boot-debug.img
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET)
ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(recovery_ramdisk),$(INSTALLED_DEBUG_RAMDISK_TARGET), $(INTERNAL_RECOVERYIMAGE_ARGS))
+ INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_RECOVERYIMAGE_ARGS))
else
- INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INSTALLED_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET), $(INTERNAL_BOOTIMAGE_ARGS))
+ INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_BOOTIMAGE_ARGS))
endif
# If boot.img is chained but boot-debug.img is not signed, libavb in bootloader
@@ -2445,7 +2451,7 @@
endef
# Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img
-$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
$(call pretty,"Target boot debug image: $@")
$(call build-debug-bootimage-target, $@)
@@ -2454,55 +2460,57 @@
echo "make $@: ignoring dependencies"
$(foreach b,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call build-debug-bootimage-target,$b))
-endif # TARGET_NO_KERNEL
-endif # INSTALLED_BOOTIMAGE_TARGET
+endif # BUILDING_DEBUG_BOOT_IMAGE
-ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
-ifeq ($(BUILDING_RAMDISK_IMAGE),true)
# -----------------------------------------------------------------
# vendor debug ramdisk
# Combines vendor ramdisk files and debug ramdisk files to build the vendor debug ramdisk.
-#
+ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
INTERNAL_VENDOR_DEBUG_RAMDISK_FILES := $(filter $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, \
$(ALL_GENERATED_SOURCES) \
$(ALL_DEFAULT_INSTALLED_MODULES))
-INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt
-INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json)
-$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)
-$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
-$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)
- @echo Installed file list: $@
- mkdir -p $(dir $@)
- rm -f $@
- mkdir -p $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) # The dir might not be created if no modules are installed here.
- $(FILESLIST) $(TARGET_VENDOR_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) > $(@:.txt=.json)
- $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
-
-INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT)
+# The debug vendor ramdisk combines vendor ramdisk and debug ramdisk.
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS := $(TARGET_VENDOR_RAMDISK_OUT)
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS := $(INTERNAL_VENDOR_RAMDISK_TARGET)
# Exclude recovery files in the default vendor ramdisk if including a standalone
# recovery ramdisk in vendor_boot.
ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))
-ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-endif
-endif
+ ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))
+ INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_RECOVERY_ROOT_OUT)
+ INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
+ endif # BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT != true
+endif # BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT == true
-# The vendor debug ramdisk combines vendor ramdisk and debug ramdisk.
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT)
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK)
+
+# INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK would ensure TARGET_VENDOR_DEBUG_RAMDISK_OUT is created.
+INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt
+INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json)
+$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)
+$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS)
+$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL)
+ @echo "Installed file list: $@"
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)
+ $(FILESLIST) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) > $(@:.txt=.json)
+ $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
+
+INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT)
+
$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK)
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
- mkdir -p $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)
- $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@)
+ $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
-ifeq (true,$(BOARD_BUILD_VENDOR_RAMDISK_IMAGE))
INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-debug.img
$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET)
- $(call pretty,"Target vendor debug ramdisk: $@")
+ @echo "Target debug vendor ramdisk: $@"
$(copy-file-to-target)
-endif
# -----------------------------------------------------------------
# vendor_boot-debug.img.
@@ -2534,76 +2542,64 @@
$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))
$(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@))
-endif # BUILDING_RAMDISK_IMAGE
-endif # BUILDING_VENDOR_BOOT_IMAGE
+endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
+# Appends a few test harness specific properties into the adb_debug.prop.
+ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1
+ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1
+
+INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET := $(strip $(filter $(TARGET_DEBUG_RAMDISK_OUT)/adb_debug.prop,$(INTERNAL_DEBUG_RAMDISK_FILES)))
+INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop
+$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET): $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET)
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@)
+ifdef INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET
+ $(hide) cp $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) $@
+endif
+ $(hide) echo "" >> $@
+ $(hide) echo "#" >> $@
+ $(hide) echo "# ADDITIONAL TEST HARNESS PROPERTIES" >> $@
+ $(hide) echo "#" >> $@
+ $(hide) $(foreach line,$(ADDITIONAL_TEST_HARNESS_PROPERTIES), \
+ echo "$(line)" >> $@;)
+
+INTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \
+ $(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET) \
+ $(ALL_GENERATED_SOURCES) \
+ $(ALL_DEFAULT_INSTALLED_MODULES))
+
+# The order is important here. The test harness ramdisk staging directory has to
+# come last so that it can override the adb_debug.prop in the debug ramdisk
+# staging directory.
+INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT)
+INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES)
+
+ifdef BUILDING_DEBUG_BOOT_IMAGE
# -----------------------------------------------------------------
# The test harness ramdisk, which is based off debug_ramdisk, plus a
# few additional test-harness-specific properties in adb_debug.prop.
+INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img
-ifdef BUILDING_RAMDISK_IMAGE
-BUILT_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img
-INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(BUILT_TEST_HARNESS_RAMDISK_TARGET)
-
-# Appends a few test harness specific properties into the adb_debug.prop.
-TEST_HARNESS_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop
-ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1
-ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1
-
-# $(1): a list of key=value pairs for additional property assignments
-# $(2): the target .prop file to append the properties from $(1)
-define append-test-harness-props
- echo "#" >> $(2); \
- echo "# ADDITIONAL TEST HARNESS_PROPERTIES" >> $(2); \
- echo "#" >> $(2);
- $(foreach line,$(1), echo "$(line)" >> $(2);)
-endef
-
-INTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \
- $(ALL_GENERATED_SOURCES) \
- $(ALL_DEFAULT_INSTALLED_MODULES))
-
-# ramdisk-test-harness.img will merge the content from either ramdisk.img or
-# ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT is set
-# or not.
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
- $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(recovery_ramdisk)
-else
- $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
- $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INSTALLED_RAMDISK_TARGET)
-endif # BOARD_USES_RECOVERY_AS_BOOT
-
-# The test harness ramdisk will rsync the files from the debug ramdisk, then appends some props.
-$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET)
-$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
- $(call pretty,"Target test harness ramdisk: $@")
- rsync --chmod=u+w -a $(TARGET_DEBUG_RAMDISK_OUT)/ $(TARGET_TEST_HARNESS_RAMDISK_OUT)
- $(call append-test-harness-props,$(ADDITIONAL_TEST_HARNESS_PROPERTIES),$(TEST_HARNESS_PROP_TARGET))
- $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_TEST_HARNESS_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS)
+$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
+ @echo "Target test harness ramdisk: $@"
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@)
+ $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
.PHONY: ramdisk_test_harness-nodeps
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- ramdisk_test_harness-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-else
- ramdisk_test_harness-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-endif # BOARD_USES_RECOVERY_AS_BOOT
ramdisk_test_harness-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
- echo "make $@: ignoring dependencies"
- rsync --chmod=u+w -a $(TARGET_DEBUG_RAMDISK_OUT)/ $(TARGET_TEST_HARNESS_RAMDISK_OUT)
- $(call append-test-harness-props,$(ADDITIONAL_TEST_HARNESS_PROPERTIES),$(TEST_HARNESS_PROP_TARGET))
- $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_TEST_HARNESS_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
-
-endif # BUILDING_RAMDISK_IMAGE
+ @echo "make $@: ignoring dependencies"
+ $(hide) rm -f $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+ $(hide) mkdir -p $(dir $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS)
+ $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
# -----------------------------------------------------------------
# the boot-test-harness.img, which is the kernel plus ramdisk-test-harness.img
#
# Note: it's intentional to skip signing for boot-test-harness.img, because it
# can only be used if the device is unlocked with verification error.
-ifneq ($(INSTALLED_BOOTIMAGE_TARGET),)
-ifneq ($(strip $(TARGET_NO_KERNEL)),true)
-
ifneq ($(strip $(BOARD_KERNEL_BINARIES)),)
INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-test-harness,$(BOARD_KERNEL_BINARIES)), \
$(PRODUCT_OUT)/$(k).img)
@@ -2612,6 +2608,7 @@
endif
# Replace ramdisk-debug.img in $(MKBOOTIMG) ARGS with ramdisk-test-harness.img to build boot-test-harness.img
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS := $(subst $(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_DEBUG_BOOTIMAGE_ARGS))
# If boot.img is chained but boot-test-harness.img is not signed, libavb in bootloader
@@ -2631,8 +2628,7 @@
endef
# Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img.
-$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
-$(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
$(call pretty,"Target boot test harness image: $@")
$(call build-boot-test-harness-target,$@)
@@ -2641,31 +2637,31 @@
echo "make $@: ignoring dependencies"
$(foreach b,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(call build-boot-test-harness-target,$b))
-endif # TARGET_NO_KERNEL
-endif # INSTALLED_BOOTIMAGE_TARGET
-endif # BOARD_BUILD_SYSTEM_ROOT_IMAGE is not true
+endif # BUILDING_DEBUG_BOOT_IMAGE
-ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
-ifeq ($(BUILDING_RAMDISK_IMAGE),true)
# -----------------------------------------------------------------
# vendor test harness ramdisk, which is a vendor ramdisk combined with
# a test harness ramdisk.
+ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE
INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-test-harness)/vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT)
-# Exclude recovery files in the default vendor ramdisk if including a standalone
-# recovery ramdisk in vendor_boot.
-ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))
-ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))
-$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
-$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-endif
-endif
+# The order is important here. The test harness ramdisk staging directory has to
+# come last so that it can override the adb_debug.prop in the debug ramdisk
+# staging directory.
+INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT)
+INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES)
-# The vendor test harness ramdisk combines vendor ramdisk and test harness ramdisk.
-$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS)
$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
- $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(TARGET_TEST_HARNESS_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@)
+ $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
+
+INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-test-harness.img
+$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET)
+ @echo "Target test harness vendor ramdisk: $@"
+ $(copy-file-to-target)
# -----------------------------------------------------------------
# vendor_boot-test-harness.img.
@@ -2684,8 +2680,10 @@
$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))
$(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@))
-endif # BUILDING_RAMDISK_IMAGE
-endif # BUILDING_VENDOR_BOOT_IMAGE
+endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
+endif # BUILDING_DEBUG_BOOT_IMAGE || BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
# Creates a compatibility symlink between two partitions, e.g. /system/vendor to /vendor
# $1: from location (e.g $(TARGET_OUT)/vendor)
@@ -2772,7 +2770,9 @@
.PHONY: installed-file-list
installed-file-list: $(INSTALLED_FILES_FILE)
-$(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE))
+ifeq ($(HOST_OS),linux)
+$(call dist-for-goals, sdk sdk_addon, $(INSTALLED_FILES_FILE))
+endif
systemimage_intermediates := \
$(call intermediates-dir-for,PACKAGING,systemimage)
@@ -2795,9 +2795,16 @@
ifeq ($(BOARD_AVB_ENABLE),true)
$(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)
endif
+ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
+$(BUILT_SYSTEMIMAGE): $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 $(HOST_OUT_EXECUTABLES)/apksigner \
+ $(FSVERITY_APK_MANIFEST_PATH) $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8
+endif
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
$(call build-systemimage-target,$@)
+$(call declare-1p-container,$(BUILT_SYSTEMIMAGE),system/extras)
+$(call declare-container-license-deps,$(BUILT_SYSTEMIMAGE),$(FULL_SYSTEMIMAGE_DEPS),$(PRODUCT_OUT)/:)
+
INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img
SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
@@ -2839,8 +2846,14 @@
$(copy-file-to-target)
$(hide) $(call assert-max-image-size,$@,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
+$(call declare-1p-container,$(INSTALLED_SYSTEMIMAGE_TARGET),)
+$(call declare-container-license-deps,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BUILT_SYSTEMIMAGE),$(BUILT_SYSTEMIMAGE):/)
+
systemimage: $(INSTALLED_SYSTEMIMAGE_TARGET)
+.PHONY: systemlicense
+systemlicense: $(call license-metadata-dir)/$(INSTALLED_SYSTEMIMAGE_TARGET).meta_lic reportmissinglicenses
+
.PHONY: systemimage-nodeps snod
systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \
| $(INTERNAL_USERIMAGES_DEPS)
@@ -3278,7 +3291,8 @@
$(call pretty,"Target odm fs image: $(INSTALLED_ODMIMAGE_TARGET)")
@mkdir -p $(TARGET_OUT_ODM)
@mkdir -p $(odmimage_intermediates) && rm -rf $(odmimage_intermediates)/odm_image_info.txt
- $(call generate-userimage-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, skip_fsck=true)
+ $(call generate-image-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, odm, \
+ skip_fsck=true)
PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
$(BUILD_IMAGE) \
$(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt \
@@ -3329,7 +3343,8 @@
$(call pretty,"Target vendor_dlkm fs image: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)")
@mkdir -p $(TARGET_OUT_VENDOR_DLKM)
@mkdir -p $(vendor_dlkmimage_intermediates) && rm -rf $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt
- $(call generate-userimage-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, skip_fsck=true)
+ $(call generate-image-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, \
+ vendor_dlkm, skip_fsck=true)
PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
$(BUILD_IMAGE) \
$(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt \
@@ -3380,7 +3395,8 @@
$(call pretty,"Target odm_dlkm fs image: $(INSTALLED_ODM_DLKMIMAGE_TARGET)")
@mkdir -p $(TARGET_OUT_ODM_DLKM)
@mkdir -p $(odm_dlkmimage_intermediates) && rm -rf $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt
- $(call generate-userimage-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, skip_fsck=true)
+ $(call generate-image-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, \
+ odm_dlkm, skip_fsck=true)
PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
$(BUILD_IMAGE) \
$(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt \
@@ -3430,23 +3446,29 @@
# -----------------------------------------------------------------
# Protected VM firmware image
-ifdef BOARD_PREBUILT_PVMFWIMAGE
+ifeq ($(BOARD_USES_PVMFWIMAGE),true)
INSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img
+INTERNAL_PREBUILT_PVMFWIMAGE := packages/modules/Virtualization/pvmfw/pvmfw.img
+
+ifdef BOARD_PREBUILT_PVMFWIMAGE
+BUILT_PVMFWIMAGE_TARGET := $(BOARD_PREBUILT_PVMFWIMAGE)
+else ifeq ($(BUILDING_PVMFW_IMAGE),true)
+BUILT_PVMFWIMAGE_TARGET := $(INTERNAL_PREBUILT_PVMFWIMAGE)
+endif
ifeq ($(BOARD_AVB_ENABLE),true)
-$(INSTALLED_PVMFWIMAGE_TARGET): $(BOARD_PREBUILT_PVMFWIMAGE) $(AVBTOOL) $(BOARD_AVB_PVMFW_KEY_PATH)
- cp $(BOARD_PREBUILT_PVMFWIMAGE) $@
+$(INSTALLED_PVMFWIMAGE_TARGET): $(BUILT_PVMFWIMAGE_TARGET) $(AVBTOOL) $(BOARD_AVB_PVMFW_KEY_PATH)
+ cp $(BUILT_PVMFWIMAGE_TARGET) $@
$(AVBTOOL) add_hash_footer \
--image $@ \
- --partition_size $(BOARD_PVMFWIMG_PARTITION_SIZE) \
+ --partition_size $(BOARD_PVMFWIMAGE_PARTITION_SIZE) \
--partition_name pvmfw $(INTERNAL_AVB_PVMFW_SIGNING_ARGS) \
$(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)
else
-$(INSTALLED_PVMFWIMAGE_TARGET): $(BOARD_PREBUILT_PVMFWIMAGE)
- cp $(BOARD_PREBUILT_PVMFWIMAGE) $@
+$(eval $(call copy-one-file,$(BUILT_PVMFWIMAGE_TARGET),$(INSTALLED_PVMFWIMAGE_TARGET)))
endif
-endif # BOARD_PREBUILT_PVMFWIMAGE
+endif # BOARD_USES_PVMFWIMAGE
# Returns a list of image targets corresponding to the given list of partitions. For example, it
# returns "$(INSTALLED_PRODUCTIMAGE_TARGET)" for "product", or "$(INSTALLED_SYSTEMIMAGE_TARGET)
@@ -4305,7 +4327,6 @@
INTERNAL_OTATOOLS_MODULES := \
aapt2 \
add_img_to_target_files \
- aftltool \
apksigner \
append2simg \
avbtool \
@@ -4362,6 +4383,7 @@
shflags \
sign_apex \
sign_target_files_apks \
+ sign_virt_apex \
signapk \
simg2img \
sload_f2fs \
@@ -4373,6 +4395,7 @@
verity_signer \
verity_verifier \
zipalign \
+ zucchini \
# Additional tools to unpack and repack the apex file.
INTERNAL_OTATOOLS_MODULES += \
@@ -4621,10 +4644,10 @@
endif # BOARD_AVB_DTBO_KEY_PATH
endif # BOARD_AVB_ENABLE
endif # BOARD_PREBUILT_DTBOIMAGE
-ifdef BOARD_PREBUILT_PVMFWIMAGE
+ifeq ($(BOARD_USES_PVMFWIMAGE),true)
$(hide) echo "has_pvmfw=true" >> $@
ifeq ($(BOARD_AVB_ENABLE),true)
- $(hide) echo "pvmfw_size=$(BOARD_PVMFWIMG_PARTITION_SIZE)" >> $@
+ $(hide) echo "pvmfw_size=$(BOARD_PVMFWIMAGE_PARTITION_SIZE)" >> $@
$(hide) echo "avb_pvmfw_add_hash_footer_args=$(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)" >> $@
ifdef BOARD_AVB_PVMFW_KEY_PATH
$(hide) echo "avb_pvmfw_key_path=$(BOARD_AVB_PVMFW_KEY_PATH)" >> $@
@@ -4632,7 +4655,7 @@
$(hide) echo "avb_pvmfw_rollback_index_location=$(BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION)" >> $@
endif # BOARD_AVB_PVMFW_KEY_PATH
endif # BOARD_AVB_ENABLE
-endif # BOARD_PREBUILT_PVMFWIMAGE
+endif # BOARD_USES_PVMFWIMAGE
$(call dump-dynamic-partitions-info,$@)
@# VINTF checks
ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true)
@@ -4659,6 +4682,12 @@
ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)
$(hide) echo "partial_ota_update_partitions_list=$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)" >> $@
endif
+ifeq ($(BUILDING_WITH_VSDK),true)
+ $(hide) echo "building_with_vsdk=true" >> $@
+endif
+ifeq ($(TARGET_FLATTEN_APEX),false)
+ $(hide) echo "target_flatten_apex=false" >> $@
+endif
.PHONY: misc_info
misc_info: $(INSTALLED_MISC_INFO_TARGET)
@@ -4707,6 +4736,7 @@
ifeq ($(AB_OTA_UPDATER),true)
updater_dep := system/update_engine/update_engine.conf
+updater_dep := external/zucchini/version_info.h
endif
# Build OTA tools if non-A/B is allowed
@@ -4843,6 +4873,7 @@
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)
# 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)
@@ -5179,10 +5210,11 @@
ifeq ($(AB_OTA_UPDATER),true)
@# When using the A/B updater, include the updater config files in the zip.
$(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt
- $(hide) for part in $(AB_OTA_PARTITIONS); do \
+ $(hide) cp $(TOPDIR)external/zucchini/version_info.h $(zip_root)/META/zucchini_config.txt
+ $(hide) for part in $(strip $(AB_OTA_PARTITIONS)); do \
echo "$${part}" >> $(zip_root)/META/ab_partitions.txt; \
done
- $(hide) for conf in $(AB_OTA_POSTINSTALL_CONFIG); do \
+ $(hide) for conf in $(strip $(AB_OTA_POSTINSTALL_CONFIG)); do \
echo "$${conf}" >> $(zip_root)/META/postinstall_config.txt; \
done
ifdef OSRELEASED_DIRECTORY
@@ -5237,7 +5269,10 @@
ifdef BOARD_PREBUILT_PVMFWIMAGE
$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
$(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
-endif # BOARD_PREBUILT_PVMFWIMAGE
+else ifeq ($(BUILDING_PVMFW_IMAGE),true)
+ $(hide) mkdir -p $(zip_root)/IMAGES
+ $(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/IMAGES/
+endif
ifdef BOARD_PREBUILT_BOOTLOADER
$(hide) mkdir -p $(zip_root)/IMAGES
$(hide) cp $(INSTALLED_BOOTLOADER_MODULE) $(zip_root)/IMAGES/
@@ -5329,7 +5364,9 @@
@echo Package NDK sysroot...
$(hide) tar cjf $@ -C $(SOONG_OUT_DIR) ndk
+ifeq ($(HOST_OS),linux)
$(call dist-for-goals,sdk,$(NDK_SYSROOT_TARGET))
+endif
ifeq ($(build_ota_package),true)
# -----------------------------------------------------------------
@@ -5424,6 +5461,8 @@
ifeq ($(BUILD_OS),linux)
ifneq ($(DEX2OAT),)
dexpreopt_tools_deps := $(DEXPREOPT_GEN_DEPS) $(DEXPREOPT_GEN) $(AAPT2)
+dexpreopt_tools_deps += $(HOST_OUT_EXECUTABLES)/dexdump
+dexpreopt_tools_deps += $(HOST_OUT_EXECUTABLES)/oatdump
DEXPREOPT_TOOLS_ZIP := $(PRODUCT_OUT)/dexpreopt_tools.zip
$(DEXPREOPT_TOOLS_ZIP): $(dexpreopt_tools_deps)
$(DEXPREOPT_TOOLS_ZIP): PRIVATE_DEXPREOPT_TOOLS_DEPS := $(dexpreopt_tools_deps)
@@ -5434,25 +5473,34 @@
endif # BUILD_OS == linux
DEXPREOPT_CONFIG_ZIP := $(PRODUCT_OUT)/dexpreopt_config.zip
-$(DEXPREOPT_CONFIG_ZIP): $(FULL_SYSTEMIMAGE_DEPS) \
- $(INTERNAL_RAMDISK_FILES) \
- $(INTERNAL_USERDATAIMAGE_FILES) \
- $(INTERNAL_VENDORIMAGE_FILES) \
- $(INTERNAL_PRODUCTIMAGE_FILES) \
- $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \
- $(DEX_PREOPT_CONFIG_FOR_MAKE) \
- $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)
+
+$(DEXPREOPT_CONFIG_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \
+ $(INSTALLED_VENDORIMAGE_TARGET) \
+ $(INSTALLED_ODMIMAGE_TARGET) \
+ $(INSTALLED_PRODUCTIMAGE_TARGET) \
+
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
+$(DEXPREOPT_CONFIG_ZIP): $(DEX_PREOPT_CONFIG_FOR_MAKE) \
+ $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) \
+
+endif
$(DEXPREOPT_CONFIG_ZIP): $(SOONG_ZIP)
$(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/dexpreopt_config
+
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
ifneq (,$(DEX_PREOPT_CONFIG_FOR_MAKE))
$(hide) cp $(DEX_PREOPT_CONFIG_FOR_MAKE) $(PRODUCT_OUT)/dexpreopt_config
endif
ifneq (,$(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE))
$(hide) cp $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(PRODUCT_OUT)/dexpreopt_config
endif
+endif #!TARGET_BUILD_UNBUNDLED
$(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/dexpreopt_config -D $(PRODUCT_OUT)/dexpreopt_config
+.PHONY: dexpreopt_config_zip
+dexpreopt_config_zip: $(DEXPREOPT_CONFIG_ZIP)
+
# -----------------------------------------------------------------
# A zip of the symbols directory. Keep the full paths to make it
# more obvious where these files came from.
@@ -5877,6 +5925,8 @@
# -----------------------------------------------------------------
# The SDK
+ifneq ($(filter sdk,$(MAKECMDGOALS)),)
+
# The SDK includes host-specific components, so it belongs under HOST_OUT.
sdk_dir := $(HOST_OUT)/sdk/$(TARGET_PRODUCT)
@@ -5886,15 +5936,11 @@
# darwin-x86 --> android-sdk_12345_mac-x86
# windows-x86 --> android-sdk_12345_windows
#
+ifneq ($(HOST_OS),linux)
+ $(error Building the monolithic SDK is only supported on Linux)
+endif
sdk_name := android-sdk_$(FILE_NAME_TAG)
-ifeq ($(HOST_OS),darwin)
- INTERNAL_SDK_HOST_OS_NAME := mac
-else
- INTERNAL_SDK_HOST_OS_NAME := $(HOST_OS)
-endif
-ifneq ($(HOST_OS),windows)
- INTERNAL_SDK_HOST_OS_NAME := $(INTERNAL_SDK_HOST_OS_NAME)-$(SDK_HOST_ARCH)
-endif
+INTERNAL_SDK_HOST_OS_NAME := linux-$(SDK_HOST_ARCH)
sdk_name := $(sdk_name)_$(INTERNAL_SDK_HOST_OS_NAME)
sdk_dep_file := $(sdk_dir)/sdk_deps.mk
@@ -5914,9 +5960,7 @@
atree_dir := development/build
-sdk_atree_files := \
- $(atree_dir)/sdk.exclude.atree \
- $(atree_dir)/sdk-$(HOST_OS)-$(SDK_HOST_ARCH).atree
+sdk_atree_files := $(atree_dir)/sdk.exclude.atree
# development/build/sdk-android-<abi>.atree is used to differentiate
# between architecture models (e.g. ARMv5TE versus ARMv7) when copying
@@ -5970,7 +6014,7 @@
$(INTERNAL_SDK_TARGET): $(deps)
@echo "Package SDK: $@"
$(hide) rm -rf $(PRIVATE_DIR) $@
- $(hide) for f in $(target_gnu_MODULES); do \
+ $(hide) for f in $(strip $(target_gnu_MODULES)); do \
if [ -f $$f ]; then \
echo SDK: $(if $(SDK_GNU_ERROR),ERROR:,warning:) \
including GNU target $$f >&2; \
@@ -5998,22 +6042,16 @@
-o $(PRIVATE_DIR) && \
cp -f $(target_notice_file_txt) \
$(PRIVATE_DIR)/system-images/android-$(PLATFORM_VERSION)/$(TARGET_CPU_ABI)/NOTICE.txt && \
- cp -f $(tools_notice_file_txt) $(PRIVATE_DIR)/platform-tools/NOTICE.txt && \
HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \
development/build/tools/sdk_clean.sh $(PRIVATE_DIR) && \
chmod -R ug+rwX $(PRIVATE_DIR) && \
cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) \
) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 )
-
-# Is a Windows SDK requested? If so, we need some definitions from here
-# in order to find the Linux SDK used to create the Windows one.
-MAIN_SDK_NAME := $(sdk_name)
MAIN_SDK_DIR := $(sdk_dir)
MAIN_SDK_ZIP := $(INTERNAL_SDK_TARGET)
-ifneq ($(filter win_sdk winsdk-tools,$(MAKECMDGOALS)),)
-include $(TOPDIR)development/build/tools/windows_sdk.mk
-endif
+
+endif # sdk in MAKECMDGOALS
# -----------------------------------------------------------------
# Findbugs
diff --git a/core/OWNERS b/core/OWNERS
index 5456d4f..8794434 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -1,5 +1,5 @@
-per-file dex_preopt*.mk = ngeoffray@google.com,calin@google.com,mathewi@google.com,dbrazdil@google.com
-per-file verify_uses_libraries.sh = ngeoffray@google.com,calin@google.com,mathieuc@google.com
+per-file dex_preopt*.mk = ngeoffray@google.com,calin@google.com,mathewi@google.com,skvadrik@google.com
+per-file verify_uses_libraries.sh = ngeoffray@google.com,calin@google.com,skvadrik@google.com
# For version updates
per-file version_defaults.mk = aseaton@google.com,elisapascual@google.com,lubomir@google.com,pscovanner@google.com
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 39f0155..d24449b 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -28,8 +28,16 @@
$(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)
$(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)
+$(call add_soong_config_var,ANDROID,BOARD_USES_RECOVERY_AS_BOOT)
+$(call add_soong_config_var,ANDROID,BOARD_BUILD_SYSTEM_ROOT_IMAGE)
+$(call add_soong_config_var,ANDROID,PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)
-ifeq (,$(filter com.google.android.conscrypt,$(PRODUCT_PACKAGES)))
+ifneq (,$(filter sdk win_sdk sdk_addon,$(MAKECMDGOALS)))
+ # The artifacts in the SDK zip are OK to build with prebuilt stubs enabled,
+ # even if prebuilt apexes are not enabled, because the system images in the
+ # SDK stub are not currently used (and will be removed: b/205008975).
+ MODULE_BUILD_FROM_SOURCE ?= false
+else ifeq (,$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES)))
# Prebuilt module SDKs require prebuilt modules to work, and currently
# prebuilt modules are only provided for com.google.android.xxx. If we can't
# find one of them in PRODUCT_PACKAGES then assume com.android.xxx are in use,
@@ -42,14 +50,16 @@
$(call add_soong_config_namespace,art_module)
SOONG_CONFIG_art_module += source_build
endif
-ifneq (,$(findstring .android.art,$(TARGET_BUILD_APPS)))
+ifneq (,$(SOONG_CONFIG_art_module_source_build))
+ # Keep an explicit setting.
+else ifneq (,$(findstring .android.art,$(TARGET_BUILD_APPS)))
# Build ART modules from source if they are listed in TARGET_BUILD_APPS.
SOONG_CONFIG_art_module_source_build := true
else ifeq (,$(filter-out modules_% mainline_modules_%,$(TARGET_PRODUCT)))
# Always build from source for the module targets. This ought to be covered by
# the TARGET_BUILD_APPS check above, but there are test builds that don't set it.
SOONG_CONFIG_art_module_source_build := true
-else ifdef MODULE_BUILD_FROM_SOURCE
+else ifeq (true,$(MODULE_BUILD_FROM_SOURCE))
# Build from source if other Mainline modules are.
SOONG_CONFIG_art_module_source_build := true
else ifneq (,$(filter true,$(NATIVE_COVERAGE) $(CLANG_COVERAGE)))
@@ -61,10 +71,6 @@
# Prebuilts aren't built with sanitizers either.
SOONG_CONFIG_art_module_source_build := true
MODULE_BUILD_FROM_SOURCE := true
-else ifneq (,$(PRODUCT_FUCHSIA))
- # Fuchsia picks out ART internal packages that aren't available in the
- # prebuilt.
- SOONG_CONFIG_art_module_source_build := true
else ifeq (,$(filter x86 x86_64,$(HOST_CROSS_ARCH)))
# We currently only provide prebuilts for x86 on host. This skips prebuilts in
# cuttlefish builds for ARM servers.
@@ -72,7 +78,7 @@
else ifneq (,$(filter dex2oatds dex2oats,$(PRODUCT_HOST_PACKAGES)))
# Some products depend on host tools that aren't available as prebuilts.
SOONG_CONFIG_art_module_source_build := true
-else ifeq (,$(filter com.google.android.art,$(PRODUCT_PACKAGES)))
+else ifeq (,$(findstring com.google.android.art,$(PRODUCT_PACKAGES)))
# TODO(b/192006406): There is currently no good way to control which prebuilt
# APEX (com.google.android.art or com.android.art) gets picked for deapexing
# to provide dex jars for hiddenapi and dexpreopting. Instead the AOSP APEX is
@@ -82,7 +88,7 @@
# This sets the default for building ART APEXes from source rather than
# prebuilts (in packages/modules/ArtPrebuilt and prebuilt/module_sdk/art) in
# all other platform builds.
- SOONG_CONFIG_art_module_source_build ?= false
+ SOONG_CONFIG_art_module_source_build ?= true
endif
# Apex build mode variables
@@ -90,6 +96,10 @@
$(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static)
endif
-ifdef MODULE_BUILD_FROM_SOURCE
+ifeq (true,$(MODULE_BUILD_FROM_SOURCE))
$(call add_soong_config_var_value,ANDROID,module_build_from_source,true)
endif
+
+# TODO(b/196084106): Remove when Java optimizations enabled by default for
+# system packages.
+$(call add_soong_config_var,ANDROID,SYSTEM_OPTIMIZE_JAVA)
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 1b7a279..8a5440f 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -373,7 +373,13 @@
LOCAL_BUILT_MODULE := $(intermediates)/$(my_built_module_stem)
-ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))
+ ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
+ $(call pretty-error, LOCAL_SOONG_INSTALLED_MODULE can only be used from $(SOONG_ANDROID_MK))
+ endif
+ # Use the install path requested by Soong.
+ LOCAL_INSTALLED_MODULE := $(LOCAL_SOONG_INSTALLED_MODULE)
+else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
# Apk and its attachments reside in its own subdir.
ifeq ($(LOCAL_MODULE_CLASS),APPS)
# framework-res.apk doesn't like the additional layer.
@@ -507,91 +513,98 @@
## Module installation rule
###########################################################
-my_init_rc_installed :=
-my_init_rc_path :=
-my_init_rc_pairs :=
my_installed_symlinks :=
-my_default_test_module :=
-ifeq ($(use_testcase_folder),true)
-arch_dir := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
-my_default_test_module := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)/$(arch_dir)/$(my_installed_module_stem)
-arch_dir :=
-endif
-ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
-ifneq ($(LOCAL_INSTALLED_MODULE),$(my_default_test_module))
-$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
-$(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
+ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))
+ # Soong already generated the copy rule, but make the installed location depend on the Make
+ # copy of the intermediates for now, as some rules that collect intermediates may expect
+ # them to exist.
+ $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
+
+ $(foreach symlink, $(LOCAL_SOONG_INSTALL_SYMLINKS), \
+ $(call declare-0p-target,$(symlink)))
+ $(my_all_targets) : | $(LOCAL_SOONG_INSTALL_SYMLINKS)
+else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+ $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
+ $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
@echo "Install: $@"
-ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
+ ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
$(copy-file-or-link-to-new-target)
-else
+ else
$(copy-file-to-new-target)
-endif
+ endif
$(PRIVATE_POST_INSTALL_CMD)
-endif
-ifndef LOCAL_IS_HOST_MODULE
-# Rule to install the module's companion init.rc.
-ifneq ($(strip $(LOCAL_FULL_INIT_RC)),)
-my_init_rc := $(LOCAL_FULL_INIT_RC)
-else
-my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc))
-endif
-ifneq ($(strip $(my_init_rc)),)
-# Make doesn't support recovery as an output partition, but some Soong modules installed in recovery
-# have init.rc files that need to be installed alongside them. Manually handle the case where the
-# output file is in the recovery partition.
-my_init_rc_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))
-my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc)))
-my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))
+ # Rule to install the module's companion symlinks
+ my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))
+ $(foreach symlink,$(my_installed_symlinks),\
+ $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink))\
+ $(call declare-0p-target,$(symlink)))
-# Make sure we only set up the copy rules once, even if another arch variant
-# shares a common LOCAL_INIT_RC.
-my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
-my_init_rc_new_installed := $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs))
-ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
-
-$(my_all_targets) : $(my_init_rc_installed)
-endif # my_init_rc
-endif # !LOCAL_IS_HOST_MODULE
-
-# Rule to install the module's companion symlinks
-my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))
-$(foreach symlink,$(my_installed_symlinks),\
- $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink)))
-
-$(my_all_targets) : | $(my_installed_symlinks)
+ $(my_all_targets) : | $(my_installed_symlinks)
endif # !LOCAL_UNINSTALLABLE_MODULE
###########################################################
-## VINTF manifest fragment goals
+## VINTF manifest fragment and init.rc goals
###########################################################
my_vintf_installed:=
+my_vintf_path:=
my_vintf_pairs:=
+my_init_rc_installed :=
+my_init_rc_path :=
+my_init_rc_pairs :=
ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
-ifndef LOCAL_IS_HOST_MODULE
-ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),)
-my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS)
-else
-my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml))
-endif
-ifneq ($(strip $(my_vintf_fragments)),)
+ ifndef LOCAL_IS_HOST_MODULE
+ # Rule to install the module's companion vintf fragments.
+ ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),)
+ my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS)
+ else
+ my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml))
+ endif
+ ifneq ($(strip $(my_vintf_fragments)),)
+ # Make doesn't support recovery as an output partition, but some Soong modules installed in recovery
+ # have init.rc files that need to be installed alongside them. Manually handle the case where the
+ # output file is in the recovery partition.
+ my_vintf_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))
+ my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(my_vintf_path)/vintf/manifest/$(notdir $(xml)))
+ my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml)))
-my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(TARGET_OUT$(partition_tag)_ETC)/vintf/manifest/$(notdir $(xml)))
-my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml)))
+ # Only set up copy rules once, even if another arch variant shares it
+ my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs))
+ my_vintf_new_installed := $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs))
-# Only set up copy rules once, even if another arch variant shares it
-my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs))
-my_vintf_new_installed := $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs))
+ ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs)
-ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs)
+ $(my_all_targets) : $(my_vintf_new_installed)
+ endif # my_vintf_fragments
-$(my_all_targets) : $(my_vintf_new_installed)
-endif # LOCAL_VINTF_FRAGMENTS
-endif # !LOCAL_IS_HOST_MODULE
+ # Rule to install the module's companion init.rc.
+ ifneq ($(strip $(LOCAL_FULL_INIT_RC)),)
+ my_init_rc := $(LOCAL_FULL_INIT_RC)
+ else
+ my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc))
+ endif
+ ifneq ($(strip $(my_init_rc)),)
+ # Make doesn't support recovery as an output partition, but some Soong modules installed in recovery
+ # have init.rc files that need to be installed alongside them. Manually handle the case where the
+ # output file is in the recovery partition.
+ my_init_rc_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))
+ my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc)))
+ my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))
+
+ # Make sure we only set up the copy rules once, even if another arch variant
+ # shares a common LOCAL_INIT_RC.
+ my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
+ my_init_rc_new_installed := $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs))
+
+ ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
+
+ $(my_all_targets) : $(my_init_rc_installed)
+ endif # my_init_rc
+
+ endif # !LOCAL_IS_HOST_MODULE
endif # !LOCAL_UNINSTALLABLE_MODULE
###########################################################
@@ -718,8 +731,9 @@
# The module itself.
$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
- $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \
- $(LOCAL_BUILT_MODULE):$(dir)/$(my_installed_module_stem))) \
+ $(eval my_compat_dist_$(suite) := $(patsubst %:$(LOCAL_INSTALLED_MODULE),$(LOCAL_INSTALLED_MODULE):$(LOCAL_INSTALLED_MODULE),\
+ $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \
+ $(LOCAL_BUILT_MODULE):$(dir)/$(my_installed_module_stem)))) \
$(eval my_compat_dist_config_$(suite) := ))
@@ -902,12 +916,38 @@
ALL_MODULES.$(my_register_name).TARGET_BUILT := \
$(ALL_MODULES.$(my_register_name).TARGET_BUILT) $(LOCAL_BUILT_MODULE)
endif
-ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
-ALL_MODULES.$(my_register_name).INSTALLED := \
+ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))
+ # Store the list of paths to installed locations of files provided by this
+ # module. Used as dependencies of the image packaging rules when the module
+ # is installed by the current product.
+ ALL_MODULES.$(my_register_name).INSTALLED := \
+ $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \
+ $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\
+ $(word 2,$(subst :,$(space),$(f)))) \
+ $(LOCAL_SOONG_INSTALL_SYMLINKS) \
+ $(my_init_rc_installed) \
+ $(my_installed_test_data) \
+ $(my_vintf_installed))
+ # Store the list of colon-separated pairs of the built and installed locations
+ # of files provided by this module. Used by custom packaging rules like
+ # package-modules.mk that need to copy the built files to a custom install
+ # location during packaging.
+ #
+ # Translate copies from $(LOCAL_PREBUILT_MODULE_FILE) to $(LOCAL_BUILT_MODULE)
+ # so that package-modules.mk gets any transtive dependencies added to
+ # $(LOCAL_BUILT_MODULE), for example unstripped symbols files.
+ ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
+ $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \
+ $(patsubst $(LOCAL_PREBUILT_MODULE_FILE):%,$(LOCAL_BUILT_MODULE):%,$(LOCAL_SOONG_INSTALL_PAIRS)) \
+ $(my_init_rc_pairs) \
+ $(my_test_data_pairs) \
+ $(my_vintf_pairs))
+else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+ ALL_MODULES.$(my_register_name).INSTALLED := \
$(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \
$(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed) $(my_installed_symlinks) \
$(my_installed_test_data) $(my_vintf_installed))
-ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
+ ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
$(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \
$(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \
$(my_init_rc_pairs) $(my_test_data_pairs) $(my_vintf_pairs))
@@ -935,6 +975,12 @@
my_required_modules += $(LOCAL_REQUIRED_MODULES_$($(my_prefix)OS))
endif
+ALL_MODULES.$(my_register_name).SHARED_LIBS := \
+ $(ALL_MODULES.$(my_register_name).SHARED_LIBS) $(LOCAL_SHARED_LIBRARIES)
+
+ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS := \
+ $(ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS) $(LOCAL_SYSTEM_SHARED_LIBRARIES)
+
##########################################################################
## When compiling against the VNDK, add the .vendor or .product suffix to
## required modules.
@@ -1009,7 +1055,9 @@
endif
ALL_MODULES.$(my_register_name).FOR_HOST_CROSS := $(my_host_cross)
ALL_MODULES.$(my_register_name).MODULE_NAME := $(LOCAL_MODULE)
-ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := $(LOCAL_COMPATIBILITY_SUITE)
+ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := \
+ $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES) \
+ $(filter-out $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES),$(LOCAL_COMPATIBILITY_SUITE))
ALL_MODULES.$(my_register_name).TEST_CONFIG := $(test_config)
ALL_MODULES.$(my_register_name).EXTRA_TEST_CONFIGS := $(LOCAL_EXTRA_FULL_TEST_CONFIGS)
ALL_MODULES.$(my_register_name).TEST_MAINLINE_MODULES := $(LOCAL_TEST_MAINLINE_MODULES)
@@ -1026,7 +1074,7 @@
##########################################################
# Track module-level dependencies.
# Use $(LOCAL_MODULE) instead of $(my_register_name) to ignore module's bitness.
-ifdef RECORD_ALL_DEPS
+# (b/204397180) Unlock RECORD_ALL_DEPS was acknowledged reasonable for better Atest performance.
ALL_DEPS.MODULES += $(LOCAL_MODULE)
ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(sort \
$(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) \
@@ -1043,7 +1091,6 @@
license_files := $(call find-parent-file,$(LOCAL_PATH),MODULE_LICENSE*)
ALL_DEPS.$(LOCAL_MODULE).LICENSE := $(sort $(ALL_DEPS.$(LOCAL_MODULE).LICENSE) $(license_files))
-endif
###########################################################
## Take care of my_module_tags
diff --git a/core/board_config.mk b/core/board_config.mk
index 1b08f9a..6bbb3a0 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -84,6 +84,7 @@
_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE
_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_SIZE
_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE
+_board_strip_readonly_list += BOARD_PVMFWIMAGE_PARTITION_SIZE
# Logical partitions related variables.
_board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE
@@ -124,6 +125,43 @@
# Defines the list of logical vendor ramdisk names to build or include in vendor_boot.
_board_strip_readonly_list += BOARD_VENDOR_RAMDISK_FRAGMENTS
+# These are all variables used to build $(INSTALLED_MISC_INFO_TARGET)
+# in build/make/core/Makefile. Their values get used in command line
+# arguments, so they have to be stripped to make the ninja files stable.
+_board_strip_list :=
+_board_strip_list += BOARD_DTBOIMG_PARTITION_SIZE
+_board_strip_list += BOARD_AVB_DTBO_KEY_PATH
+_board_strip_list += BOARD_AVB_DTBO_ALGORITHM
+_board_strip_list += BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION
+_board_strip_list += BOARD_AVB_PVMFW_KEY_PATH
+_board_strip_list += BOARD_AVB_PVMFW_ALGORITHM
+_board_strip_list += BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION
+_board_strip_list += BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST
+_board_strip_list += BOARD_BPT_DISK_SIZE
+_board_strip_list += BOARD_BPT_INPUT_FILES
+_board_strip_list += BOARD_BPT_MAKE_TABLE_ARGS
+_board_strip_list += BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION
+_board_strip_list += BOARD_AVB_VBMETA_VENDOR_ALGORITHM
+_board_strip_list += BOARD_AVB_VBMETA_VENDOR_KEY_PATH
+_board_strip_list += BOARD_AVB_VBMETA_VENDOR
+_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION
+_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ALGORITHM
+_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_KEY_PATH
+_board_strip_list += BOARD_AVB_VBMETA_SYSTEM
+_board_strip_list += BOARD_AVB_RECOVERY_KEY_PATH
+_board_strip_list += BOARD_AVB_RECOVERY_ALGORITHM
+_board_strip_list += BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION
+_board_strip_list += BOARD_AVB_VENDOR_BOOT_KEY_PATH
+_board_strip_list += BOARD_AVB_VENDOR_BOOT_ALGORITHM
+_board_strip_list += BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION
+_board_strip_list += BOARD_GKI_SIGNING_SIGNATURE_ARGS
+_board_strip_list += BOARD_GKI_SIGNING_ALGORITHM
+_board_strip_list += BOARD_GKI_SIGNING_KEY_PATH
+_board_strip_list += BOARD_MKBOOTIMG_ARGS
+_board_strip_list += BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE
+_board_strip_list += ODM_MANIFEST_SKUS
+
+
_build_broken_var_list := \
BUILD_BROKEN_DUP_RULES \
BUILD_BROKEN_DUP_SYSPROP \
@@ -184,7 +222,32 @@
.KATI_READONLY := TARGET_DEVICE_DIR
endif
+# TODO(colefaust) change this if to RBC_PRODUCT_CONFIG when
+# the board configuration is known to work on everything
+# the product config works on.
+ifndef RBC_BOARD_CONFIG
include $(board_config_mk)
+else
+ $(shell mkdir -p $(OUT_DIR)/rbc)
+ $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_board_config.mk)
+
+ $(shell $(OUT_DIR)/soong/mk2rbc \
+ --mode=write -r --outdir $(OUT_DIR)/rbc \
+ --boardlauncher=$(OUT_DIR)/rbc/boardlauncher.rbc \
+ --input_variables=$(OUT_DIR)/rbc/make_vars_pre_board_config.mk \
+ $(board_config_mk))
+ ifneq ($(.SHELLSTATUS),0)
+ $(error board configuration converter failed: $(.SHELLSTATUS))
+ endif
+
+ $(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_board_config_results.mk \
+ $(OUT_DIR)/soong/rbcrun RBC_OUT="make,global" $(OUT_DIR)/rbc/boardlauncher.rbc)
+ ifneq ($(.SHELLSTATUS),0)
+ $(error board configuration runner failed: $(.SHELLSTATUS))
+ endif
+
+ include $(OUT_DIR)/rbc/rbc_board_config_results.mk
+endif
ifneq (,$(and $(TARGET_ARCH),$(TARGET_ARCH_SUITE)))
$(error $(board_config_mk) erroneously sets both TARGET_ARCH and TARGET_ARCH_SUITE)
@@ -204,6 +267,7 @@
# Clean up and verify BoardConfig variables
$(foreach var,$(_board_strip_readonly_list),$(eval $(var) := $$(strip $$($(var)))))
+$(foreach var,$(_board_strip_list),$(eval $(var) := $$(strip $$($(var)))))
$(foreach var,$(_board_true_false_vars), \
$(if $(filter-out true false,$($(var))), \
$(error Valid values of $(var) are "true", "false", and "". Not "$($(var))")))
@@ -439,6 +503,86 @@
endif
.KATI_READONLY := BUILDING_RAMDISK_IMAGE
+# Are we building a debug vendor_boot image
+BUILDING_DEBUG_VENDOR_BOOT_IMAGE :=
+# Can't build vendor_boot-debug.img if BOARD_BUILD_SYSTEM_ROOT_IMAGE is true,
+# because building debug vendor_boot image requires a ramdisk.
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+ ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+ $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but so is BOARD_BUILD_SYSTEM_ROOT_IMAGE. \
+ Skip building the debug vendor_boot image.)
+ endif
+# Can't build vendor_boot-debug.img if we're not building a ramdisk.
+else ifndef BUILDING_RAMDISK_IMAGE
+ ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+ $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a ramdisk image. \
+ Skip building the debug vendor_boot image.)
+ endif
+# Can't build vendor_boot-debug.img if we're not building a vendor_boot.img.
+else ifndef BUILDING_VENDOR_BOOT_IMAGE
+ ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+ $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a vendor_boot image. \
+ Skip building the debug vendor_boot image.)
+ endif
+else
+ ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),)
+ BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true
+ else ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+ BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true
+ endif
+endif
+.KATI_READONLY := BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
+_has_boot_img_artifact :=
+ifneq ($(strip $(TARGET_NO_KERNEL)),true)
+ ifdef BUILDING_BOOT_IMAGE
+ _has_boot_img_artifact := true
+ endif
+ # BUILDING_RECOVERY_IMAGE && BOARD_USES_RECOVERY_AS_BOOT implies that
+ # recovery is being built with the file name *boot.img*, which still counts
+ # as "building boot.img".
+ ifdef BUILDING_RECOVERY_IMAGE
+ ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+ _has_boot_img_artifact := true
+ endif
+ endif
+endif
+
+# Are we building a debug boot image
+BUILDING_DEBUG_BOOT_IMAGE :=
+# Can't build boot-debug.img if BOARD_BUILD_SYSTEM_ROOT_IMAGE is true,
+# because building debug boot image requires a ramdisk.
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+ ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+ $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but so is BOARD_BUILD_SYSTEM_ROOT_IMAGE. \
+ Skip building the debug boot image.)
+ endif
+# Can't build boot-debug.img if we're not building a ramdisk.
+else ifndef BUILDING_RAMDISK_IMAGE
+ ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+ $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a ramdisk image. \
+ Skip building the debug boot image.)
+ endif
+# Can't build boot-debug.img if we're not building a boot.img.
+else ifndef _has_boot_img_artifact
+ ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+ $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a boot image. \
+ Skip building the debug boot image.)
+ endif
+else
+ ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),)
+ BUILDING_DEBUG_BOOT_IMAGE := true
+ # Don't build boot-debug.img if we're already building vendor_boot-debug.img.
+ ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+ BUILDING_DEBUG_BOOT_IMAGE :=
+ endif
+ else ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+ BUILDING_DEBUG_BOOT_IMAGE := true
+ endif
+endif
+.KATI_READONLY := BUILDING_DEBUG_BOOT_IMAGE
+_has_boot_img_artifact :=
+
# Are we building a userdata image
BUILDING_USERDATA_IMAGE :=
ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),)
@@ -695,6 +839,24 @@
endif
.KATI_READONLY := BUILDING_ODM_DLKM_IMAGE
+BOARD_USES_PVMFWIMAGE :=
+ifdef BOARD_PREBUILT_PVMFWIMAGE
+ BOARD_USES_PVMFWIMAGE := true
+endif
+ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true)
+ BOARD_USES_PVMFWIMAGE := true
+endif
+.KATI_READONLY := BOARD_USES_PVMFWIMAGE
+
+BUILDING_PVMFW_IMAGE :=
+ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true)
+ BUILDING_PVMFW_IMAGE := true
+endif
+ifdef BOARD_PREBUILT_PVMFWIMAGE
+ BUILDING_PVMFW_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_PVMFW_IMAGE
+
###########################################
# Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE.
TARGET_RECOVERY_UPDATER_LIBS ?=
@@ -745,7 +907,7 @@
ifdef BOARD_VNDK_VERSION
ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
- $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current))
+ $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current)
endif
ifneq ($(BOARD_VNDK_VERSION),current)
$(call check_vndk_version,$(BOARD_VNDK_VERSION))
@@ -766,8 +928,8 @@
endif
###########################################
-# APEXes are by default flattened, i.e. non-updatable.
-# It can be unflattened (and updatable) by inheriting from
+# APEXes are by default flattened, i.e. non-updatable, if not building unbundled
+# apps. It can be unflattened (and updatable) by inheriting from
# updatable_apex.mk
#
# APEX flattening can also be forcibly enabled (resp. disabled) by
@@ -776,7 +938,7 @@
ifdef OVERRIDE_TARGET_FLATTEN_APEX
TARGET_FLATTEN_APEX := $(OVERRIDE_TARGET_FLATTEN_APEX)
else
- ifeq (,$(TARGET_FLATTEN_APEX))
+ ifeq (,$(TARGET_BUILD_APPS)$(TARGET_FLATTEN_APEX))
TARGET_FLATTEN_APEX := true
endif
endif
@@ -803,8 +965,8 @@
$(KATI_deprecated_var $(m),Please convert to Soong)))
$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\
- $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)#copy_headers),\
- $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)#copy_headers))
+ $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers),\
+ $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers))
$(foreach m,$(filter-out BUILD_COPY_HEADERS,$(DEFAULT_ERROR_BUILD_MODULE_TYPES)),\
$(if $(filter true,$(BUILD_BROKEN_USES_$(m))),\
diff --git a/core/build_id.mk b/core/build_id.mk
index 0fb59d9..ba5ca42 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -18,4 +18,4 @@
# (like "CRB01"). It must be a single word, and is
# capitalized by convention.
-BUILD_ID=SQ1A.211205.008
+BUILD_ID=AOSP.MASTER
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 94a027c..415334f 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -42,6 +42,7 @@
LOCAL_CLANG_LDFLAGS:=
LOCAL_CLASSPATH:=
LOCAL_COMPATIBILITY_SUITE:=
+LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY:=
LOCAL_COMPATIBILITY_SUPPORT_FILES:=
LOCAL_COMPRESSED_MODULE:=
LOCAL_CONLYFLAGS:=
@@ -186,6 +187,7 @@
LOCAL_MODULE_TAGS:=
LOCAL_MODULE_TARGET_ARCH:=
LOCAL_MODULE_TARGET_ARCH_WARN:=
+LOCAL_MODULE_TYPE:=
LOCAL_MODULE_UNSUPPORTED_HOST_ARCH:=
LOCAL_MODULE_UNSUPPORTED_HOST_ARCH_WARN:=
LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH:=
@@ -282,7 +284,11 @@
LOCAL_SOONG_DEXPREOPT_CONFIG :=
LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=
LOCAL_SOONG_HEADER_JAR :=
+LOCAL_SOONG_INSTALL_PAIRS :=
+LOCAL_SOONG_INSTALL_SYMLINKS :=
+LOCAL_SOONG_INSTALLED_MODULE :=
LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=
+LOCAL_SOONG_LICENSE_METADATA :=
LOCAL_SOONG_LINK_TYPE :=
LOCAL_SOONG_LINT_REPORTS :=
LOCAL_SOONG_PROGUARD_DICT :=
diff --git a/core/combo/HOST_CROSS_windows-x86.mk b/core/combo/HOST_CROSS_windows-x86.mk
deleted file mode 100644
index d924901..0000000
--- a/core/combo/HOST_CROSS_windows-x86.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Copyright (C) 2006 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Settings to use MinGW as a cross-compiler under Linux
-# Included by combo/select.make
-
-define $(combo_var_prefix)transform-shared-lib-to-toc
-$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OBJDUMP) -x $(1) | grep "^Name" | cut -f3 -d" " > $(2)
-$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)NM) -g -f p $(1) | cut -f1-2 -d" " >> $(2)
-endef
diff --git a/core/combo/HOST_CROSS_windows-x86_64.mk b/core/combo/HOST_CROSS_windows-x86_64.mk
deleted file mode 100644
index d924901..0000000
--- a/core/combo/HOST_CROSS_windows-x86_64.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Copyright (C) 2006 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Settings to use MinGW as a cross-compiler under Linux
-# Included by combo/select.make
-
-define $(combo_var_prefix)transform-shared-lib-to-toc
-$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OBJDUMP) -x $(1) | grep "^Name" | cut -f3 -d" " > $(2)
-$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)NM) -g -f p $(1) | cut -f1-2 -d" " >> $(2)
-endef
diff --git a/core/combo/select.mk b/core/combo/select.mk
index 33c8e6d..7617558 100644
--- a/core/combo/select.mk
+++ b/core/combo/select.mk
@@ -27,6 +27,13 @@
combo_var_prefix := $(combo_2nd_arch_prefix)$(combo_target)
# Set reasonable defaults for the various variables
+ifeq ($(combo_target),HOST_CROSS_)
+$(KATI_obsolete_var \
+ $(combo_var_prefix)GLOBAL_ARFLAGS \
+ $(combo_var_prefix)STATIC_LIB_SUFFIX \
+ $(combo_var_prefix)transform-shared-lib-to-toc \
+ ,HOST_CROSS builds are not supported in Make)
+else
$(combo_var_prefix)GLOBAL_ARFLAGS := crsPD -format=gnu
@@ -34,3 +41,5 @@
# Now include the combo for this specific target.
include $(BUILD_COMBOS)/$(combo_target)$(combo_os_arch).mk
+
+endif
diff --git a/core/config.mk b/core/config.mk
index 93c5db1..bfff84e 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -252,6 +252,10 @@
# Initialize SOONG_CONFIG_NAMESPACES so that it isn't recursive.
SOONG_CONFIG_NAMESPACES :=
+# TODO(asmundak): remove add_soong_config_namespace, add_soong_config_var,
+# and add_soong_config_var_value once all their usages are replaced with
+# soong_config_set/soong_config_append.
+
# The add_soong_config_namespace function adds a namespace and initializes it
# to be empty.
# $1 is the namespace.
@@ -259,7 +263,7 @@
define add_soong_config_namespace
$(eval SOONG_CONFIG_NAMESPACES += $1) \
-$(eval SOONG_CONFIG_$1 :=)
+$(eval SOONG_CONFIG_$(strip $1) :=)
endef
# The add_soong_config_var function adds a a list of soong config variables to
@@ -268,8 +272,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_$1 += $2) \
-$(foreach v,$2,$(eval SOONG_CONFIG_$1_$v := $($v)))
+$(eval SOONG_CONFIG_$(strip $1) += $2) \
+$(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $($v)))
endef
# The add_soong_config_var_value function defines a make variable and also adds
@@ -282,6 +286,40 @@
$(call add_soong_config_var,$1,$2)
endef
+# Soong config namespace variables manipulation.
+#
+# internal utility to define a namespace and a variable in it.
+define soong_config_define_internal
+$(if $(filter $1,$(SOONG_CONFIG_NAMESPACES)),,$(eval SOONG_CONFIG_NAMESPACES:=$(SOONG_CONFIG_NAMESPACES) $1)) \
+$(if $(filter $2,$(SOONG_CONFIG_$(strip $1))),,$(eval SOONG_CONFIG_$(strip $1):=$(SOONG_CONFIG_$(strip $1)) $2))
+endef
+
+# soong_config_set defines the variable in the given Soong config namespace
+# and sets its value. If the namespace does not exist, it will be defined.
+# $1 is the namespace. $2 is the variable name. $3 is the variable value.
+# Ex: $(call soong_config_set,acme,COOL_FEATURE,true)
+define soong_config_set
+$(call soong_config_define_internal,$1,$2) \
+$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$3)
+endef
+
+# soong_config_append appends to the value of the variable in the given Soong
+# config namespace. If the variable does not exist, it will be defined. If the
+# namespace does not exist, it will be defined.
+# $1 is the namespace, $2 is the variable name, $3 is the value
+define soong_config_append
+$(call soong_config_define_internal,$1,$2) \
+$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(SOONG_CONFIG_$(strip $1)_$(strip $2)) $3)
+endef
+
+# soong_config_append gets to the value of the variable in the given Soong
+# config namespace. If the namespace or variables does not exist, an
+# empty string will be returned.
+# $1 is the namespace, $2 is the variable name
+define soong_config_get
+$(SOONG_CONFIG_$(strip $1)_$(strip $2))
+endef
+
# Set the extensions used for various packages
COMMON_PACKAGE_SUFFIX := .zip
COMMON_JAVA_PACKAGE_SUFFIX := .jar
@@ -445,6 +483,11 @@
ifneq ($(filter true,$(SOONG_ALLOW_MISSING_DEPENDENCIES)),)
ALLOW_MISSING_DEPENDENCIES := true
endif
+# Mac builds default to ALLOW_MISSING_DEPENDENCIES, at least until the host
+# tools aren't enabled by default for Mac.
+ifeq ($(HOST_OS),darwin)
+ ALLOW_MISSING_DEPENDENCIES := true
+endif
.KATI_READONLY := ALLOW_MISSING_DEPENDENCIES
TARGET_BUILD_USE_PREBUILT_SDKS :=
@@ -485,11 +528,8 @@
#
ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))
AAPT := $(HOST_OUT_EXECUTABLES)/aapt
- MAINDEXCLASSES := $(HOST_OUT_EXECUTABLES)/mainDexClasses
-
else # TARGET_BUILD_USE_PREBUILT_SDKS
AAPT := $(prebuilt_sdk_tools_bin)/aapt
- MAINDEXCLASSES := $(prebuilt_sdk_tools)/mainDexClasses
endif # TARGET_BUILD_USE_PREBUILT_SDKS
ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))
@@ -507,14 +547,14 @@
ACP := $(prebuilt_build_tools_bin)/acp
CKATI := $(prebuilt_build_tools_bin)/ckati
DEPMOD := $(HOST_OUT_EXECUTABLES)/depmod
-FILESLIST := $(SOONG_HOST_OUT_EXECUTABLES)/fileslist
+FILESLIST := $(HOST_OUT_EXECUTABLES)/fileslist
FILESLIST_UTIL :=$= build/make/tools/fileslist_util.py
HOST_INIT_VERIFIER := $(HOST_OUT_EXECUTABLES)/host_init_verifier
-XMLLINT := $(SOONG_HOST_OUT_EXECUTABLES)/xmllint
+XMLLINT := $(HOST_OUT_EXECUTABLES)/xmllint
# SOONG_ZIP is exported by Soong, but needs to be defined early for
# $OUT/dexpreopt.global. It will be verified against the Soong version.
-SOONG_ZIP := $(SOONG_HOST_OUT_EXECUTABLES)/soong_zip
+SOONG_ZIP := $(HOST_OUT_EXECUTABLES)/soong_zip
# ---------------------------------------------------------------
# Generic tools.
@@ -603,7 +643,7 @@
# Path to tools.jar
HOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar
-APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner --compatible-output=no
+APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner
# Boolean variable determining if the allow list for compatible properties is enabled
PRODUCT_COMPATIBLE_PROPERTY := true
@@ -722,8 +762,14 @@
.KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES
ifdef PRODUCT_SHIPPING_API_LEVEL
- ifneq ($(call numbers_less_than,$(PRODUCT_SHIPPING_API_LEVEL),$(BOARD_SYSTEMSDK_VERSIONS)),)
- $(error BOARD_SYSTEMSDK_VERSIONS ($(BOARD_SYSTEMSDK_VERSIONS)) must all be greater than or equal to PRODUCT_SHIPPING_API_LEVEL ($(PRODUCT_SHIPPING_API_LEVEL)))
+ board_api_level := $(firstword $(BOARD_API_LEVEL) $(BOARD_SHIPPING_API_LEVEL))
+ ifneq (,$(board_api_level))
+ min_systemsdk_version := $(call math_min,$(board_api_level),$(PRODUCT_SHIPPING_API_LEVEL))
+ else
+ min_systemsdk_version := $(PRODUCT_SHIPPING_API_LEVEL)
+ endif
+ 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)
@@ -785,13 +831,22 @@
sepolicy_major_vers :=
sepolicy_minor_vers :=
+# BOARD_SEPOLICY_VERS must take the format "NN.m" and contain the sepolicy
+# version identifier corresponding to the sepolicy on which the non-platform
+# policy is to be based. If unspecified, this will build against the current
+# public platform policy in tree
+ifndef BOARD_SEPOLICY_VERS
+# The default platform policy version.
+BOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION)
+endif
+
# A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports.
PLATFORM_SEPOLICY_COMPAT_VERSIONS := \
- 26.0 \
- 27.0 \
28.0 \
29.0 \
30.0 \
+ 31.0 \
+ 32.0 \
.KATI_READONLY := \
PLATFORM_SEPOLICY_COMPAT_VERSIONS \
@@ -998,6 +1053,14 @@
BOARD_PREBUILT_HIDDENAPI_DIR ?=
.KATI_READONLY := BOARD_PREBUILT_HIDDENAPI_DIR
+ifdef USE_HOST_MUSL
+ ifneq (,$(or $(BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE),\
+ $(BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY),\
+ $(BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY)))
+ $(error USE_HOST_MUSL can't be set when native host builds are enabled in Make with BUILD_BROKEN_USES_BUILD_HOST_*)
+ endif
+endif
+
# ###############################################################
# Set up final options.
# ###############################################################
@@ -1168,7 +1231,4 @@
DEFAULT_DATA_OUT_MODULES := ltp $(ltp_packages) $(kselftest_modules)
.KATI_READONLY := DEFAULT_DATA_OUT_MODULES
-# Make RECORD_ALL_DEPS readonly.
-RECORD_ALL_DEPS :=$= $(filter true,$(RECORD_ALL_DEPS))
-
include $(BUILD_SYSTEM)/dumpvar.mk
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 46f7f24..a0ff119 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -299,9 +299,6 @@
my_sanitize := $(filter-out cfi,$(my_sanitize))
my_cflags += -fno-lto
my_ldflags += -fno-lto
-
- # TODO(b/133876586): Disable experimental pass manager for fuzzer builds.
- my_cflags += -fno-experimental-new-pass-manager
endif
ifneq ($(filter integer_overflow,$(my_sanitize)),)
diff --git a/core/definitions.mk b/core/definitions.mk
index c5fe76b..c981152 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -37,6 +37,10 @@
# sub-variables.
ALL_MODULES:=
+# The relative paths of the non-module targets in the system.
+ALL_NON_MODULES:=
+NON_MODULES_WITHOUT_LICENSE_METADATA:=
+
# Full paths to targets that should be added to the "make droid"
# set of installed targets.
ALL_DEFAULT_INSTALLED_MODULES:=
@@ -539,7 +543,7 @@
$(eval _all_module_refs := \
$(sort \
$(foreach m,$(sort $(ALL_MODULES)), \
- $(ALL_MODULES.$(m).NOTICE_DEPS) \
+ $(call word-colon,1,$(ALL_MODULES.$(m).NOTICE_DEPS)) \
) \
) \
) \
@@ -557,7 +561,7 @@
$(eval ALL_MODULES.$(m).NOTICE_DEPS := \
$(sort \
$(foreach d,$(sort $(ALL_MODULES.$(m).NOTICE_DEPS)), \
- $(_lookup.$(d)) \
+ $(foreach n,$(_lookup.$(call word-colon,1,$(d))),$(n):$(call wordlist-colon,2,9999,$(d))) \
) \
) \
) \
@@ -573,36 +577,67 @@
endef
###########################################################
-## License metadata build rule for my_register_name $1
+## License metadata build rule for my_register_name $(1)
###########################################################
define license-metadata-rule
-$(strip $(eval _dir := $(call license-metadata-dir)))
-$(strip $(eval _deps := $(sort $(filter-out $(_dir)/$(1).meta_lic,$(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS), $(_dir)/$(d).meta_lic)))))
-$(strip $(eval _notices := $(sort $(ALL_MODULES.$(1).NOTICES))))
-$(strip $(eval _tgts := $(sort $(ALL_MODULES.$(1).BUILT) $(ALL_MODULES.$(1).INSTALLED))))
-$(foreach b,$(_tgts),
-$(_dir)/$(b).meta_module ::
- mkdir -p $$(dir $$@)
- echo $(_dir)/$(1).meta_lic >> $$@
- sort -u $$@ -o $$@
+$(foreach meta_lic, $(ALL_MODULES.$(1).DELAYED_META_LIC),$(call _license-metadata-rule,$(1),$(meta_lic)))
+$(call notice-rule,$(1))
+endef
-)
-$(_dir)/$(1).meta_lic: PRIVATE_KINDS := $(sort $(ALL_MODULES.$(1).LICENSE_KINDS))
-$(_dir)/$(1).meta_lic: PRIVATE_CONDITIONS := $(sort $(ALL_MODULES.$(1).LICENSE_CONDITIONS))
-$(_dir)/$(1).meta_lic: PRIVATE_NOTICES := $(_notices)
-$(_dir)/$(1).meta_lic: PRIVATE_NOTICE_DEPS := $(_deps)
-$(_dir)/$(1).meta_lic: PRIVATE_TARGETS := $(_tgts)
-$(_dir)/$(1).meta_lic: PRIVATE_IS_CONTAINER := $(ALL_MODULES.$(1).IS_CONTAINER)
-$(_dir)/$(1).meta_lic: PRIVATE_PACKAGE_NAME := $(strip $(ALL_MODULES.$(1).LICENSE_PACKAGE_NAME))
-$(_dir)/$(1).meta_lic: PRIVATE_INSTALL_MAP := $(sort $(ALL_MODULES.$(1).LICENSE_INSTALL_MAP))
-$(_dir)/$(1).meta_lic : $(_deps) $(_notices) $(foreach b,$(_tgts), $(_dir)/$(b).meta_module) build/make/tools/build-license-metadata.sh
+define _license-metadata-rule
+$(strip $(eval _srcs := $(strip $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED)), $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT)), $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT), $(call word-colon,1,$d)))))))
+$(strip $(eval _deps := $(sort $(filter-out $(2)%,\
+ $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),\
+ $(addsuffix :$(call wordlist-colon,2,9999,$(d)), \
+ $(foreach dt,$(ALL_MODULES.$(d).BUILT) $(ALL_MODULES.$(d).INSTALLED),\
+ $(ALL_TARGETS.$(dt).META_LIC))))))))
+$(strip $(eval _notices := $(sort $(ALL_MODULES.$(1).NOTICES))))
+$(strip $(eval _tgts := $(sort $(ALL_MODULES.$(1).BUILT))))
+$(strip $(eval _inst := $(sort $(ALL_MODULES.$(1).INSTALLED))))
+$(strip $(eval _path := $(sort $(ALL_MODULES.$(1).PATH))))
+$(strip $(eval _map := $(strip $(foreach _m,$(sort $(ALL_MODULES.$(1).LICENSE_INSTALL_MAP)), \
+ $(eval _s := $(call word-colon,1,$(_m))) \
+ $(eval _d := $(call word-colon,2,$(_m))) \
+ $(eval _ns := $(if $(strip $(ALL_MODULES.$(_s).INSTALLED)),$(ALL_MODULES.$(_s).INSTALLED),$(if $(strip $(ALL_MODULES.$(_s).BUILT)),$(ALL_MODULES.$(_s).BUILT),$(_s)))) \
+ $(foreach ns,$(_ns),$(ns):$(_d) ) \
+))))
+
+$(2): PRIVATE_KINDS := $(sort $(ALL_MODULES.$(1).LICENSE_KINDS))
+$(2): PRIVATE_CONDITIONS := $(sort $(ALL_MODULES.$(1).LICENSE_CONDITIONS))
+$(2): PRIVATE_NOTICES := $(_notices)
+$(2): PRIVATE_NOTICE_DEPS := $(_deps)
+$(2): PRIVATE_SOURCES := $(_srcs)
+$(2): PRIVATE_TARGETS := $(_tgts)
+$(2): PRIVATE_INSTALLED := $(_inst)
+$(2): PRIVATE_PATH := $(_path)
+$(2): PRIVATE_IS_CONTAINER := $(ALL_MODULES.$(1).IS_CONTAINER)
+$(2): PRIVATE_PACKAGE_NAME := $(strip $(ALL_MODULES.$(1).LICENSE_PACKAGE_NAME))
+$(2): PRIVATE_INSTALL_MAP := $(_map)
+$(2): PRIVATE_MODULE_TYPE := $(ALL_MODULES.$(1).MODULE_TYPE)
+$(2): PRIVATE_MODULE_CLASS := $(ALL_MODULES.$(1).MODULE_CLASS)
+$(2): PRIVATE_INSTALL_MAP := $(_map)
+$(2): $(BUILD_LICENSE_METADATA)
+$(2) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) )
rm -f $$@
mkdir -p $$(dir $$@)
- build/make/tools/build-license-metadata.sh -k $$(PRIVATE_KINDS) -c $$(PRIVATE_CONDITIONS) -n $$(PRIVATE_NOTICES) -d $$(PRIVATE_NOTICE_DEPS) -m $$(PRIVATE_INSTALL_MAP) -t $$(PRIVATE_TARGETS) $$(if $$(PRIVATE_IS_CONTAINER),-is_container) -p $$(PRIVATE_PACKAGE_NAME) -o $$@
+ $(BUILD_LICENSE_METADATA) \
+ $$(addprefix -mt ,$$(PRIVATE_MODULE_TYPE)) \
+ $$(addprefix -mc ,$$(PRIVATE_MODULE_CLASS)) \
+ $$(addprefix -k ,$$(PRIVATE_KINDS)) \
+ $$(addprefix -c ,$$(PRIVATE_CONDITIONS)) \
+ $$(addprefix -n ,$$(PRIVATE_NOTICES)) \
+ $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS)) \
+ $$(addprefix -s ,$$(PRIVATE_SOURCES)) \
+ $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP)) \
+ $$(addprefix -t ,$$(PRIVATE_TARGETS)) \
+ $$(addprefix -i ,$$(PRIVATE_INSTALLED)) \
+ $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \
+ -p '$$(PRIVATE_PACKAGE_NAME)' \
+ $$(addprefix -r ,$$(PRIVATE_PATH)) \
+ -o $$@
+endef
-.PHONY: $(1).meta_lic
-$(1).meta_lic : $(_dir)/$(1).meta_lic
-
+define notice-rule
$(strip $(eval _mifs := $(sort $(ALL_MODULES.$(1).MODULE_INSTALLED_FILENAMES))))
$(strip $(eval _infs := $(sort $(ALL_MODULES.$(1).INSTALLED_NOTICE_FILE))))
@@ -611,11 +646,10 @@
$(if $(strip $(filter $(1),$(INSTALLED_NOTICE_FILES.$(inf).MODULE))),
$(strip $(eval _mif := $(firstword $(foreach m,$(_mifs),$(if $(filter %/src/$(m).txt,$(inf)),$(m))))))
-$(inf) : $(_dir)/$(1).meta_lic
$(inf): PRIVATE_INSTALLED_MODULE := $(_mif)
-$(inf) : PRIVATE_NOTICES := $(_notices)
+$(inf) : PRIVATE_NOTICES := $(sort $(foreach n,$(_notices),$(call word-colon,1,$(n) )))
-$(inf): $(_notices)
+$(inf): $(foreach n,$(_notices),$(call word-colon,1,$(n)) )
@echo Notice file: $$< -- $$@
mkdir -p $$(dir $$@)
awk 'FNR==1 && NR > 1 {print "\n"} {print}' $$(PRIVATE_NOTICES) > $$@
@@ -625,10 +659,190 @@
endef
###########################################################
+## License metadata build rule for non-module target $(1)
+###########################################################
+define non-module-license-metadata-rule
+$(strip $(eval _dir := $(call license-metadata-dir)))
+$(strip $(eval _tgt := $(strip $(1))))
+$(strip $(eval _deps := $(sort $(filter-out 0p: :,$(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)),$(ALL_TARGETS.$(call word-colon,1,$(d)).META_LIC):$(call wordlist-colon,2,9999,$(d)))))))
+$(strip $(eval _notices := $(sort $(ALL_NON_MODULES.$(_tgt).NOTICES))))
+$(strip $(eval _path := $(sort $(ALL_NON_MODULES.$(_tgt).PATH))))
+$(strip $(eval _install_map := $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS)))
+$(strip \
+ $(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)), \
+ $(if $(strip $(ALL_TARGETS.$(d).META_LIC)), \
+ , \
+ $(eval NON_MODULES_WITHOUT_LICENSE_METADATA += $(d))) \
+ ) \
+)
+
+$(_dir)/$(_tgt).meta_lic: PRIVATE_KINDS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_KINDS))
+$(_dir)/$(_tgt).meta_lic: PRIVATE_CONDITIONS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS))
+$(_dir)/$(_tgt).meta_lic: PRIVATE_NOTICES := $(_notices)
+$(_dir)/$(_tgt).meta_lic: PRIVATE_NOTICE_DEPS := $(_deps)
+$(_dir)/$(_tgt).meta_lic: PRIVATE_SOURCES := $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)
+$(_dir)/$(_tgt).meta_lic: PRIVATE_TARGETS := $(_tgt)
+$(_dir)/$(_tgt).meta_lic: PRIVATE_PATH := $(_path)
+$(_dir)/$(_tgt).meta_lic: PRIVATE_IS_CONTAINER := $(ALL_NON_MODULES.$(_tgt).IS_CONTAINER)
+$(_dir)/$(_tgt).meta_lic: PRIVATE_PACKAGE_NAME := $(strip $(ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME))
+$(_dir)/$(_tgt).meta_lic: PRIVATE_INSTALL_MAP := $(strip $(_install_map))
+$(_dir)/$(_tgt).meta_lic: $(BUILD_LICENSE_METADATA)
+$(_dir)/$(_tgt).meta_lic : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) )
+ rm -f $$@
+ mkdir -p $$(dir $$@)
+ $(BUILD_LICENSE_METADATA) \
+ -mt raw -mc unknown \
+ $$(addprefix -k ,$$(PRIVATE_KINDS)) \
+ $$(addprefix -c ,$$(PRIVATE_CONDITIONS)) \
+ $$(addprefix -n ,$$(PRIVATE_NOTICES)) \
+ $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS)) \
+ $$(addprefix -s ,$$(PRIVATE_SOURCES)) \
+ $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP)) \
+ $$(addprefix -t ,$$(PRIVATE_TARGETS)) \
+ $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \
+ -p '$$(PRIVATE_PACKAGE_NAME)' \
+ $$(addprefix -r ,$$(PRIVATE_PATH)) \
+ -o $$@
+
+endef
+
+###########################################################
+## Declare the license metadata for non-module target $(1).
+##
+## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0
+## $(3) -- license conditions e.g. notice by_exception_only
+## $(4) -- license text filenames (notices)
+## $(5) -- package name
+## $(6) -- project path
+###########################################################
+define declare-license-metadata
+$(strip \
+ $(eval _tgt := $(strip $(1))) \
+ $(eval ALL_NON_MODULES += $(_tgt)) \
+ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \
+ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \
+ $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \
+ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \
+ $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \
+)
+endef
+
+###########################################################
+## Declare the license metadata for non-module container-type target $(1).
+##
+## Container-type targets are targets like .zip files that
+## merely aggregate other files.
+##
+## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0
+## $(3) -- license conditions e.g. notice by_exception_only
+## $(4) -- license text filenames (notices)
+## $(5) -- package name
+## $(6) -- project path
+###########################################################
+define declare-container-license-metadata
+$(strip \
+ $(eval _tgt := $(strip $(1))) \
+ $(eval ALL_NON_MODULES += $(_tgt)) \
+ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \
+ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \
+ $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \
+ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \
+ $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \
+ $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \
+)
+endef
+
+###########################################################
+## Declare that non-module target $(1) is a non-copyrightable file.
+##
+## e.g. an information-only file merely listing other files.
+###########################################################
+define declare-0p-target
+$(strip \
+ $(eval _tgt := $(strip $(1))) \
+ $(eval ALL_0P_TARGETS += $(_tgt)) \
+)
+endef
+
+###########################################################
+## Declare non-module target $(1) to have a first-party license
+## (Android Apache 2.0)
+##
+## $(2) -- project path
+###########################################################
+define declare-1p-target
+$(call declare-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2))
+endef
+
+###########################################################
+## Declare non-module container-type target $(1) to have a
+## first-party license (Android Apache 2.0).
+##
+## Container-type targets are targets like .zip files that
+## merely aggregate other files.
+##
+## $92) -- project path
+###########################################################
+define declare-1p-container
+$(call declare-container-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2))
+endef
+
+###########################################################
+## Declare license dependencies $(2) for non-module target $(1)
+###########################################################
+define declare-license-deps
+$(strip \
+ $(eval _tgt := $(strip $(1))) \
+ $(eval ALL_NON_MODULES += $(_tgt)) \
+ $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \
+)
+endef
+
+###########################################################
+## Declare license dependencies $(2) for non-module container-type target $(1)
+##
+## Container-type targets are targets like .zip files that
+## merely aggregate other files.
+##
+## $(3) -- root mappings space-separated source:target
+###########################################################
+define declare-container-license-deps
+$(strip \
+ $(eval _tgt := $(strip $(1))) \
+ $(eval ALL_NON_MODULES += $(_tgt)) \
+ $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \
+ $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \
+ $(eval ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS := $(strip $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS) $(3))) \
+)
+endef
+
+###########################################################
+## Declares the rule to report targets with no license metadata.
+###########################################################
+define report-missing-licenses-rule
+.PHONY: reportmissinglicenses
+reportmissinglicenses: PRIVATE_NON_MODULES:=$(sort $(NON_MODULES_WITHOUT_LICENSE_METADATA))
+reportmissinglicenses:
+ @echo Reporting $$(words $$(PRIVATE_NON_MODULES)) targets without license metadata
+ $$(foreach t,$$(PRIVATE_NON_MODULES),if ! [ -h $$(t) ]; then echo No license metadata for $$(t) >&2; fi;)
+
+endef
+
+###########################################################
## Declares a license metadata build rule for ALL_MODULES
###########################################################
define build-license-metadata
-$(foreach m,$(sort $(ALL_MODULES)),$(eval $(call license-metadata-rule,$(m))))
+$(strip \
+ $(strip $(eval _dir := $(call license-metadata-dir))) \
+ $(foreach t,$(sort $(ALL_0P_TARGETS)), \
+ $(eval ALL_TARGETS.$(t).META_LIC := 0p) \
+ ) \
+ $(foreach t,$(sort $(ALL_NON_MODULES)), \
+ $(eval ALL_TARGETS.$(t).META_LIC := $(_dir)/$(t).meta_lic) \
+ ) \
+ $(foreach t,$(sort $(ALL_NON_MODULES)),$(eval $(call non-module-license-metadata-rule,$(t)))) \
+ $(foreach m,$(sort $(ALL_MODULES)),$(eval $(call license-metadata-rule,$(m)))) \
+ $(eval $(call report-missing-licenses-rule)))
endef
###########################################################
@@ -1106,11 +1320,11 @@
$(hide) mkdir -p $(dir $@)
$(hide) $(BCC_COMPAT) -O3 -o $(dir $@)/$(notdir $(<:.bc=.o)) -fPIC -shared \
-rt-path $(RS_PREBUILT_CLCORE) -mtriple $(RS_COMPAT_TRIPLE) $<
-$(hide) $(PRIVATE_CXX_LINK) -shared -Wl,-soname,$(notdir $@) -nostdlib \
+$(hide) $(PRIVATE_CXX_LINK) -fuse-ld=lld -target $(CLANG_TARGET_TRIPLE) -shared -Wl,-soname,$(notdir $@) -nostdlib \
-Wl,-rpath,\$$ORIGIN/../lib \
$(dir $@)/$(notdir $(<:.bc=.o)) \
$(RS_PREBUILT_COMPILER_RT) \
- -o $@ $(CLANG_TARGET_GLOBAL_LDFLAGS) -Wl,--hash-style=sysv \
+ -o $@ $(CLANG_TARGET_GLOBAL_LLDFLAGS) -Wl,--hash-style=sysv \
-L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib64 \
-L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib \
$(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so \
@@ -1928,21 +2142,10 @@
# b/37750224
AAPT_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0
-# Search for generated R.java/Manifest.java in $1, copy the found R.java as $2.
-# Also copy them to a central 'R' directory to make it easier to add the files to an IDE.
+# Search for generated R.java in $1, copy the found R.java as $2.
define find-generated-R.java
-$(hide) for GENERATED_MANIFEST_FILE in `find $(1) \
- -name Manifest.java 2> /dev/null`; do \
- dir=`awk '/package/{gsub(/\./,"/",$$2);gsub(/;/,"",$$2);print $$2;exit}' $$GENERATED_MANIFEST_FILE`; \
- mkdir -p $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
- cp $$GENERATED_MANIFEST_FILE $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
- done;
$(hide) for GENERATED_R_FILE in `find $(1) \
-name R.java 2> /dev/null`; do \
- dir=`awk '/package/{gsub(/\./,"/",$$2);gsub(/;/,"",$$2);print $$2;exit}' $$GENERATED_R_FILE`; \
- mkdir -p $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
- cp $$GENERATED_R_FILE $(TARGET_COMMON_OUT_ROOT)/R/$$dir \
- || exit 31; \
cp $$GENERATED_R_FILE $(2) || exit 32; \
done;
@# Ensure that the target file is always created, i.e. also in case we did not
@@ -2340,6 +2543,7 @@
define add-jar-resources-to-package
rm -rf $(3)
mkdir -p $(3)
+ zipinfo -1 $(2) > /dev/null
unzip -qo $(2) -d $(3) $$(zipinfo -1 $(2) | grep -v -E "\.class$$")
$(JAR) uf $(1) $(call jar-args-sorted-files-in-directory,$(3))
endef
@@ -3004,9 +3208,10 @@
# Can be passed a subdirectory to use for the common testcase directory.
define compatibility_suite_dirs
$(strip \
- $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1)),\
- $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\
- $(COMPATIBILITY_TESTCASES_OUT_$(1))) \
+ $(if $(COMPATIBILITY_TESTCASES_OUT_$(1)), \
+ $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1))$(LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY),\
+ $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\
+ $(COMPATIBILITY_TESTCASES_OUT_$(1)))) \
$($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(2))
endef
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index 51238a3..d5293cf 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -104,11 +104,13 @@
$(call add_json_bool, DisableGenerateProfile, $(filter false,$(WITH_DEX_PREOPT_GENERATE_PROFILE)))
$(call add_json_str, ProfileDir, $(PRODUCT_DEX_PREOPT_PROFILE_DIR))
$(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS))
- $(call add_json_list, UpdatableBootJars, $(PRODUCT_UPDATABLE_BOOT_JARS))
+ $(call add_json_list, ApexBootJars, $(PRODUCT_APEX_BOOT_JARS))
$(call add_json_list, ArtApexJars, $(filter $(PRODUCT_BOOT_JARS),$(ART_APEX_JARS)))
$(call add_json_list, SystemServerJars, $(PRODUCT_SYSTEM_SERVER_JARS))
$(call add_json_list, SystemServerApps, $(PRODUCT_SYSTEM_SERVER_APPS))
- $(call add_json_list, UpdatableSystemServerJars, $(PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS))
+ $(call add_json_list, ApexSystemServerJars, $(PRODUCT_APEX_SYSTEM_SERVER_JARS))
+ $(call add_json_list, StandaloneSystemServerJars, $(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS))
+ $(call add_json_list, ApexStandaloneSystemServerJars, $(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS))
$(call add_json_bool, BrokenSuboptimalOrderOfSystemServerJars, $(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS))
$(call add_json_list, SpeedApps, $(PRODUCT_DEXPREOPT_SPEED_APPS))
$(call add_json_list, PreoptFlags, $(PRODUCT_DEX_PREOPT_DEFAULT_FLAGS))
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index a2837f3..ea50313 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -60,11 +60,6 @@
LOCAL_DEX_PREOPT :=
endif
-# Don't preopt system server jars that are updatable.
-ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS)))
- LOCAL_DEX_PREOPT :=
-endif
-
# if WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true and module is not in boot class path skip
# Also preopt system server jars since selinux prevents system server from loading anything from
# /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
@@ -278,6 +273,7 @@
my_dexpreopt_image_locations_on_host :=
my_dexpreopt_image_locations_on_device :=
my_dexpreopt_infix := boot
+my_create_dexpreopt_config :=
ifeq (true, $(DEXPREOPT_USE_ART_IMAGE))
my_dexpreopt_infix := art
endif
@@ -293,7 +289,16 @@
LOCAL_UNCOMPRESS_DEX := true
endif
endif
+ my_create_dexpreopt_config := true
+endif
+# dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true,
+# but dexpreopt config files are required to dexpreopt in post-processing.
+ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true)
+ my_create_dexpreopt_config := true
+endif
+
+ifeq ($(my_create_dexpreopt_config), true)
ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
my_module_multilib := $(LOCAL_MULTILIB)
# If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
@@ -402,8 +407,6 @@
my_dexpreopt_config := $(intermediates)/dexpreopt.config
my_dexpreopt_config_for_postprocessing := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config
- my_dexpreopt_script := $(intermediates)/dexpreopt.sh
- my_dexpreopt_zip := $(intermediates)/dexpreopt.zip
my_dexpreopt_config_merger := $(BUILD_SYSTEM)/dex_preopt_config_merger.py
$(my_dexpreopt_config): $(my_dexpreopt_dep_configs) $(my_dexpreopt_config_merger)
@@ -416,12 +419,39 @@
echo -e -n '$(subst $(newline),\n,$(subst ','\'',$(subst \,\\,$(PRIVATE_CONTENTS))))' > $@
$(PRIVATE_CONFIG_MERGER) $@ $(PRIVATE_DEP_CONFIGS)
+$(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing)))
+
+$(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing)
+
+# System server jars defined in Android.mk are deprecated.
+ifneq (true, $(PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS))
+ ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS) $(PRODUCT_APEX_SYSTEM_SERVER_JARS)))
+ $(error System server jars defined in Android.mk are deprecated. \
+ Convert $(LOCAL_MODULE) to Android.bp or temporarily disable the error \
+ with 'PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS := true')
+ endif
+endif
+
+ifdef LOCAL_DEX_PREOPT
+ # System server jars must be copied into predefined locations expected by
+ # dexpreopt. Copy rule must be exposed to Ninja (as it uses these files as
+ # inputs), so it cannot go in dexpreopt.sh.
+ ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS)))
+ my_dexpreopt_jar_copy := $(OUT_DIR)/soong/system_server_dexjars/$(LOCAL_MODULE).jar
+ $(my_dexpreopt_jar_copy): PRIVATE_BUILT_MODULE := $(LOCAL_BUILT_MODULE)
+ $(my_dexpreopt_jar_copy): $(LOCAL_BUILT_MODULE)
+ @cp $(PRIVATE_BUILT_MODULE) $@
+ endif
+
+ my_dexpreopt_script := $(intermediates)/dexpreopt.sh
+ my_dexpreopt_zip := $(intermediates)/dexpreopt.zip
.KATI_RESTAT: $(my_dexpreopt_script)
$(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE)
$(my_dexpreopt_script): PRIVATE_GLOBAL_SOONG_CONFIG := $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)
$(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(DEX_PREOPT_CONFIG_FOR_MAKE)
$(my_dexpreopt_script): PRIVATE_MODULE_CONFIG := $(my_dexpreopt_config)
$(my_dexpreopt_script): $(DEXPREOPT_GEN)
+ $(my_dexpreopt_script): $(my_dexpreopt_jar_copy)
$(my_dexpreopt_script): $(my_dexpreopt_config) $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(DEX_PREOPT_CONFIG_FOR_MAKE)
@echo "$(PRIVATE_MODULE) dexpreopt gen"
$(DEXPREOPT_GEN) \
@@ -431,8 +461,6 @@
-dexpreopt_script $@ \
-out_dir $(OUT_DIR)
- $(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing)))
-
my_dexpreopt_deps := $(my_dex_jar)
my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))
my_dexpreopt_deps += \
@@ -468,7 +496,6 @@
$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
$(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_zip)
- $(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing)
$(my_all_targets): $(my_dexpreopt_zip)
@@ -477,3 +504,4 @@
my_dexpreopt_zip :=
my_dexpreopt_config_for_postprocessing :=
endif # LOCAL_DEX_PREOPT
+endif # my_create_dexpreopt_config
\ No newline at end of file
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 8c25086..b673050 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -167,6 +167,10 @@
else
$(error Unsupported HOST_CROSS_OS $(HOST_CROSS_OS))
endif
+else ifeq ($(HOST_OS),darwin)
+ HOST_CROSS_OS := darwin
+ HOST_CROSS_ARCH := arm64
+ HOST_CROSS_2ND_ARCH :=
endif
ifeq ($(HOST_OS),)
@@ -293,8 +297,11 @@
#################################################################
# Set up minimal BOOTCLASSPATH list of jars to build/execute
# java code with dalvikvm/art.
-# Jars present in the ART apex. These should match exactly the list of
-# Java libraries in the ART apex build rule.
+# Jars present in the ART apex. These should match exactly the list of Java
+# libraries in art-bootclasspath-fragment. The APEX variant name
+# (com.android.art) is the same regardless which Soong module provides the ART
+# APEX. See the long comment in build/soong/java/dexprepopt_bootjars.go for
+# details.
ART_APEX_JARS := \
com.android.art:core-oj \
com.android.art:core-libart \
@@ -307,6 +314,16 @@
endif
#################################################################
+# Dumps all variables that match [A-Z][A-Z0-9_]* (with a few exceptions)
+# to the file at $(1). It is used to print only the variables that are
+# likely to be relevant to the product or board configuration.
+define dump-variables-rbc
+$(file >$(OUT_DIR)/dump-variables-rbc-temp.txt,$(subst $(space),$(newline),$(.VARIABLES)))\
+$(file >$(1),\
+$(foreach v, $(shell grep -he "^[A-Z][A-Z0-9_]*$$" $(OUT_DIR)/dump-variables-rbc-temp.txt | grep -vhE "^(SOONG_.*|LOCAL_PATH|TOPDIR|PRODUCT_COPY_OUT_.*)$$"),\
+$(v) := $(strip $($(v)))$(newline)))
+endef
+
# Read the product specs so we can get TARGET_DEVICE and other
# variables that we need in order to locate the output files.
include $(BUILD_SYSTEM)/product_config.mk
@@ -320,6 +337,12 @@
SDK_HOST_ARCH := x86
TARGET_OS := linux
+# Some board configuration files use $(PRODUCT_OUT)
+TARGET_OUT_ROOT := $(OUT_DIR)/target
+TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product
+PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
+.KATI_READONLY := TARGET_OUT_ROOT TARGET_PRODUCT_OUT_ROOT PRODUCT_OUT
+
include $(BUILD_SYSTEM)/board_config.mk
# the target build type defaults to release
@@ -332,28 +355,24 @@
SOONG_OUT_DIR := $(OUT_DIR)/soong
-TARGET_OUT_ROOT := $(OUT_DIR)/target
-
HOST_OUT_ROOT := $(OUT_DIR)/host
-.KATI_READONLY := SOONG_OUT_DIR TARGET_OUT_ROOT HOST_OUT_ROOT
+.KATI_READONLY := SOONG_OUT_DIR HOST_OUT_ROOT
# We want to avoid two host bin directories in multilib build.
HOST_OUT := $(HOST_OUT_ROOT)/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
-SOONG_HOST_OUT := $(SOONG_OUT_DIR)/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
+
+# Soong now installs to the same directory as Make.
+SOONG_HOST_OUT := $(HOST_OUT)
HOST_CROSS_OUT := $(HOST_OUT_ROOT)/$(HOST_CROSS_OS)-$(HOST_CROSS_ARCH)
.KATI_READONLY := HOST_OUT SOONG_HOST_OUT HOST_CROSS_OUT
-TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product
-
TARGET_COMMON_OUT_ROOT := $(TARGET_OUT_ROOT)/common
HOST_COMMON_OUT_ROOT := $(HOST_OUT_ROOT)/common
-PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
-
-.KATI_READONLY := TARGET_PRODUCT_OUT_ROOT TARGET_COMMON_OUT_ROOT HOST_COMMON_OUT_ROOT PRODUCT_OUT
+.KATI_READONLY := TARGET_COMMON_OUT_ROOT HOST_COMMON_OUT_ROOT
OUT_DOCS := $(TARGET_COMMON_OUT_ROOT)/docs
OUT_NDK_DOCS := $(TARGET_COMMON_OUT_ROOT)/ndk-docs
diff --git a/core/envsetup.rbc b/core/envsetup.rbc
deleted file mode 100644
index 451623b..0000000
--- a/core/envsetup.rbc
+++ /dev/null
@@ -1,207 +0,0 @@
-# Copyright 2021 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-load(":build_id.rbc|init", _build_id_init = "init")
-
-def _all_versions():
- """Returns all known versions."""
- versions = ["OPR1", "OPD1", "OPD2", "OPM1", "OPM2", "PPR1", "PPD1", "PPD2", "PPM1", "PPM2", "QPR1"]
- for v in ("Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"):
- for e in ("P1A", "P1B", "P2A", "P2B", "D1A", "D1B", "D2A", "D2B", "Q1A", "Q1B", "Q2A", "Q2B", "Q3A", "Q3B"):
- versions.append(v + e)
- return versions
-
-def _allowed_versions(all_versions, min_version, max_version, default_version):
- """Checks that version range and default versions is valid, returns all versions in range."""
- for v in (min_version, max_version, default_version):
- if v not in all_versions:
- fail("% is invalid" % v)
-
- min_i = all_versions.index(min_version)
- max_i = all_versions.index(max_version)
- def_i = all_versions.index(default_version)
- if min_i > max_i:
- fail("%s should come before %s in the version list" % (min_version, max_version))
- if def_i < min_i or def_i > max_i:
- fail("%s should come between % and %s" % (default_version, min_version, max_version))
- return all_versions[min_i:max_i + 1]
-
-# This function is a manual conversion of the version_defaults.mk
-def _versions_default(g, all_versions):
- """Handle various build version information.
-
- Guarantees that the following are defined:
- PLATFORM_VERSION
- PLATFORM_SDK_VERSION
- PLATFORM_VERSION_CODENAME
- DEFAULT_APP_TARGET_SDK
- BUILD_ID
- BUILD_NUMBER
- PLATFORM_SECURITY_PATCH
- PLATFORM_VNDK_VERSION
- PLATFORM_SYSTEMSDK_VERSIONS
- """
-
- # If build_id.rbc exists, it may override some of the defaults.
- # Note that build.prop target also wants INTERNAL_BUILD_ID_MAKEFILE to be set if the file exists.
- if _build_id_init != None:
- _build_id_init(g)
- g["INTERNAL_BUILD_ID_MAKEFILE"] = "build/make/core/build_id"
-
- allowed_versions = _allowed_versions(all_versions, v_min, v_max, v_default)
- g.setdefault("TARGET_PLATFORM_VERSION", v_default)
- if g["TARGET_PLATFORM_VERSION"] not in allowed_versions:
- fail("% is not valid, must be one of %s" % (g["TARGET_PLATFORM_VERSION"], allowed_versions))
-
- g["DEFAULT_PLATFORM_VERSION"] = v_default
- g["PLATFORM_VERSION_LAST_STABLE"] = 11
- g.setdefault("PLATFORM_VERSION_CODENAME", g["TARGET_PLATFORM_VERSION"])
- # TODO(asmundak): set PLATFORM_VERSION_ALL_CODENAMES
-
- g.setdefault("PLATFORM_SDK_VERSION", 30)
- version_codename = g["PLATFORM_VERSION_CODENAME"]
- if version_codename == "REL":
- g.setdefault("PLATFORM_VERSION", g["PLATFORM_VERSION_LAST_STABLE"])
- g["PLATFORM_PREVIEW_SDK_VERSION"] = 0
- g.setdefault("DEFAULT_APP_TARGET_SDK", g["PLATFORM_SDK_VERSION"])
- g.setdefault("PLATFORM_VNDK_VERSION", g["PLATFORM_SDK_VERSION"])
- else:
- g.setdefault("PLATFORM_VERSION", version_codename)
- g.setdefault("PLATFORM_PREVIEW_SDK_VERSION", 1)
- g.setdefault("DEFAULT_APP_TARGET_SDK", version_codename)
- g.setdefault("PLATFORM_VNDK_VERSION", version_codename)
-
- g.setdefault("PLATFORM_SYSTEMSDK_MIN_VERSION", 28)
- versions = [str(i) for i in range(g["PLATFORM_SYSTEMSDK_MIN_VERSION"], g["PLATFORM_SDK_VERSION"] + 1)]
- versions.append(version_codename)
- g["PLATFORM_SYSTEMSDK_VERSIONS"] = sorted(versions)
-
- # Used to indicate the security patch that has been applied to the device.
- # It must signify that the build includes all security patches issued up through the designated Android Public Security Bulletin.
- # It must be of the form "YYYY-MM-DD" on production devices.
- # It must match one of the Android Security Patch Level strings of the Public Security Bulletins.
- # If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
- g.setdefault("PLATFORM_SECURITY_PATCH", "2021-03-05")
- dt = 'TZ="GMT" %s' % g["PLATFORM_SECURITY_PATCH"]
- g.setdefault("PLATFORM_SECURITY_PATCH_TIMESTAMP", rblf_shell("date -d '%s' +%%s" % dt))
-
- # Used to indicate the base os applied to the device. Can be an arbitrary string, but must be a single word.
- # If there is no $PLATFORM_BASE_OS set, keep it empty.
- g.setdefault("PLATFORM_BASE_OS", "")
-
- # Used to signify special builds. E.g., branches and/or releases, like "M5-RC7". Can be an arbitrary string, but
- # must be a single word and a valid file name. If there is no BUILD_ID set, make it obvious.
- g.setdefault("BUILD_ID", "UNKNOWN")
-
- # BUILD_NUMBER should be set to the source control value that represents the current state of the source code.
- # E.g., a perforce changelist number or a git hash. Can be an arbitrary string (to allow for source control that
- # uses something other than numbers), but must be a single word and a valid file name.
- #
- # If no BUILD_NUMBER is set, create a useful "I am an engineering build from this date/time" value. Make it start
- # with a non-digit so that anyone trying to parse it as an integer will probably get "0".
- g.setdefault("BUILD_NUMBER", "eng.%s.%s" % (g["USER"], "TIMESTAMP"))
-
- # Used to set minimum supported target sdk version. Apps targeting SDK version lower than the set value will result
- # in a warning being shown when any activity from the app is started.
- g.setdefault("PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION", 23)
-
-def init(g):
- """Initializes globals.
-
- The code is the Starlark counterpart of the contents of the
- envsetup.mk file.
- Args:
- g: globals dictionary
- """
- all_versions = _all_versions()
- _versions_default(g, all_versions)
- for v in all_versions:
- g["IS_AT_LEAST" + v] = True
- if v == g["TARGET_PLATFORM_VERSION"]:
- break
-
- # ---------------------------------------------------------------
- # If you update the build system such that the environment setup or buildspec.mk need to be updated,
- # increment this number, and people who haven't re-run those will have to do so before they can build.
- # Make sure to also update the corresponding value in buildspec.mk.default and envsetup.sh.
- g["CORRECT_BUILD_ENV_SEQUENCE_NUMBER"] = 13
-
- g.setdefault("TARGET_PRODUCT", "aosp_arm")
- g.setdefault("TARGET_BUILD_VARIANT", "eng")
-
- g.setdefault("TARGET_BUILD_APPS", [])
- g["TARGET_BUILD_UNBUNDLED"] = (g["TARGET_BUILD_APPS"] != []) or (getattr(g, "TARGET_BUILD_UNBUNDLED_IMAGE", "") != "")
-
- # ---------------------------------------------------------------
- # Set up configuration for host machine. We don't do cross-compiles except for arm, so the HOST
- # is whatever we are running on.
- host = rblf_shell("uname -sm")
- if host.find("Linux") >= 0:
- g["HOST_OS"] = "linux"
- elif host.find("Darwin") >= 0:
- g["HOST_OS"] = "darwin"
- else:
- fail("Cannot run on %s OS" % host)
-
- # TODO(asmundak): set g.HOST_OS_EXTRA
-
- g["BUILD_OS"] = g["HOST_OS"]
-
- # TODO(asmundak): check cross-OS build
-
- if host.find("x86_64") >= 0:
- g["HOST_ARCH"] = "x86_64"
- g["HOST_2ND_ARCH"] = "x86"
- g["HOST_IS_64_BIT"] = True
- elif host.find("i686") >= 0 or host.find("x86") >= 0:
- fail("Building on a 32-bit x86 host is not supported: %s" % host)
- elif g["HOST_OS"] == "darwin":
- g["HOST_2ND_ARCH"] = ""
-
- g["HOST_2ND_ARCH_VAR_PREFIX"] = "2ND_"
- g["HOST_2ND_ARCH_MODULE_SUFFIX"] = "_32"
- g["HOST_CROSS_2ND_ARCH_VAR_PREFIX"] = "2ND_"
- g["HOST_CROSS_2ND_ARCH_MODULE_SUFFIX"] = "_64"
- g["TARGET_2ND_ARCH_VAR_PREFIX"] = "2ND_"
-
- # TODO(asmundak): envsetup.mk lines 216-226:
- # convert combo-related stuff from combo/select.mk
-
- # on windows, the tools have .exe at the end, and we depend on the
- # host config stuff being done first
- g["BUILD_ARCH"] = g["HOST_ARCH"]
- g["BUILD_2ND_ARCH"] = g["HOST_2ND_ARCH"]
-
- # the host build defaults to release, and it must be release or debug
- g.setdefault("HOST_BUILD_TYPE", "release")
- if g["HOST_BUILD_TYPE"] not in ["release", "debug"]:
- fail("HOST_BUILD_TYPE must be either release or debug, not '%s'" % g["HOST_BUILD_TYPE"])
-
- # TODO(asmundak): there is more stuff in envsetup.mk lines 249-292, but
- # it does not seem to affect product configuration. Revisit this.
-
- g["ART_APEX_JARS"] = [
- "com.android.art:core-oj",
- "com.android.art:core-libart",
- "com.android.art:okhttp",
- "com.android.art:bouncycastle",
- "com.android.art:apache-xml",
- ]
-
- if g.get("TARGET_BUILD_TYPE", "") != "debug":
- g["TARGET_BUILD_TYPE"] = "release"
-
-v_default = "SP1A"
-v_min = "SP1A"
-v_max = "SP1A"
diff --git a/core/java_common.mk b/core/java_common.mk
index 1798ca8..5981b60 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -21,15 +21,20 @@
# Modules can override this logic by specifying
# LOCAL_JAVA_LANGUAGE_VERSION explicitly.
ifeq (,$(LOCAL_JAVA_LANGUAGE_VERSION))
- ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_18_SUPPORT)))
- LOCAL_JAVA_LANGUAGE_VERSION := 1.7
- else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_19_SUPPORT)))
- LOCAL_JAVA_LANGUAGE_VERSION := 1.8
- else ifneq (,$(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS))
- # TODO(ccross): allow 1.9 for current and unbundled once we have SDK system modules
- LOCAL_JAVA_LANGUAGE_VERSION := 1.8
- else
+ ifdef LOCAL_IS_HOST_MODULE
+ # Host modules always default to 1.9
LOCAL_JAVA_LANGUAGE_VERSION := 1.9
+ else
+ ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_18_SUPPORT)))
+ LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+ else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_19_SUPPORT)))
+ LOCAL_JAVA_LANGUAGE_VERSION := 1.8
+ else ifneq (,$(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS))
+ # TODO(ccross): allow 1.9 for current and unbundled once we have SDK system modules
+ LOCAL_JAVA_LANGUAGE_VERSION := 1.8
+ else
+ LOCAL_JAVA_LANGUAGE_VERSION := 1.9
+ endif
endif
endif
LOCAL_JAVACFLAGS += -source $(LOCAL_JAVA_LANGUAGE_VERSION) -target $(LOCAL_JAVA_LANGUAGE_VERSION)
@@ -378,9 +383,8 @@
endif # USE_CORE_LIB_BOOTCLASSPATH
endif # !LOCAL_IS_HOST_MODULE
-ifdef RECORD_ALL_DEPS
+# (b/204397180) Record ALL_DEPS by default.
ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) $(full_java_bootclasspath_libs)
-endif
# Export the SDK libs. The sdk library names listed in LOCAL_SDK_LIBRARIES are first exported.
# Then sdk library names exported from dependencies are all re-exported.
diff --git a/core/java_renderscript.mk b/core/java_renderscript.mk
index 572d6e4..055ff14 100644
--- a/core/java_renderscript.mk
+++ b/core/java_renderscript.mk
@@ -107,7 +107,7 @@
# Prevent these from showing up on the device
# One exception is librsjni.so, which is needed for
# both native path and compat path.
-rs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni.so)/librsjni.so
+rs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni)/librsjni.so
LOCAL_JNI_SHARED_LIBRARIES += librsjni
ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(FORCE_BUILD_RS_COMPAT))
diff --git a/core/main.mk b/core/main.mk
index c10a3cc..f7cf8de 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -115,15 +115,6 @@
EMMA_INSTRUMENT := true
endif
-ifeq (true,$(EMMA_INSTRUMENT))
-# Adding the jacoco library can cause the inclusion of
-# some typically banned classes
-# So if the user didn't specify SKIP_BOOT_JARS_CHECK, enable it here
-ifndef SKIP_BOOT_JARS_CHECK
-SKIP_BOOT_JARS_CHECK := true
-endif
-endif
-
ifdef TARGET_ARCH_SUITE
# TODO(b/175577370): Enable this error.
# $(error TARGET_ARCH_SUITE is not supported in kati/make builds)
@@ -320,6 +311,13 @@
ro.vendor.build.dont_use_vabc=true
endif
+# Set the flag in vendor. So VTS would know if the new fingerprint format is in use when
+# the system images are replaced by GSI.
+ifeq ($(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT),true)
+ADDITIONAL_VENDOR_PROPERTIES += \
+ ro.vendor.build.fingerprint_has_digest=1
+endif
+
ADDITIONAL_VENDOR_PROPERTIES += \
ro.vendor.build.security_patch=$(VENDOR_SECURITY_PATCH) \
ro.product.board=$(TARGET_BOOTLOADER_BOARD_NAME) \
@@ -350,7 +348,7 @@
ADDITIONAL_PRODUCT_PROPERTIES += ro.build.characteristics=$(TARGET_AAPT_CHARACTERISTICS)
ifeq ($(AB_OTA_UPDATER),true)
-ADDITIONAL_PRODUCT_PROPERTIES += ro.product.ab_ota_partitions=$(subst $(space),$(comma),$(AB_OTA_PARTITIONS))
+ADDITIONAL_PRODUCT_PROPERTIES += ro.product.ab_ota_partitions=$(subst $(space),$(comma),$(strip $(AB_OTA_PARTITIONS)))
endif
# -----------------------------------------------------------------
@@ -361,7 +359,7 @@
is_sdk_build :=
-ifneq ($(filter sdk win_sdk sdk_addon,$(MAKECMDGOALS)),)
+ifneq ($(filter sdk sdk_addon,$(MAKECMDGOALS)),)
is_sdk_build := true
endif
@@ -536,13 +534,23 @@
# Include all of the makefiles in the system
#
-subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list) $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
+subdir_makefiles := $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT).mk $(SOONG_ANDROID_MK)
+# Android.mk files are only used on Linux builds, Mac only supports Android.bp
+ifeq ($(HOST_OS),linux)
+ subdir_makefiles += $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
+endif
+subdir_makefiles += $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
.KATI_READONLY := subdir_makefiles_total
$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
+# For an unbundled image, we can skip blueprint_tools because unbundled image
+# aims to remove a large number framework projects from the manifest, the
+# sources or dependencies for these tools may be missing from the tree.
+ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE))
droid_targets : blueprint_tools
+endif
endif # dont_bother
@@ -1229,14 +1237,43 @@
# Name resolution for LOCAL_REQUIRED_MODULES:
# See the select-bitness-of-required-modules definition.
# $(1): product makefile
+
+# TODO(asmundak):
+# `product-installed-files` and `host-installed-files` macros below used to
+# call `get-product-var` directly to obtain per-file configuration variable
+# values (the value of variable FOO is fetched from PRODUCT.<product-makefile>.FOO).
+# Starlark-based configuration does not maintain per-file variable variable
+# values. To work around this problem, we utilize the fact that
+# `product-installed-files` and `host-installed-files` are called only in
+# two places:
+# 1. For the top-level product makefile (in this file). In this case
+# $(call get-product-var <product>, FOO) is the same as $(FOO) as the
+# product configuration has been run already. Therefore we define
+# _product-var macro to pick the values directly from product config
+# variables when using Starlark-based configuration.
+# 2. To check the path requirements (in artifact_path_requirements.mk).
+# Starlark-based configuration does not perform this check at the moment.
+# In the longer run most of the logic of this file will be moved to the
+# Starlark.
+
+ifndef RBC_PRODUCT_CONFIG
+define _product-var
+ $(call get-product-var,$(1),$(2))
+endef
+else
+define _product-var
+ $(call $(2))
+endef
+endif
+
define product-installed-files
$(eval _pif_modules := \
- $(call get-product-var,$(1),PRODUCT_PACKAGES) \
- $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \
- $(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
- $(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
- $(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
- $(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
+ $(call _product-var,$(1),PRODUCT_PACKAGES) \
+ $(if $(filter eng,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_ENG)) \
+ $(if $(filter debug,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
+ $(if $(filter tests,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
+ $(if $(filter asan,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
+ $(if $(filter java_coverage,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
$(call auto-included-modules) \
) \
$(eval ### Filter out the overridden packages and executables before doing expansion) \
@@ -1247,13 +1284,13 @@
$(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \
$(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \
$(call resolve-product-relative-paths,\
- $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
+ $(foreach cf,$(call _product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
endef
# Similar to product-installed-files above, but handles PRODUCT_HOST_PACKAGES instead
# This does support the :32 / :64 syntax, but does not support module overrides.
define host-installed-files
- $(eval _hif_modules := $(call get-product-var,$(1),PRODUCT_HOST_PACKAGES)) \
+ $(eval _hif_modules := $(call _product-var,$(1),PRODUCT_HOST_PACKAGES)) \
$(eval ### Split host vs host cross modules) \
$(eval _hcif_modules := $(filter host_cross_%,$(_hif_modules))) \
$(eval _hif_modules := $(filter-out host_cross_%,$(_hif_modules))) \
@@ -1278,7 +1315,11 @@
)
endef
-ifdef FULL_BUILD
+ifeq ($(HOST_OS),darwin)
+ # Target builds are not supported on Mac
+ product_target_FILES :=
+ product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT))
+else ifdef FULL_BUILD
ifneq (true,$(ALLOW_MISSING_DEPENDENCIES))
# Check to ensure that all modules in PRODUCT_PACKAGES exist (opt in per product)
ifeq (true,$(PRODUCT_ENFORCE_PACKAGES_EXIST))
@@ -1338,7 +1379,7 @@
# Verify the artifact path requirements made by included products.
is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true)
- ifneq (true,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS)))
+ ifeq (,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS),$(RBC_PRODUCT_CONFIG),$(RBC_BOARD_CONFIG)))
include $(BUILD_SYSTEM)/artifact_path_requirements.mk
endif
else
@@ -1434,7 +1475,9 @@
# contains everything that's built during the current make, but it also further
# extends ALL_DEFAULT_INSTALLED_MODULES.
ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
-include $(BUILD_SYSTEM)/Makefile
+ifeq ($(HOST_OS),linux)
+ include $(BUILD_SYSTEM)/Makefile
+endif
modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
ALL_DEFAULT_INSTALLED_MODULES :=
@@ -1533,6 +1576,9 @@
.PHONY: vendorramdisk_debug
vendorramdisk_debug: $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET)
+.PHONY: vendorramdisk_test_harness
+vendorramdisk_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET)
+
.PHONY: productimage
productimage: $(INSTALLED_PRODUCTIMAGE_TARGET)
@@ -1557,6 +1603,10 @@
.PHONY: bootimage
bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
+ifeq (true,$(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST))
+$(call dist-for-goals, bootimage, $(INSTALLED_BOOTIMAGE_TARGET))
+endif
+
.PHONY: bootimage_debug
bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
@@ -1592,6 +1642,7 @@
$(INSTALLED_VENDORIMAGE_TARGET) \
$(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \
$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \
+ $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \
$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \
$(INSTALLED_VENDOR_RAMDISK_TARGET) \
$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \
@@ -1631,8 +1682,7 @@
$(INSTALLED_FILES_JSON_ROOT) \
$(INSTALLED_FILES_FILE_RECOVERY) \
$(INSTALLED_FILES_JSON_RECOVERY) \
- $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
- soong_docs
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET)
# The droidcore target depends on the droidcore-unbundled subset and any other
# targets for a non-unbundled (full source) full system build.
@@ -1648,7 +1698,11 @@
endif
.PHONY: apps_only
-ifneq ($(TARGET_BUILD_APPS),)
+ifeq ($(HOST_OS),darwin)
+ # Mac only supports building host modules
+ droid_targets: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) dist_files
+
+else ifneq ($(TARGET_BUILD_APPS),)
# If this build is just for apps, only build apps and not the full system by default.
unbundled_build_modules :=
@@ -1731,7 +1785,6 @@
$(call dist-for-goals, droidcore, \
$(BUILT_OTATOOLS_PACKAGE) \
$(APPCOMPAT_ZIP) \
- $(DEXPREOPT_CONFIG_ZIP) \
$(DEXPREOPT_TOOLS_ZIP) \
)
@@ -1779,6 +1832,7 @@
$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
$(INSTALLED_MISC_INFO_TARGET) \
$(INSTALLED_RAMDISK_TARGET) \
+ $(DEXPREOPT_CONFIG_ZIP) \
)
# Put a copy of the radio/bootloader files in the dist dir.
@@ -1812,6 +1866,7 @@
$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \
$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \
+ $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \
$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \
$(INSTALLED_VENDOR_RAMDISK_TARGET) \
$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \
@@ -1847,6 +1902,8 @@
ifdef CLANG_COVERAGE
$(foreach f,$(SOONG_NDK_API_XML), \
$(call dist-for-goals,droidcore,$(f):ndk_apis/$(notdir $(f))))
+ $(foreach f,$(SOONG_CC_API_XML), \
+ $(call dist-for-goals,droidcore,$(f):cc_apis/$(notdir $(f))))
endif
# For full system build (whether unbundled or not), we configure
@@ -1872,16 +1929,18 @@
.PHONY: docs
docs: $(ALL_DOCS)
-.PHONY: sdk win_sdk winsdk-tools sdk_addon
+.PHONY: sdk sdk_addon
+ifeq ($(HOST_OS),linux)
ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)
sdk: $(ALL_SDK_TARGETS)
-$(call dist-for-goals,sdk win_sdk, \
+$(call dist-for-goals,sdk, \
$(ALL_SDK_TARGETS) \
$(SYMBOLS_ZIP) \
$(COVERAGE_ZIP) \
$(APPCOMPAT_ZIP) \
$(INSTALLED_BUILD_PROP_TARGET) \
)
+endif
# umbrella targets to assit engineers in verifying builds
.PHONY: java native target host java-host java-target native-host native-target \
diff --git a/core/ninja_config.mk b/core/ninja_config.mk
index 2e1bd69..2157c9e 100644
--- a/core/ninja_config.mk
+++ b/core/ninja_config.mk
@@ -38,15 +38,19 @@
test-art% \
user \
userdataimage \
- userdebug \
- win_sdk \
- winsdk-tools
+ userdebug
include $(wildcard vendor/*/build/ninja_config.mk)
# Any Android goals that need to be built.
ANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS),\
$(sort $(ORIGINAL_MAKECMDGOALS) $(MAKECMDGOALS)))
+# Temporary compatibility support until the build server configs are updated
+ANDROID_GOALS := $(patsubst win_sdk,sdk,$(ANDROID_GOALS))
+ifneq ($(HOST_OS),linux)
+ ANDROID_GOALS := $(filter-out sdk,$(ANDROID_GOALS))
+ ANDROID_GOALS := $(patsubst sdk_repo,sdk-repo-build-tools sdk-repo-platform-tools,$(ANDROID_GOALS))
+endif
# Goals we need to pass to Ninja.
NINJA_GOALS := $(filter-out $(NINJA_EXCLUDE_GOALS), $(ANDROID_GOALS))
ifndef NINJA_GOALS
diff --git a/core/node_fns.mk b/core/node_fns.mk
index 8d20160..2243cd7 100644
--- a/core/node_fns.mk
+++ b/core/node_fns.mk
@@ -208,7 +208,7 @@
$(eval $(1).$(2).inherited := \
$(call get-inherited-nodes,$(1).$(2),$(3)))
- $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3))
+ $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3),$(4))
$(call _expand-inherited-values,$(1),$(2),$(3),$(4))
diff --git a/core/notice_files.mk b/core/notice_files.mk
index 9678380..4edbbb8 100644
--- a/core/notice_files.mk
+++ b/core/notice_files.mk
@@ -81,43 +81,78 @@
# Include shared libraries' notices for "container" types, but not for binaries etc.
notice_deps := \
$(strip \
- $(LOCAL_REQUIRED_MODULES) \
- $(LOCAL_STATIC_LIBRARIES) \
- $(LOCAL_WHOLE_STATIC_LIBRARIES) \
- $(LOCAL_SHARED_LIBRARIES) \
- $(LOCAL_DYLIB_LIBRARIES) \
- $(LOCAL_RLIB_LIBRARIES) \
- $(LOCAL_PROC_MACRO_LIBRARIES) \
- $(LOCAL_HEADER_LIBRARIES) \
- $(LOCAL_STATIC_JAVA_LIBRARIES) \
- $(LOCAL_JAVA_LIBRARIES) \
- $(LOCAL_JNI_SHARED_LIBRARIES) \
+ $(foreach d, \
+ $(LOCAL_REQUIRED_MODULES) \
+ $(LOCAL_STATIC_LIBRARIES) \
+ $(LOCAL_WHOLE_STATIC_LIBRARIES) \
+ $(LOCAL_SHARED_LIBRARIES) \
+ $(LOCAL_DYLIB_LIBRARIES) \
+ $(LOCAL_RLIB_LIBRARIES) \
+ $(LOCAL_PROC_MACRO_LIBRARIES) \
+ $(LOCAL_HEADER_LIBRARIES) \
+ $(LOCAL_STATIC_JAVA_LIBRARIES) \
+ $(LOCAL_JAVA_LIBRARIES) \
+ $(LOCAL_JNI_SHARED_LIBRARIES) \
+ ,$(subst :,_,$(d)):static \
+ ) \
)
else
notice_deps := \
$(strip \
- $(LOCAL_REQUIRED_MODULES) \
- $(LOCAL_STATIC_LIBRARIES) \
- $(LOCAL_WHOLE_STATIC_LIBRARIES) \
- $(LOCAL_RLIB_LIBRARIES) \
- $(LOCAL_PROC_MACRO_LIBRARIES) \
- $(LOCAL_HEADER_LIBRARIES) \
- $(LOCAL_STATIC_JAVA_LIBRARIES) \
+ $(foreach d, \
+ $(LOCAL_REQUIRED_MODULES) \
+ $(LOCAL_STATIC_LIBRARIES) \
+ $(LOCAL_WHOLE_STATIC_LIBRARIES) \
+ $(LOCAL_RLIB_LIBRARIES) \
+ $(LOCAL_PROC_MACRO_LIBRARIES) \
+ $(LOCAL_HEADER_LIBRARIES) \
+ $(LOCAL_STATIC_JAVA_LIBRARIES) \
+ ,$(subst :,_,$(d)):static \
+ )$(foreach d, \
+ $(LOCAL_SHARED_LIBRARIES) \
+ $(LOCAL_DYLIB_LIBRARIES) \
+ $(LOCAL_JAVA_LIBRARIES) \
+ $(LOCAL_JNI_SHARED_LIBRARIES) \
+ ,$(subst :,_,$(d)):dynamic \
+ ) \
)
endif
ifeq ($(LOCAL_IS_HOST_MODULE),true)
-notice_deps := $(strip $(notice_deps) $(LOCAL_HOST_REQUIRED_MODULES))
+notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_HOST_REQUIRED_MODULES),$(subst :,_,$(d)):static))
else
-notice_deps := $(strip $(notice_deps) $(LOCAL_TARGET_REQUIRED_MODULES))
+notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_TARGET_REQUIRED_MODULES),$(subst :,_,$(d)):static))
endif
+local_path := $(LOCAL_PATH)
+
+
+module_license_metadata :=
+
ifdef my_register_name
-ALL_MODULES.$(my_register_name).LICENSE_PACKAGE_NAME := $(strip $(license_package_name))
-ALL_MODULES.$(my_register_name).LICENSE_KINDS := $(ALL_MODULES.$(my_register_name).LICENSE_KINDS) $(license_kinds)
-ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS := $(ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS) $(license_conditions)
-ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP := $(ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP) $(install_map)
-ALL_MODULES.$(my_register_name).NOTICE_DEPS := $(ALL_MODULES.$(my_register_name).NOTICE_DEPS) $(notice_deps)
-ALL_MODULES.$(my_register_name).IS_CONTAINER := $(strip $(filter-out false,$(ALL_MODULES.$(my_register_name).IS_CONTAINER) $(is_container)))
+ module_license_metadata := $(call local-intermediates-dir)/$(my_register_name).meta_lic
+
+ $(foreach target,$(ALL_MODULES.$(my_register_name).BUILT) $(ALL_MODULES.$(my_register_name).INSTALLED),\
+ $(eval ALL_TARGETS.$(target).META_LIC := $(module_license_metadata)))
+
+ ALL_MODULES.$(my_register_name).META_LIC := $(strip $(ALL_MODULES.$(my_register_name).META_LIC) $(module_license_metadata))
+
+ ifdef LOCAL_SOONG_LICENSE_METADATA
+ # Soong modules have already produced a license metadata file, copy it to where Make expects it.
+ $(eval $(call copy-one-file, $(LOCAL_SOONG_LICENSE_METADATA), $(module_license_metadata)))
+ else
+ # Make modules don't have enough information to produce a license metadata rule until after fix-notice-deps
+ # has been called, store the necessary information until later.
+ ALL_MODULES.$(my_register_name).DELAYED_META_LIC := $(strip $(ALL_MODULES.$(my_register_name).DELAYED_META_LIC) $(module_license_metadata))
+ ALL_MODULES.$(my_register_name).LICENSE_PACKAGE_NAME := $(strip $(license_package_name))
+ ALL_MODULES.$(my_register_name).MODULE_TYPE := $(strip $(ALL_MODULES.$(my_register_name).MODULE_TYPE) $(LOCAL_MODULE_TYPE))
+ ALL_MODULES.$(my_register_name).MODULE_CLASS := $(strip $(ALL_MODULES.$(my_register_name).MODULE_CLASS) $(LOCAL_MODULE_CLASS))
+ ALL_MODULES.$(my_register_name).LICENSE_KINDS := $(ALL_MODULES.$(my_register_name).LICENSE_KINDS) $(license_kinds)
+ ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS := $(ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS) $(license_conditions)
+ ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP := $(ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP) $(install_map)
+ ALL_MODULES.$(my_register_name).NOTICE_DEPS := $(ALL_MODULES.$(my_register_name).NOTICE_DEPS) $(notice_deps)
+ ALL_MODULES.$(my_register_name).IS_CONTAINER := $(strip $(filter-out false,$(ALL_MODULES.$(my_register_name).IS_CONTAINER) $(is_container)))
+ ALL_MODULES.$(my_register_name).PATH := $(strip $(ALL_MODULES.$(my_register_name).PATH) $(local_path))
+ endif
endif
ifdef notice_file
@@ -179,15 +214,17 @@
installed_notice_file := $($(my_prefix)OUT_NOTICE_FILES)/src/$(module_installed_filename).txt
+$(installed_notice_file): $(module_license_metadata)
+
ifdef my_register_name
ALL_MODULES.$(my_register_name).INSTALLED_NOTICE_FILE := $(ALL_MODULES.$(my_register_name).INSTALLED_NOTICE_FILE) $(installed_notice_file)
ALL_MODULES.$(my_register_name).MODULE_INSTALLED_FILENAMES := $(ALL_MODULES.$(my_register_name).MODULE_INSTALLED_FILENAMES) $(module_installed_filename)
INSTALLED_NOTICE_FILES.$(installed_notice_file).MODULE := $(my_register_name)
else
$(installed_notice_file): PRIVATE_INSTALLED_MODULE := $(module_installed_filename)
-$(installed_notice_file) : PRIVATE_NOTICES := $(notice_file)
+$(installed_notice_file) : PRIVATE_NOTICES := $(sort $(foreach n,$(notice_file),$(if $(filter %:%,$(n)), $(call word-colon,1,$(n)), $(n))))
-$(installed_notice_file): $(notice_file)
+$(installed_notice_file): $(foreach n,$(notice_file),$(if $(filter %:%,$(n)), $(call word-colon,1,$(n)), $(n)))
@echo Notice file: $< -- $@
$(hide) mkdir -p $(dir $@)
$(hide) awk 'FNR==1 && NR > 1 {print "\n"} {print}' $(PRIVATE_NOTICES) > $@
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 9f5a599..800dbbc 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -35,6 +35,10 @@
endif
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
+ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)
+$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_STEM or LOCAL_BUILT_MODULE_STEM)
+endif
+
ifneq ($(strip $(LOCAL_MODULE)),)
$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE)
endif
diff --git a/core/product-graph.mk b/core/product-graph.mk
index 968d01b..f28ea3d 100644
--- a/core/product-graph.mk
+++ b/core/product-graph.mk
@@ -81,6 +81,7 @@
$(products_graph): PRIVATE_PRODUCTS_FILTER := $(products_list)
$(products_graph): $(this_makefile)
+ifeq (,$(RBC_PRODUCT_CONFIG)$(RBC_NO_PRODUCT_GRAPH)$(RBC_BOARD_CONFIG))
@echo Product graph DOT: $@ for $(PRIVATE_PRODUCTS_FILTER)
$(hide) echo 'digraph {' > $@.in
$(hide) echo 'graph [ ratio=.5 ];' >> $@.in
@@ -89,6 +90,10 @@
$(foreach p,$(PRIVATE_PRODUCTS),$(call emit-product-node-props,$(p),$@.in))
$(hide) echo '}' >> $@.in
$(hide) build/make/tools/filter-product-graph.py $(PRIVATE_PRODUCTS_FILTER) < $@.in > $@
+else
+ @echo RBC_PRODUCT_CONFIG and RBC_NO_PRODUCT_GRAPH should be unset to generate product graph
+ false
+endif
# Evaluates to the name of the product file
# $(1) product file
@@ -143,6 +148,7 @@
$(hide) cat $$< | build/make/tools/product_debug.py > $$@
endef
+ifeq (,$(RBC_PRODUCT_CONFIG)$(RBC_NO_PRODUCT_GRAPH)$(RBC_BOARD_CONFIG))
product_debug_files:=
$(foreach p,$(all_products), \
$(eval $(call transform-product-debug, $(p))) \
@@ -154,3 +160,8 @@
@echo Product graph .dot file: $(products_graph)
@echo Command to convert to pdf: dot -Tpdf -Nshape=box -o $(OUT_DIR)/products.pdf $(products_graph)
@echo Command to convert to svg: dot -Tsvg -Nshape=box -o $(OUT_DIR)/products.svg $(products_graph)
+else
+.PHONY: product-graph
+ @echo RBC_PRODUCT_CONFIG and RBC_NO_PRODUCT_GRAPH should be unset to generate product graph
+ false
+endif
diff --git a/core/product.mk b/core/product.mk
index 015fe44..31b1beb 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -213,12 +213,18 @@
# The list of product-specific kernel header dirs
_product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS
-# A list of module names of BOOTCLASSPATH (jar files)
+# A list of module names in BOOTCLASSPATH (jar files). Each module may be
+# prefixed with "<apex>:", which identifies the APEX that provides it. APEXes
+# are identified by their "variant" names, i.e. their `apex_name` values in
+# Soong, which default to the `name` values. The prefix can also be "platform:"
+# or "system_ext:", and defaults to "platform:" if left out. See the long
+# comment in build/soong/java/dexprepopt_bootjars.go for details.
_product_list_vars += PRODUCT_BOOT_JARS
-# A list of extra BOOTCLASSPATH jars (to be appended after common jars).
-# Products that include device-specific makefiles before AOSP makefiles should use this
-# instead of PRODUCT_BOOT_JARS, so that device-specific jars go after common jars.
+# A list of extra BOOTCLASSPATH jars (to be appended after common jars),
+# following the same format as PRODUCT_BOOT_JARS. Products that include
+# device-specific makefiles before AOSP makefiles should use this instead of
+# PRODUCT_BOOT_JARS, so that device-specific jars go after common jars.
_product_list_vars += PRODUCT_BOOT_JARS_EXTRA
_product_single_value_vars += PRODUCT_SUPPORTS_BOOT_SIGNER
@@ -226,11 +232,19 @@
_product_single_value_vars += PRODUCT_SUPPORTS_VERITY
_product_single_value_vars += PRODUCT_SUPPORTS_VERITY_FEC
_product_list_vars += PRODUCT_SYSTEM_SERVER_APPS
+# List of system_server classpath jars on the platform.
_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS
-# List of system_server jars delivered via apex. Format = <apex name>:<jar name>.
-_product_list_vars += PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS
+# List of system_server classpath jars delivered via apex. Format = <apex name>:<jar name>.
+_product_list_vars += PRODUCT_APEX_SYSTEM_SERVER_JARS
+# List of jars on the platform that system_server loads dynamically using separate classloaders.
+_product_list_vars += PRODUCT_STANDALONE_SYSTEM_SERVER_JARS
+# List of jars delivered via apex that system_server loads dynamically using separate classloaders.
+# Format = <apex name>:<jar name>
+_product_list_vars += PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS
# If true, then suboptimal order of system server jars does not cause an error.
_product_single_value_vars += PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS
+# If true, then system server jars defined in Android.mk are supported.
+_product_single_value_vars += PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS
# Additional system server jars to be appended at the end of the common list.
# This is necessary to avoid jars reordering due to makefile inheritance order.
@@ -267,9 +281,10 @@
_product_single_value_vars += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS
# Boot image options.
+_product_list_vars += PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION
_product_single_value_vars += \
+ PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST \
PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \
- PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION \
PRODUCT_USES_DEFAULT_ART_CONFIG \
_product_single_value_vars += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER
@@ -366,11 +381,6 @@
_product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES
_product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES
-# A list of <overridden-apex>:<override-apex> pairs that specifies APEX module
-# overrides to be applied to the APEX names in the boot jar variables
-# (PRODUCT_BOOT_JARS, PRODUCT_UPDATABLE_BOOT_JARS etc).
-_product_list_vars += PRODUCT_BOOT_JAR_MODULE_OVERRIDES
-
# Controls for whether different partitions are built for the current product.
_product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE
_product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
@@ -385,12 +395,16 @@
_product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE
_product_single_value_vars += PRODUCT_BUILD_RECOVERY_IMAGE
_product_single_value_vars += PRODUCT_BUILD_BOOT_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_DEBUG_BOOT_IMAGE
_product_single_value_vars += PRODUCT_BUILD_VENDOR_BOOT_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE
_product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE
_product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE
-# List of boot jars delivered via apex
-_product_list_vars += PRODUCT_UPDATABLE_BOOT_JARS
+# List of boot jars delivered via updatable APEXes, following the same format as
+# PRODUCT_BOOT_JARS.
+_product_list_vars += PRODUCT_APEX_BOOT_JARS
# If set, device uses virtual A/B.
_product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA
@@ -427,6 +441,21 @@
_product_single_value_vars += PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES
+# Install a copy of the debug policy to the system_ext partition, and allow
+# init-second-stage to load debug policy from system_ext.
+# This option is only meant to be set by GSI products.
+_product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
+
+# If set, metadata files for the following artifacts will be generated.
+# - system/framework/*.jar
+# - system/framework/oat/<arch>/*.{oat,vdex,art}
+# - system/etc/boot-image.prof
+# - system/etc/dirty-image-objects
+# One fsverity metadata container file per one input file will be generated in
+# system.img, with a suffix ".fsv_meta". e.g. a container file for
+# "/system/framework/foo.jar" will be "system/framework/foo.jar.fsv_meta".
+_product_single_value_vars += PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA
+
.KATI_READONLY := _product_single_value_vars _product_list_vars
_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
diff --git a/core/product_config.mk b/core/product_config.mk
index 5c85fb8..0e969fe 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -77,6 +77,46 @@
$(sort $(shell find $(2) -name "$(1)" -type f | $(SED_EXTENDED) "s:($(2)/?(.*)):\\1\\:$(3)/\\2:" | sed "s://:/:g"))
endef
+#
+# Convert file file to the PRODUCT_COPY_FILES/PRODUCT_SDK_ADDON_COPY_FILES
+# format: for each file F return $(F):$(PREFIX)/$(notdir $(F))
+# $(1): files list
+# $(2): prefix
+
+define copy-files
+$(foreach f,$(1),$(f):$(2)/$(notdir $(f)))
+endef
+
+#
+# Convert the list of file names to the list of PRODUCT_COPY_FILES items
+# $(1): from pattern
+# $(2): to pattern
+# $(3): file names
+# E.g., calling product-copy-files-by-pattern with
+# (from/%, to/%, a b)
+# returns
+# from/a:to/a from/b:to/b
+define product-copy-files-by-pattern
+$(join $(patsubst %,$(1),$(3)),$(patsubst %,:$(2),$(3)))
+endef
+
+# Return empty unless the board matches
+define is-board-platform2
+$(filter $(1), $(TARGET_BOARD_PLATFORM))
+endef
+
+# Return empty unless the board is in the list
+define is-board-platform-in-list2
+$(filter $(1),$(TARGET_BOARD_PLATFORM))
+endef
+
+# Return empty unless the board is QCOM
+define is-vendor-board-qcom
+$(if $(strip $(TARGET_BOARD_PLATFORM) $(QCOM_BOARD_PLATFORMS)),\
+ $(filter $(TARGET_BOARD_PLATFORM),$(QCOM_BOARD_PLATFORMS)),\
+ $(error both TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) and QCOM_BOARD_PLATFORMS=$(QCOM_BOARD_PLATFORMS)))
+endef
+
# ---------------------------------------------------------------
# Check for obsolete PRODUCT- and APP- goals
ifeq ($(CALLED_FROM_SETUP),true)
@@ -162,11 +202,30 @@
ifneq (1,$(words $(current_product_makefile)))
$(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))
endif
+
+ifndef RBC_PRODUCT_CONFIG
$(call import-products, $(current_product_makefile))
+else
+ $(shell mkdir -p $(OUT_DIR)/rbc)
+ $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_product_config.mk)
+
+ $(shell 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)
+ ifneq ($(.SHELLSTATUS),0)
+ $(error product configuration converter failed: $(.SHELLSTATUS))
+ endif
+ include $(OUT_DIR)/rbc/rbc_product_config_results.mk
+ PRODUCTS += $(current_product_makefile)
+endif
endif # Import all or just the current product makefile
+ifndef RBC_PRODUCT_CONFIG
# Quick check
$(check-all-products)
+endif
ifeq ($(SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK),)
# Import all the products that have made artifact path requirements, so that we can verify
@@ -186,6 +245,7 @@
$(dump-products)
endif
+ifndef RBC_PRODUCT_CONFIG
# Convert a short name like "sooner" into the path to the product
# file defining that product.
#
@@ -198,6 +258,9 @@
############################################################################
# Strip and assign the PRODUCT_ variables.
$(call strip-product-vars)
+else
+INTERNAL_PRODUCT := $(current_product_makefile)
+endif
current_product_makefile :=
all_product_makefiles :=
@@ -249,24 +312,30 @@
PRODUCT_BOOT_JARS := $(call qualify-platform-jars,$(PRODUCT_BOOT_JARS))
-# Replaces references to overridden boot jar modules in a boot jars variable.
-# $(1): Name of a boot jars variable with <apex>:<jar> pairs.
-define replace-boot-jar-module-overrides
- $(foreach pair,$(PRODUCT_BOOT_JAR_MODULE_OVERRIDES),\
- $(eval _rbjmo_from := $(call word-colon,1,$(pair)))\
- $(eval _rbjmo_to := $(call word-colon,2,$(pair)))\
- $(eval $(1) := $(patsubst $(_rbjmo_from):%,$(_rbjmo_to):%,$($(1)))))
-endef
-
-$(call replace-boot-jar-module-overrides,PRODUCT_BOOT_JARS)
-$(call replace-boot-jar-module-overrides,PRODUCT_UPDATABLE_BOOT_JARS)
-$(call replace-boot-jar-module-overrides,ART_APEX_JARS)
+# b/191127295: force core-icu4j onto boot image. It comes from a non-updatable APEX jar, but has
+# historically been part of the boot image; even though APEX jars are not meant to be part of the
+# boot image.
+# TODO(b/191686720): remove PRODUCT_APEX_BOOT_JARS to avoid a special handling of core-icu4j
+# in make rules.
+PRODUCT_APEX_BOOT_JARS := $(filter-out com.android.i18n:core-icu4j,$(PRODUCT_APEX_BOOT_JARS))
+# All APEX jars come after /system and /system_ext jars, so adding core-icu4j at the end of the list
+PRODUCT_BOOT_JARS += com.android.i18n:core-icu4j
# The extra system server jars must be appended at the end after common system server jars.
PRODUCT_SYSTEM_SERVER_JARS += $(PRODUCT_SYSTEM_SERVER_JARS_EXTRA)
PRODUCT_SYSTEM_SERVER_JARS := $(call qualify-platform-jars,$(PRODUCT_SYSTEM_SERVER_JARS))
+# Sort APEX boot and system server jars. We use deterministic alphabetical order
+# when constructing BOOTCLASSPATH and SYSTEMSERVERCLASSPATH definition on device
+# after an update. Enforce it in the build system as well to avoid recompiling
+# everything after an update due a change in the order.
+PRODUCT_APEX_BOOT_JARS := $(sort $(PRODUCT_APEX_BOOT_JARS))
+PRODUCT_APEX_SYSTEM_SERVER_JARS := $(sort $(PRODUCT_APEX_SYSTEM_SERVER_JARS))
+
+PRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \
+ $(call qualify-platform-jars,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS))
+
ifndef PRODUCT_SYSTEM_NAME
PRODUCT_SYSTEM_NAME := $(PRODUCT_NAME)
endif
@@ -303,10 +372,10 @@
endif
endif
-$(foreach pair,$(PRODUCT_UPDATABLE_BOOT_JARS), \
+$(foreach pair,$(PRODUCT_APEX_BOOT_JARS), \
$(eval jar := $(call word-colon,2,$(pair))) \
$(if $(findstring $(jar), $(PRODUCT_BOOT_JARS)), \
- $(error A jar in PRODUCT_UPDATABLE_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is)))
+ $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is)))
ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)
ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)
@@ -350,6 +419,12 @@
$(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS))
endif
+ifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
+ ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64,$(PRODUCT_NAME)))
+ $(error Only GSI products are allowed to set PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)
+ endif
+endif
+
ifndef PRODUCT_USE_DYNAMIC_PARTITIONS
PRODUCT_USE_DYNAMIC_PARTITIONS := $(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)
endif
@@ -475,6 +550,7 @@
# Copy and check the value of each PRODUCT_BUILD_*_IMAGE variable
$(foreach image, \
+ PVMFW \
SYSTEM \
SYSTEM_OTHER \
VENDOR \
diff --git a/core/product_config.rbc b/core/product_config.rbc
index 111e759..1ccffcc 100644
--- a/core/product_config.rbc
+++ b/core/product_config.rbc
@@ -12,24 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-load("//build/make/core:envsetup.rbc", _envsetup_init = "init")
-
"""Runtime functions."""
-def _global_init():
- """Returns dict created from the runtime environment."""
- globals = dict()
+_soong_config_namespaces_key = "$SOONG_CONFIG_NAMESPACES"
+_dist_for_goals_key = "$dist_for_goals"
+def _init_globals(input_variables_init):
+ """Initializes dictionaries of global variables.
- # Environment variables
- for k in dir(rblf_env):
- globals[k] = getattr(rblf_env, k)
+ This function runs the given input_variables_init function,
+ passing it a globals dictionary and a handle as if it
+ were a regular product. It then returns 2 copies of
+ the globals dictionary, so that one can be kept around
+ to diff changes made to the other later.
+ """
+ globals_base = {"PRODUCT_SOONG_NAMESPACES": []}
+ input_variables_init(globals_base, __h_new())
- # Variables set as var=value command line arguments
- for k in dir(rblf_cli):
- globals[k] = getattr(rblf_cli, k)
-
- globals.setdefault("PRODUCT_SOONG_NAMESPACES", [])
- _envsetup_init(globals)
+ # Rerun input_variables_init to produce a copy
+ # of globals_base, because starlark doesn't support
+ # deep copying objects.
+ globals = {"PRODUCT_SOONG_NAMESPACES": []}
+ input_variables_init(globals, __h_new())
# Variables that should be defined.
mandatory_vars = [
@@ -37,7 +40,6 @@
"PLATFORM_VERSION",
"PRODUCT_SOONG_NAMESPACES",
# TODO(asmundak): do we need TARGET_ARCH? AOSP does not reference it
- "TARGET_BUILD_TYPE",
"TARGET_BUILD_VARIANT",
"TARGET_PRODUCT",
]
@@ -45,12 +47,11 @@
if not bv in globals:
fail(bv, " is not defined")
- return globals
-
-_globals_base = _global_init()
+ return (globals, globals_base)
def __print_attr(attr, value):
- if not value:
+ # Allow using empty strings to clear variables, but not None values
+ if value == None:
return
if type(value) == "list":
if _options.rearrange:
@@ -62,26 +63,55 @@
elif _options.format == "pretty":
print(attr, "=", repr(value))
elif _options.format == "make":
- print(attr, ":=", value)
+ # Trim all spacing to a single space
+ print(attr, ":=", _mkstrip(value))
else:
fail("bad output format", _options.format)
-def _printvars(globals, cfg):
- """Prints known configuration variables."""
+def _printvars(state):
+ """Prints configuration and global variables."""
+ (globals, cfg, globals_base) = state
for attr, val in sorted(cfg.items()):
__print_attr(attr, val)
if _options.print_globals:
print()
- for attr, val in sorted(globals.items()):
- if attr not in _globals_base:
- __print_attr(attr, val)
+ _printglobals(globals, globals_base)
+
+def _printglobals(globals, globals_base):
+ for attr, val in sorted(globals.items()):
+ if attr == _soong_config_namespaces_key:
+ __print_attr("SOONG_CONFIG_NAMESPACES", val.keys())
+ for nsname, nsvars in sorted(val.items()):
+ # Define SOONG_CONFIG_<ns> for Make, othewise
+ # it cannot be added to .KATI_READONLY list
+ if _options.format == "make":
+ print("SOONG_CONFIG_" + nsname, ":=", " ".join(nsvars.keys()))
+ for var, val in sorted(nsvars.items()):
+ if val:
+ __print_attr("SOONG_CONFIG_%s_%s" % (nsname, var), val)
+ else:
+ print("SOONG_CONFIG_%s_%s :=" % (nsname, var))
+ elif attr == _dist_for_goals_key:
+ goals = []
+ src_dst_list = []
+ goal_dst_list = []
+ for goal_name, goal_src_dst_list in sorted(val.items()):
+ goals.append(goal_name)
+ for sd in sorted(goal_src_dst_list):
+ src_dst_list.append(":".join(sd))
+ goal_dst_list.append(":".join((goal_name, sd[1])))
+ print("_all_dist_goal_output_pairs:=", " ".join(goal_dst_list))
+ print("_all_dist_goals:=", " ".join(goals))
+ print("_all_dist_src_dst_pairs:=", " ".join(src_dst_list))
+ elif attr not in globals_base or globals_base[attr] != val:
+ __print_attr(attr, val)
def __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 _product_configuration(top_pcm_name, top_pcm):
+def _product_configuration(top_pcm_name, top_pcm, input_variables_init):
"""Creates configuration."""
# Product configuration is created by traversing product's inheritance
@@ -95,7 +125,7 @@
# PCM means "Product Configuration Module", i.e., a Starlark file
# whose body consists of a single init function.
- globals = dict(**_globals_base)
+ globals, globals_base = _init_globals(input_variables_init)
config_postfix = [] # Configs in postfix order
@@ -186,7 +216,27 @@
_percolate_inherited(configs, pcm_name, cfg, children_names)
configs[pcm_name] = pcm, cfg, children_names, True
- return globals, configs[top_pcm_name][1]
+ return (globals, configs[top_pcm_name][1], globals_base)
+
+
+def _dictionary_difference(a, b):
+ result = {}
+ for attr, val in a.items():
+ if attr not in b or b[attr] != val:
+ result[attr] = val
+ return result
+
+def _board_configuration(board_config_init, input_variables_init):
+ globals_base = {}
+ h_base = __h_new()
+ globals = {}
+ h = __h_new()
+
+ input_variables_init(globals_base, h_base)
+ input_variables_init(globals, h)
+ board_config_init(globals, h)
+ return (globals, _dictionary_difference(h[0], h_base[0]), globals_base)
+
def _substitute_inherited(configs, pcm_name, cfg):
"""Substitutes inherited values in all the attributes.
@@ -267,6 +317,42 @@
"""Returns configuration item for the inherited module."""
return (pcm_name,)
+def _soong_config_namespace(g, nsname):
+ """Adds given namespace if it does not exist."""
+
+ old = g.get(_soong_config_namespaces_key, {})
+ if old.get(nsname):
+ return
+
+ # A value cannot be updated, so we need to create a new dictionary
+ g[_soong_config_namespaces_key] = dict([(k,v) for k,v in old.items()] + [(nsname, {})])
+
+def _soong_config_set(g, nsname, var, value):
+ """Assigns the value to the variable in the namespace."""
+ _soong_config_namespace(g, nsname)
+ g[_soong_config_namespaces_key][nsname][var]=value
+
+def _soong_config_append(g, nsname, var, value):
+ """Appends to the value of the variable in the namespace."""
+ _soong_config_namespace(g, nsname)
+ ns = g[_soong_config_namespaces_key][nsname]
+ oldv = ns.get(var)
+ if oldv == None:
+ ns[var] = value
+ else:
+ ns[var] += " " + value
+
+
+def _soong_config_get(g, nsname, var):
+ """Gets to the value of the variable in the namespace."""
+ return g.get(_soong_config_namespaces_key, {}).get(nsname, {}).get(var, None)
+
+
+def _abspath(path):
+ """Provided for compatibility, to be removed later."""
+ return path
+
+
def _addprefix(prefix, string_or_list):
"""Adds prefix and returns a list.
@@ -297,7 +383,7 @@
def __words(string_or_list):
if type(string_or_list) == "list":
return string_or_list
- return string_or_list.split()
+ return _mkstrip(string_or_list).split()
# Handle manipulation functions.
# A handle passed to a PCM consists of:
@@ -347,6 +433,27 @@
if type(val) == "list":
val.append(_indirect(pcm_name))
+def __base(path):
+ """Returns basename."""
+ return path.rsplit("/",1)[-1]
+
+def _board_platform_in(g, string_or_list):
+ """Returns true if board is in the list."""
+ board = g.get("TARGET_BOARD_PLATFORM","")
+ if not board:
+ return False
+ return board in __words(string_or_list)
+
+
+def _board_platform_is(g, s):
+ """True if board is the same as argument."""
+ return g.get("TARGET_BOARD_PLATFORM","") == s
+
+
+def _copy_files(l, outdir):
+ """Generate <item>:<outdir>/item for each item."""
+ return ["%s:%s/%s" % (path, outdir, __base(path)) for path in __words(l)]
+
def _copy_if_exists(path_pair):
"""If from file exists, returns [from:to] pair."""
value = path_pair.split(":", 2)
@@ -366,7 +473,14 @@
def _find_and_copy(pattern, from_dir, to_dir):
"""Return a copy list for the files matching the pattern."""
- return ["%s/%s:%s/%s" % (from_dir, f, to_dir, f) for f in rblf_wildcard(pattern, from_dir)]
+ return sorted(["%s/%s:%s/%s" % (
+ from_dir, f, to_dir, f) for f in rblf_find_files(from_dir, pattern, only_files=1)])
+
+def _findstring(needle, haystack):
+ """Equivalent to GNU make's $(findstring)."""
+ if haystack.find(needle) < 0:
+ return ""
+ return needle
def _filter_out(pattern, text):
"""Return all the words from `text' that do not match any word in `pattern'.
@@ -399,11 +513,18 @@
res.append(w)
return res
+def _notdir(paths):
+ """Equivalent to the GNU make function $(notdir).
+
+ Returns the name of the file at the end of each path in paths.
+ """
+ return " ".join([__base(w) for w in __words(paths)])
+
def __mk2regex(words):
"""Returns regular expression equivalent to Make pattern."""
# TODO(asmundak): this will mishandle '\%'
- return "^(" + "|".join([w.replace("%", ".*", 1) for w in words]) + ")"
+ return "^(" + "|".join([w.replace("%", ".*", 1) for w in words if w]) + ")$"
def _regex_match(regex, w):
return rblf_regex(regex, w)
@@ -418,7 +539,25 @@
def _expand_wildcard(pattern):
"""Expands shell wildcard pattern."""
- return rblf_wildcard(pattern)
+ result = []
+ for word in __words(pattern):
+ result.extend(rblf_wildcard(word))
+ return result
+
+def _mkdist_for_goals(g, goal, src_dst_list):
+ """Implements dist-for-goals macro."""
+ goals_map = g.get(_dist_for_goals_key, {})
+ pairs = goals_map.get(goal)
+ if pairs == None:
+ pairs = []
+ g[_dist_for_goals_key] = dict([(k,v) for k,v in goals_map.items()] + [(goal, pairs)])
+ for src_dst in __words(src_dst_list):
+ pair=src_dst.split(":")
+ if len(pair) > 2:
+ fail(src_dst + " should be a :-separated pair")
+ pairs.append((pair[0],pair[1] if len(pair) == 2 and pair[1] else __base(pair[0])))
+ g[_dist_for_goals_key][goal] = pairs
+
def _mkerror(file, message = ""):
"""Prints error and stops."""
@@ -426,11 +565,121 @@
def _mkwarning(file, message = ""):
"""Prints warning."""
- print("%s: warning: %s" % (file, message))
+ 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)
+
def _mkinfo(file, message = ""):
"""Prints info."""
- print(message)
+ rblf_log(message)
+
+
+def __mkparse_pattern(pattern):
+ """Parses Make's patsubst pattern."""
+ in_escape = False
+ res = []
+ acc = ""
+ for c in pattern.elems():
+ if in_escape:
+ in_escape = False
+ acc += c
+ elif c == '\\':
+ in_escape = True
+ elif c == '%' and not res:
+ res.append(acc)
+ acc = ''
+ else:
+ acc += c
+ if in_escape:
+ acc += '\\'
+ res.append(acc)
+ return res
+
+
+def __mkpatsubst_word(parsed_pattern,parsed_subst, word):
+ (before, after) = parsed_pattern
+ if not word.startswith(before):
+ return word
+ if not word.endswith(after):
+ return word
+ if len(parsed_subst) < 2:
+ return parsed_subst[0]
+ return parsed_subst[0] + word[len(before):len(word) - len(after)] + parsed_subst[1]
+
+
+def _mkpatsubst(pattern, replacement, s):
+ """Emulates Make's patsubst.
+
+ Tokenizes `s` (unless it is already a list), and then performs a simple
+ wildcard substitution (in other words, `foo%bar` pattern is equivalent to
+ the regular expression `^foo(.*)bar$, and the first `%` in replacement is
+ $1 in regex terms).
+ """
+ parsed_pattern = __mkparse_pattern(pattern)
+ words = s if type(s) == "list" else _mkstrip(s).split(" ")
+ if len(parsed_pattern) == 1:
+ out_words = [ replacement if x == pattern else x for x in words]
+ else:
+ parsed_replacement = __mkparse_pattern(replacement)
+ out_words = [__mkpatsubst_word(parsed_pattern, parsed_replacement, x) for x in words]
+ return out_words if type(s) == "list" else " ".join(out_words)
+
+
+def _mkstrip(s):
+ """Emulates Make's strip.
+
+ That is, removes string's leading and trailing whitespace characters and
+ replaces any sequence of whitespace characters with with a single space.
+ """
+ if type(s) != "string":
+ return s
+ result = ""
+ was_space = False
+ for ch in s.strip().elems():
+ is_space = ch.isspace()
+ if not is_space:
+ if was_space:
+ result += " "
+ result += ch
+ was_space = is_space
+ return result
+
+def _mksubst(old, new, s):
+ """Emulates Make's subst.
+
+ Replaces each occurence of 'old' with 'new'.
+ If 's' is a list, applies substitution to each item.
+ """
+ if type(s) == "list":
+ return [e.replace(old, new) for e in s]
+ return s.replace(old, new)
+
+
+def _product_copy_files_by_pattern(src, dest, s):
+ """Creates a copy list.
+
+ For each item in a given list, create <from>:<to> pair, where <from> and
+ <to> are the results of applying Make-style patsubst of <src> and <dest>
+ respectively. E.g. the result of calling this function with
+ ("foo/%", "bar/%", ["a", "b"]) will be
+ ["foo/a:bar/a", "foo/b:bar/b"].
+ """
+ parsed_src = __mkparse_pattern(src)
+ parsed_dest = __mkparse_pattern(dest)
+ parsed_percent = ["", ""]
+ words = s if type(s) == "list" else _mkstrip(s).split(" ")
+ return [ __mkpatsubst_word(parsed_percent, parsed_src, x) + ":" + __mkpatsubst_word(parsed_percent, parsed_dest, x) for x in words]
+
def __get_options():
"""Returns struct containing runtime global settings."""
@@ -440,6 +689,7 @@
rearrange = "",
trace_modules = False,
trace_variables = [],
+ mk2rbc_continue = False,
)
for x in getattr(rblf_cli, "RBC_OUT", "").split(","):
if x == "sort" or x == "unique":
@@ -457,13 +707,23 @@
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()
rblf = struct(
+ soong_config_namespace = _soong_config_namespace,
+ soong_config_append = _soong_config_append,
+ soong_config_set = _soong_config_set,
+ soong_config_get = _soong_config_get,
+ abspath = _abspath,
addprefix = _addprefix,
addsuffix = _addsuffix,
+ board_platform_in = _board_platform_in,
+ board_platform_is = _board_platform_is,
+ copy_files = _copy_files,
copy_if_exists = _copy_if_exists,
cfg = __h_cfg,
enforce_product_packages_exist = _enforce_product_packages_exist,
@@ -473,17 +733,27 @@
filter = _filter,
filter_out = _filter_out,
find_and_copy = _find_and_copy,
- global_init = _global_init,
+ findstring = _findstring,
inherit = _inherit,
indirect = _indirect,
+ mk2rbc_error = _mk2rbc_error,
+ mkdist_for_goals = _mkdist_for_goals,
mkinfo = _mkinfo,
mkerror = _mkerror,
+ mkpatsubst = _mkpatsubst,
mkwarning = _mkwarning,
+ mkstrip = _mkstrip,
+ mksubst = _mksubst,
+ notdir = _notdir,
printvars = _printvars,
+ printglobals = _printglobals,
product_configuration = _product_configuration,
+ board_configuration = _board_configuration,
+ product_copy_files_by_pattern = _product_copy_files_by_pattern,
require_artifacts_in_path = _require_artifacts_in_path,
require_artifacts_in_path_relaxed = _require_artifacts_in_path_relaxed,
setdefault = _setdefault,
shell = rblf_shell,
warning = _mkwarning,
+ words = __words,
)
diff --git a/core/rbe.mk b/core/rbe.mk
index 19c0e42..370d4bd 100644
--- a/core/rbe.mk
+++ b/core/rbe.mk
@@ -22,6 +22,18 @@
rbe_dir := prebuilts/remoteexecution-client/live/
endif
+ ifdef RBE_CXX_POOL
+ cxx_pool := $(RBE_CXX_POOL)
+ else
+ cxx_pool := default
+ endif
+
+ ifdef RBE_JAVA_POOL
+ java_pool := $(RBE_JAVA_POOL)
+ else
+ java_pool := java16
+ endif
+
ifdef RBE_CXX_EXEC_STRATEGY
cxx_rbe_exec_strategy := $(RBE_CXX_EXEC_STRATEGY)
else
@@ -59,8 +71,8 @@
endif
platform := container-image=docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62
- cxx_platform := $(platform),Pool=default
- java_r8_d8_platform := $(platform),Pool=java16
+ cxx_platform := $(platform),Pool=$(cxx_pool)
+ java_r8_d8_platform := $(platform),Pool=$(java_pool)
RBE_WRAPPER := $(rbe_dir)/rewrapper
RBE_CXX := --labels=type=compile,lang=cpp,compiler=clang --env_var_allowlist=PWD --exec_strategy=$(cxx_rbe_exec_strategy) --platform=$(cxx_platform) --compare=$(cxx_compare)
diff --git a/core/rust_device_test_config_template.xml b/core/rust_device_test_config_template.xml
index 9429d38..bfd2f47 100644
--- a/core/rust_device_test_config_template.xml
+++ b/core/rust_device_test_config_template.xml
@@ -15,6 +15,9 @@
-->
<!-- This test config file is auto-generated. -->
<configuration description="Config to run {MODULE} device tests.">
+
+ {EXTRA_CONFIGS}
+
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
diff --git a/core/soong_android_app_set.mk b/core/soong_android_app_set.mk
index ef9eace..ec3d8c8 100644
--- a/core/soong_android_app_set.mk
+++ b/core/soong_android_app_set.mk
@@ -6,29 +6,19 @@
$(call pretty-error,soong_apk_set.mk may only be used from Soong)
endif
-LOCAL_BUILT_MODULE_STEM := $(LOCAL_APK_SET_INSTALL_FILE)
-LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_APK_SET_INSTALL_FILE)
+LOCAL_BUILT_MODULE_STEM := package.apk
+LOCAL_INSTALLED_MODULE_STEM := $(notdir $(LOCAL_PREBUILT_MODULE_FILE))
+
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
#######################################
include $(BUILD_SYSTEM)/base_rules.mk
#######################################
-## Extract master APK from APK set into given directory
-# $(1) APK set
-# $(2) APK entry to install (e.g., splits/base.apk
+$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
-define extract-install-file-from-apk-set
-$(LOCAL_BUILT_MODULE): $(1)
- @echo "Extracting $$@"
- unzip -pq $$< $(2) >$$@
-endef
-
-$(eval $(call extract-install-file-from-apk-set,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_APK_SET_INSTALL_FILE)))
-# unzip returns 11 it there was nothing to extract, which is expected,
-# $(LOCAL_APK_SET_INSTALL_FILE) has is already there.
-LOCAL_POST_INSTALL_CMD := unzip -qoDD -j -d $(dir $(LOCAL_INSTALLED_MODULE)) \
- $(LOCAL_PREBUILT_MODULE_FILE) -x $(LOCAL_APK_SET_INSTALL_FILE) || [[ $$? -eq 11 ]]
-$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))
PACKAGES := $(PACKAGES) $(LOCAL_MODULE)
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index 82fb413..006e1dc 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -28,6 +28,17 @@
full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar
full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar
+
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_CLASSES_JAR)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_HEADER_JAR)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_FULL_MANIFEST_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEXPREOPT_CONFIG)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEX_JAR)
+
#######################################
include $(BUILD_SYSTEM)/base_rules.mk
#######################################
@@ -127,13 +138,6 @@
java-dex: $(LOCAL_SOONG_DEX_JAR)
-my_built_installed := $(foreach f,$(LOCAL_SOONG_BUILT_INSTALLED),\
- $(call word-colon,1,$(f)):$(PRODUCT_OUT)$(call word-colon,2,$(f)))
-my_installed := $(call copy-many-files, $(my_built_installed))
-ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed)
-ALL_MODULES.$(my_register_name).BUILT_INSTALLED += $(my_built_installed)
-$(my_all_targets): $(my_installed)
-
# Copy test suite files.
ifdef LOCAL_COMPATIBILITY_SUITE
my_apks_to_install := $(foreach f,$(filter %.apk %.idsig,$(LOCAL_SOONG_BUILT_INSTALLED)),$(call word-colon,1,$(f)))
diff --git a/core/soong_cc_prebuilt.mk b/core/soong_cc_rust_prebuilt.mk
similarity index 87%
rename from core/soong_cc_prebuilt.mk
rename to core/soong_cc_rust_prebuilt.mk
index 4d7b614..ca52374 100644
--- a/core/soong_cc_prebuilt.mk
+++ b/core/soong_cc_rust_prebuilt.mk
@@ -6,7 +6,7 @@
# LOCAL_SOONG_VNDK_VERSION : means the version of VNDK where this module belongs
ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
- $(call pretty-error,soong_cc_prebuilt.mk may only be used from Soong)
+ $(call pretty-error,soong_cc_rust_prebuilt.mk may only be used from Soong)
endif
ifdef LOCAL_IS_HOST_MODULE
@@ -31,9 +31,9 @@
$(call pretty-error,Unsupported LOCAL_MODULE_$(my_prefix)ARCH=$(LOCAL_MODULE_$(my_prefix)ARCH))
endif
-# Don't install static libraries by default.
+# Don't install static/rlib/proc_macro libraries.
ifndef LOCAL_UNINSTALLABLE_MODULE
- ifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS))
+ ifneq ($(filter STATIC_LIBRARIES RLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
LOCAL_UNINSTALLABLE_MODULE := true
endif
endif
@@ -45,11 +45,16 @@
endif
endif
+
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+
#######################################
include $(BUILD_SYSTEM)/base_rules.mk
#######################################
-ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
+ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
# Soong module is a static or shared library
EXPORTS_LIST += $(intermediates)
EXPORTS.$(intermediates).FLAGS := $(LOCAL_EXPORT_CFLAGS)
@@ -108,6 +113,16 @@
$(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \
$(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries))
endif
+ ifdef LOCAL_DYLIB_LIBRARIES
+ my_dylibs := $(LOCAL_DYLIB_LIBRARIES)
+ # Treat these as shared library dependencies for installation purposes.
+ ifdef LOCAL_USE_VNDK
+ my_dylibs := $(foreach l,$(my_dylibs),\
+ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))
+ endif
+ $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \
+ $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_dylibs))
+ endif
endif
my_check_same_vndk_variants :=
@@ -228,9 +243,9 @@
$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-# We don't care about installed static libraries, since the libraries have
+# We don't care about installed rlib/static libraries, since the libraries have
# already been linked into the module at that point. We do, however, care
-# about the NOTICE files for any static libraries that we use.
+# about the NOTICE files for any rlib/static libraries that we use.
# (see notice_files.mk)
#
# Filter out some NDK libraries that are not being exported.
@@ -242,6 +257,9 @@
installed_static_library_notice_file_targets := \
$(foreach lib,$(my_static_libraries) $(LOCAL_WHOLE_STATIC_LIBRARIES), \
NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-STATIC_LIBRARIES-$(lib))
+installed_static_library_notice_file_targets += \
+ $(foreach lib,$(LOCAL_RLIB_LIBRARIES), \
+ NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-RLIB_LIBRARIES-$(lib))
$(notice_target): | $(installed_static_library_notice_file_targets)
$(LOCAL_INSTALLED_MODULE): | $(notice_target)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index ec67560..617abdf 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -27,6 +27,7 @@
$(call add_json_val, Platform_sdk_version, $(PLATFORM_SDK_VERSION))
$(call add_json_str, Platform_sdk_codename, $(PLATFORM_VERSION_CODENAME))
$(call add_json_bool, Platform_sdk_final, $(filter REL,$(PLATFORM_VERSION_CODENAME)))
+$(call add_json_val, Platform_sdk_extension_version, $(PLATFORM_SDK_EXTENSION_VERSION))
$(call add_json_csv, Platform_version_active_codenames, $(PLATFORM_VERSION_ALL_CODENAMES))
$(call add_json_str, Platform_security_patch, $(PLATFORM_SECURITY_PATCH))
$(call add_json_str, Platform_preview_sdk_version, $(PLATFORM_PREVIEW_SDK_VERSION))
@@ -36,9 +37,9 @@
$(call add_json_bool, Allow_missing_dependencies, $(filter true,$(ALLOW_MISSING_DEPENDENCIES)))
$(call add_json_bool, Unbundled_build, $(TARGET_BUILD_UNBUNDLED))
-$(call add_json_bool, Unbundled_build_apps, $(TARGET_BUILD_APPS))
+$(call add_json_list, Unbundled_build_apps, $(TARGET_BUILD_APPS))
+$(call add_json_bool, Unbundled_build_image, $(TARGET_BUILD_UNBUNDLED_IMAGE))
$(call add_json_bool, Always_use_prebuilt_sdks, $(TARGET_BUILD_USE_PREBUILT_SDKS))
-$(call add_json_bool, Skip_boot_jars_check, $(SKIP_BOOT_JARS_CHECK))
$(call add_json_bool, Debuggable, $(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
$(call add_json_bool, Eng, $(filter eng,$(TARGET_BUILD_VARIANT)))
@@ -72,6 +73,7 @@
$(call add_json_str, HostArch, $(HOST_ARCH))
$(call add_json_str, HostSecondaryArch, $(HOST_2ND_ARCH))
$(call add_json_bool, HostStaticBinaries, $(BUILD_HOST_static))
+$(call add_json_bool, HostMusl, $(USE_HOST_MUSL))
$(call add_json_str, CrossHost, $(HOST_CROSS_OS))
$(call add_json_str, CrossHostArch, $(HOST_CROSS_ARCH))
@@ -141,7 +143,7 @@
$(call add_json_list, ModulesLoadedByPrivilegedModules, $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))
$(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS))
-$(call add_json_list, UpdatableBootJars, $(PRODUCT_UPDATABLE_BOOT_JARS))
+$(call add_json_list, ApexBootJars, $(PRODUCT_APEX_BOOT_JARS))
$(call add_json_bool, VndkUseCoreVariant, $(TARGET_VNDK_USE_CORE_VARIANT))
$(call add_json_bool, VndkSnapshotBuildArtifacts, $(VNDK_SNAPSHOT_BUILD_ARTIFACTS))
@@ -162,6 +164,7 @@
$(call add_json_list, VendorSnapshotDirsExcluded, $(VENDOR_SNAPSHOT_DIRS_EXCLUDED))
$(call add_json_list, RecoverySnapshotDirsIncluded, $(RECOVERY_SNAPSHOT_DIRS_INCLUDED))
$(call add_json_list, RecoverySnapshotDirsExcluded, $(RECOVERY_SNAPSHOT_DIRS_EXCLUDED))
+$(call add_json_bool, HostFakeSnapshotEnabled, $(HOST_FAKE_SNAPSHOT_ENABLE))
$(call add_json_bool, Treble_linker_namespaces, $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))
$(call add_json_bool, Enforce_vintf_manifest, $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST)))
@@ -186,6 +189,7 @@
$(call add_json_list, PgoAdditionalProfileDirs, $(PGO_ADDITIONAL_PROFILE_DIRS))
+$(call add_json_list, BoardPlatVendorPolicy, $(BOARD_PLAT_VENDOR_POLICY))
$(call add_json_list, BoardReqdMaskPolicy, $(BOARD_REQD_MASK_POLICY))
$(call add_json_list, BoardVendorSepolicyDirs, $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS))
$(call add_json_list, BoardOdmSepolicyDirs, $(BOARD_ODM_SEPOLICY_DIRS))
@@ -198,6 +202,7 @@
$(call add_json_str, BoardSepolicyVers, $(BOARD_SEPOLICY_VERS))
$(call add_json_str, PlatformSepolicyVersion, $(PLATFORM_SEPOLICY_VERSION))
+$(call add_json_str, TotSepolicyVersion, $(TOT_SEPOLICY_VERSION))
$(call add_json_bool, Flatten_apex, $(filter true,$(TARGET_FLATTEN_APEX)))
$(call add_json_bool, ForceApexSymlinkOptimization, $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION)))
@@ -264,6 +269,11 @@
$(call add_json_bool, SepolicySplit, $(filter true,$(PRODUCT_SEPOLICY_SPLIT)))
+$(call add_json_list, SepolicyFreezeTestExtraDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_DIRS))
+$(call add_json_list, SepolicyFreezeTestExtraPrebuiltDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS))
+
+$(call add_json_bool, GenerateAidlNdkPlatformBackend, $(filter true,$(NEED_AIDL_NDK_PLATFORM_BACKEND)))
+
$(call json_end)
$(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index 1ebbf14..b819cdc 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -25,6 +25,15 @@
LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_AAR)
endif
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_HEADER_JAR)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_FULL_MANIFEST_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEXPREOPT_CONFIG)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEX_JAR)
+
#######################################
include $(BUILD_SYSTEM)/base_rules.mk
#######################################
@@ -145,13 +154,7 @@
endif
endif # LOCAL_SOONG_DEX_JAR
-my_built_installed := $(foreach f,$(LOCAL_SOONG_BUILT_INSTALLED),\
- $(call word-colon,1,$(f)):$(PRODUCT_OUT)$(call word-colon,2,$(f)))
-my_installed := $(call copy-many-files, $(my_built_installed))
-ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed)
-ALL_MODULES.$(my_register_name).BUILT_INSTALLED += $(my_built_installed)
ALL_MODULES.$(my_register_name).CLASSES_JAR := $(full_classes_jar)
-$(my_register_name): $(my_installed)
ifdef LOCAL_SOONG_AAR
ALL_MODULES.$(my_register_name).AAR := $(LOCAL_SOONG_AAR)
diff --git a/core/soong_rust_prebuilt.mk b/core/soong_rust_prebuilt.mk
deleted file mode 100644
index 26c099b..0000000
--- a/core/soong_rust_prebuilt.mk
+++ /dev/null
@@ -1,184 +0,0 @@
-# Native prebuilt coming from Soong.
-# Extra inputs:
-# LOCAL_SOONG_UNSTRIPPED_BINARY
-
-ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
- $(call pretty-error,soong_rust_prebuilt.mk may only be used from Soong)
-endif
-
-ifdef LOCAL_IS_HOST_MODULE
- ifneq ($(HOST_OS),$(LOCAL_MODULE_HOST_OS))
- my_prefix := HOST_CROSS_
- LOCAL_HOST_PREFIX := $(my_prefix)
- else
- my_prefix := HOST_
- LOCAL_HOST_PREFIX :=
- endif
-else
- my_prefix := TARGET_
-endif
-
-ifeq ($($(my_prefix)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH))
- # primary arch
- LOCAL_2ND_ARCH_VAR_PREFIX :=
-else ifeq ($($(my_prefix)2ND_ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH))
- # secondary arch
- LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX)
-else
- $(call pretty-error,Unsupported LOCAL_MODULE_$(my_prefix)ARCH=$(LOCAL_MODULE_$(my_prefix)ARCH))
-endif
-
-# Don't install static/rlib/proc_macro libraries.
-ifndef LOCAL_UNINSTALLABLE_MODULE
- ifneq ($(filter STATIC_LIBRARIES RLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
- LOCAL_UNINSTALLABLE_MODULE := true
- endif
-endif
-
-
-#######################################
-include $(BUILD_SYSTEM)/base_rules.mk
-#######################################
-
-ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
- # Soong module is a static or shared library
- EXPORTS_LIST += $(intermediates)
- EXPORTS.$(intermediates).FLAGS := $(LOCAL_EXPORT_CFLAGS)
- EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS)
-
- SOONG_ALREADY_CONV += $(LOCAL_MODULE)
-
- my_link_type := $(LOCAL_SOONG_LINK_TYPE)
- my_warn_types :=
- my_allowed_types :=
- my_link_deps :=
- my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)
- my_common :=
- include $(BUILD_SYSTEM)/link_type.mk
-endif
-
-
-ifdef LOCAL_USE_VNDK
- ifneq ($(LOCAL_VNDK_DEPEND_ON_CORE_VARIANT),true)
- name_without_suffix := $(patsubst %.vendor,%,$(LOCAL_MODULE))
- ifneq ($(name_without_suffix),$(LOCAL_MODULE))
- SPLIT_VENDOR.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1
- else
- name_without_suffix := $(patsubst %.product,%,$(LOCAL_MODULE))
- ifneq ($(name_without_suffix),$(LOCAL_MODULE))
- SPLIT_PRODUCT.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1
- endif
- endif
- name_without_suffix :=
- endif
-endif
-
-# The real dependency will be added after all Android.mks are loaded and the install paths
-# of the shared libraries are determined.
-ifdef LOCAL_INSTALLED_MODULE
- ifdef LOCAL_SHARED_LIBRARIES
- my_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
- ifdef LOCAL_USE_VNDK
- my_shared_libraries := $(foreach l,$(my_shared_libraries),\
- $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))
- endif
- $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \
- $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries))
- endif
- ifdef LOCAL_DYLIB_LIBRARIES
- my_dylibs := $(LOCAL_DYLIB_LIBRARIES)
- # Treat these as shared library dependencies for installation purposes.
- ifdef LOCAL_USE_VNDK
- my_dylibs := $(foreach l,$(my_dylibs),\
- $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))
- endif
- $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \
- $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_dylibs))
- endif
-endif
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE)
-ifeq ($(LOCAL_IS_HOST_MODULE) $(if $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),true,),true true)
- $(copy-or-link-prebuilt-to-target)
- ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)
- [ -x $@ ] || ( $(call echo-error,$@,Target of symlink is not executable); false )
- endif
-else
- $(transform-prebuilt-to-target)
- ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)
- $(hide) chmod +x $@
- endif
-endif
-
-ifndef LOCAL_IS_HOST_MODULE
- ifdef LOCAL_SOONG_UNSTRIPPED_BINARY
- my_symbol_path := $(if $(LOCAL_SOONG_SYMBOL_PATH),$(LOCAL_SOONG_SYMBOL_PATH),$(my_module_path))
- # Store a copy with symbols for symbolic debugging
- my_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path))
- # drop /root as /root is mounted as /
- my_unstripped_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/root/%,$(TARGET_OUT_UNSTRIPPED)/%, $(my_unstripped_path))
- symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem)
- $(eval $(call copy-one-file,$(LOCAL_SOONG_UNSTRIPPED_BINARY),$(symbolic_output)))
- $(LOCAL_BUILT_MODULE): | $(symbolic_output)
- endif
-endif
-
-create_coverage_zip :=
-
-ifeq ($(NATIVE_COVERAGE),true)
- create_coverage_zip := true
-endif
-
-# Until Rust supports LLVM coverage, Soong assumes GCOV coverage in both cases.
-# Therefore we should create the coverage zip with the gcno files in this case as well.
-ifeq ($(CLANG_COVERAGE),true)
- create_coverage_zip := true
-endif
-
-ifdef create_coverage_zip
- ifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE)))
- $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).zip))
- ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true)
- ifdef LOCAL_IS_HOST_MODULE
- my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path))
- else
- my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))
- endif
- my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).zip
- $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path)))
- $(LOCAL_BUILT_MODULE): $(my_coverage_path)
- endif
- endif
-endif
-
-# A product may be configured to strip everything in some build variants.
-# We do the stripping as a post-install command so that LOCAL_BUILT_MODULE
-# is still with the symbols and we don't need to clean it (and relink) when
-# you switch build variant.
-ifneq ($(filter $(STRIP_EVERYTHING_BUILD_VARIANTS),$(TARGET_BUILD_VARIANT)),)
-$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := \
- $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_STRIP) --strip-all $(LOCAL_INSTALLED_MODULE)
-endif
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-
-# We don't care about installed rlib/static libraries, since the libraries have
-# already been linked into the module at that point. We do, however, care
-# about the NOTICE files for any rlib/static libraries that we use.
-# (see notice_files.mk)
-#
-# Filter out some NDK libraries that are not being exported.
-my_static_libraries := \
- $(filter-out ndk_libc++_static ndk_libc++abi ndk_libandroid_support ndk_libunwind \
- ndk_libc++_static.native_bridge ndk_libc++abi.native_bridge \
- ndk_libandroid_support.native_bridge ndk_libunwind.native_bridge, \
- $(LOCAL_STATIC_LIBRARIES))
-installed_static_library_notice_file_targets := \
- $(foreach lib,$(my_static_libraries), \
- NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-STATIC_LIBRARIES-$(lib))
-installed_static_library_notice_file_targets += \
- $(foreach lib,$(LOCAL_RLIB_LIBRARIES), \
- NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-RLIB_LIBRARIES-$(lib))
-
-$(notice_target): | $(installed_static_library_notice_file_targets)
-$(LOCAL_INSTALLED_MODULE): | $(notice_target)
diff --git a/core/sysprop.mk b/core/sysprop.mk
index ec181f5..1d38f8c 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -98,7 +98,7 @@
$(eval _option := --allow-dup)\
)
-$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) $(3) $(6)
+$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6)
$(hide) echo Building $$@
$(hide) mkdir -p $$(dir $$@)
$(hide) rm -f $$@ && touch $$@
diff --git a/core/tasks/OWNERS b/core/tasks/OWNERS
new file mode 100644
index 0000000..594930d
--- /dev/null
+++ b/core/tasks/OWNERS
@@ -0,0 +1 @@
+per-file art-host-tests.mk = dshi@google.com,dsrbecky@google.com,jdesprez@google.com,rpl@google.com
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index fdd9591..876d77a 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -20,8 +20,207 @@
include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
.PHONY: cts
-cts: $(compatibility_zip)
-$(call dist-for-goals, cts, $(compatibility_zip))
+cts: $(compatibility_zip) $(compatibility_tests_list_zip)
+$(call dist-for-goals, cts, $(compatibility_zip) $(compatibility_tests_list_zip))
.PHONY: cts_v2
cts_v2: cts
+
+# platform version check (b/32056228)
+# ============================================================
+ifneq (,$(wildcard cts/))
+ cts_platform_version_path := cts/tests/tests/os/assets/platform_versions.txt
+ cts_platform_version_string := $(shell cat $(cts_platform_version_path))
+ cts_platform_release_path := cts/tests/tests/os/assets/platform_releases.txt
+ cts_platform_release_string := $(shell cat $(cts_platform_release_path))
+
+ ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string)))
+ define error_msg
+ ============================================================
+ Could not find version "$(PLATFORM_VERSION)" in CTS platform version file:
+ $(cts_platform_version_path)
+ Most likely PLATFORM_VERSION in build/core/version_defaults.mk
+ has changed and a new version must be added to this CTS file.
+ ============================================================
+ endef
+ $(error $(error_msg))
+ endif
+ ifeq (,$(findstring $(PLATFORM_VERSION_LAST_STABLE),$(cts_platform_release_string)))
+ define error_msg
+ ============================================================
+ Could not find version "$(PLATFORM_VERSION_LAST_STABLE)" in CTS platform release file:
+ $(cts_platform_release_path)
+ Most likely PLATFORM_VERSION_LAST_STABLE in build/core/version_defaults.mk
+ has changed and a new version must be added to this CTS file.
+ ============================================================
+ endef
+ $(error $(error_msg))
+ endif
+endif
+
+# Creates a "cts-verifier" directory that will contain:
+#
+# 1. Out directory with a "android-cts-verifier" containing the CTS Verifier
+# and other binaries it needs.
+#
+# 2. Zipped version of the android-cts-verifier directory to be included with
+# the build distribution.
+##
+cts-dir := $(HOST_OUT)/cts-verifier
+verifier-dir-name := android-cts-verifier
+verifier-dir := $(cts-dir)/$(verifier-dir-name)
+verifier-zip-name := $(verifier-dir-name).zip
+verifier-zip := $(cts-dir)/$(verifier-zip-name)
+
+cts : $(verifier-zip)
+$(verifier-zip): PRIVATE_DIR := $(cts-dir)
+$(verifier-zip): $(SOONG_ANDROID_CTS_VERIFIER_ZIP)
+ rm -rf $(PRIVATE_DIR)
+ mkdir -p $(PRIVATE_DIR)
+ unzip -q -d $(PRIVATE_DIR) $<
+ $(copy-file-to-target)
+
+# For producing CTS coverage reports.
+# Run "make cts-test-coverage" in the $ANDROID_BUILD_TOP directory.
+
+cts_api_coverage_exe := $(HOST_OUT_EXECUTABLES)/cts-api-coverage
+dexdeps_exe := $(HOST_OUT_EXECUTABLES)/dexdeps
+
+coverage_out := $(HOST_OUT)/cts-api-coverage
+
+api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml
+
+napi_text_description := cts/tools/cts-api-coverage/etc/ndk-api.xml
+napi_xml_description := $(coverage_out)/ndk-api.xml
+$(napi_xml_description) : $(napi_text_description) $(ACP)
+ $(hide) echo "Preparing NDK API XML: $@"
+ $(hide) mkdir -p $(dir $@)
+ $(hide) $(ACP) $< $@
+
+system_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml
+
+cts-test-coverage-report := $(coverage_out)/test-coverage.html
+cts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html
+cts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml
+cts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html
+cts-combined-coverage-report := $(coverage_out)/combined-coverage.html
+cts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml
+
+cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description)
+cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description)
+
+android_cts_zip := $(HOST_OUT)/cts/android-cts.zip
+cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk
+
+$(cts-test-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-test-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-test-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-test-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-test-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-test-coverage-report) : $(android_cts_zip) $(cts_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS Tests API-NDK Coverage Report",\
+ $(PRIVATE_TEST_CASES),html)
+
+$(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS System API Coverage Report",\
+ $(PRIVATE_TEST_CASES),html)
+
+$(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\
+ $(PRIVATE_TEST_CASES),xml)
+
+$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(cts_verifier_apk)
+$(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-verifier-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-verifier-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-verifier-coverage-report) : $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS Verifier API Coverage Report",\
+ $(PRIVATE_TEST_CASES),html)
+
+$(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c))
+$(cts-combined-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-combined-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-combined-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-combined-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS Combined API Coverage Report",\
+ $(PRIVATE_TEST_CASES),html)
+
+$(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c))
+$(cts-combined-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-combined-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-combined-xml-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-combined-xml-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS Combined API Coverage Report - XML",\
+ $(PRIVATE_TEST_CASES),xml)
+
+.PHONY: cts-test-coverage
+cts-test-coverage : $(cts-test-coverage-report)
+
+.PHONY: cts-system-api-coverage
+cts-system-api-coverage : $(cts-system-api-coverage-report)
+
+.PHONY: cts-system-api-xml-coverage
+cts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report)
+
+.PHONY: cts-verifier-coverage
+cts-verifier-coverage : $(cts-verifier-coverage-report)
+
+.PHONY: cts-combined-coverage
+cts-combined-coverage : $(cts-combined-coverage-report)
+
+.PHONY: cts-combined-xml-coverage
+cts-combined-xml-coverage : $(cts-combined-xml-coverage-report)
+
+.PHONY: cts-coverage-report-all cts-api-coverage
+cts-coverage-report-all: cts-test-coverage cts-verifier-coverage cts-combined-coverage cts-combined-xml-coverage
+
+# Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals.
+$(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)
+$(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)
+
+# Arguments;
+# 1 - Name of the report printed out on the screen
+# 2 - List of apk files that will be scanned to generate the report
+# 3 - Format of the report
+define generate-coverage-report-cts
+ $(hide) mkdir -p $(dir $@)
+ $(hide) $(PRIVATE_CTS_API_COVERAGE_EXE) -d $(PRIVATE_DEXDEPS_EXE) -a $(PRIVATE_API_XML_DESC) -n $(PRIVATE_NAPI_XML_DESC) -f $(3) -o $@ $(2)
+ @ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)
+endef
+
+# Reset temp vars
+cts_api_coverage_dependencies :=
+cts_system_api_coverage_dependencies :=
+cts-combined-coverage-report :=
+cts-combined-xml-coverage-report :=
+cts-verifier-coverage-report :=
+cts-test-coverage-report :=
+cts-system-api-coverage-report :=
+cts-system-api-xml-coverage-report :=
+api_xml_description :=
+api_text_description :=
+system_api_xml_description :=
+napi_xml_description :=
+napi_text_description :=
+coverage_out :=
+dexdeps_exe :=
+cts_api_coverage_exe :=
+cts_verifier_apk :=
+android_cts_zip :=
diff --git a/core/tasks/dex_preopt_check.mk b/core/tasks/dex_preopt_check.mk
new file mode 100644
index 0000000..bfa1ec5
--- /dev/null
+++ b/core/tasks/dex_preopt_check.mk
@@ -0,0 +1,18 @@
+# Checks that some critical dexpreopt output files are installed.
+
+# Inputs:
+# DISABLE_DEXPREOPT_CHECK: True if the check should be disabled.
+# PRODUCT_PACKAGES: The list of packages to be installed for the product.
+# ALL_DEFAULT_INSTALLED_MODULES: The full list of modules going to be installed.
+# DEXPREOPT_SYSTEMSERVER_ARTIFACTS: The list of compilation artifacts of system server jars, which
+# is generated by Soong in dexpreopt_check.go.
+
+ifneq (true,$(DISABLE_DEXPREOPT_CHECK))
+ # Skip the check if the system server is not installed for the product.
+ ifneq (,$(filter services,$(PRODUCT_PACKAGES)))
+ $(call maybe-print-list-and-error,\
+ $(filter-out $(ALL_DEFAULT_INSTALLED_MODULES),$(DEXPREOPT_SYSTEMSERVER_ARTIFACTS)),\
+ Missing compilation artifacts. Dexpreopting is not working for some system server jars \
+ )
+ endif
+endif
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index c838264..5d5bfa8 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -16,6 +16,8 @@
'"module_name": "$(ALL_MODULES.$(m).MODULE_NAME)", ' \
'"test_config": [$(foreach w,$(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)),"$(w)", )], ' \
'"dependencies": [$(foreach w,$(sort $(ALL_DEPS.$(m).ALL_DEPS)),"$(w)", )], ' \
+ '"shared_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SHARED_LIBS)),"$(w)", )], ' \
+ '"system_shared_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS)),"$(w)", )], ' \
'"srcs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SRCS)),"$(w)", )], ' \
'"srcjars": [$(foreach w,$(sort $(ALL_MODULES.$(m).SRCJARS)),"$(w)", )], ' \
'"classes_jar": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASSES_JAR)),"$(w)", )], ' \
diff --git a/core/tasks/tools/build_custom_image.mk b/core/tasks/tools/build_custom_image.mk
index 4721591..f9ae2c1 100644
--- a/core/tasks/tools/build_custom_image.mk
+++ b/core/tasks/tools/build_custom_image.mk
@@ -57,7 +57,10 @@
my_kernel_module_copy_files :=
my_custom_image_modules_var := BOARD_$(strip $(call to-upper,$(my_custom_image_name)))_KERNEL_MODULES
ifdef $(my_custom_image_modules_var)
- my_kernel_module_copy_files += $(call build-image-kernel-modules,$(my_custom_image_modules_var),$(my_staging_dir),$(my_custom_image_name)/,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)))
+$(foreach kmod,\
+ $(call build-image-kernel-modules,$($(my_custom_image_modules_var)),$(my_staging_dir),$(CUSTOM_IMAGE_MOUNT_POINT),$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)),$($(my_custom_image_modules_var)),modules.load,,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)_stripped)),\
+ $(eval pair := $(subst :,$(space),$(kmod)))\
+ $(eval my_kernel_module_copy_files += $(word 1,$(pair)):$(subst $(my_staging_dir)/,,$(word 2,$(pair)))))
endif
# Collect CUSTOM_IMAGE_COPY_FILES.
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index 7d08a2f..47cf440 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -80,13 +80,18 @@
compatibility_zip_deps += $(test_suite_notice_txt)
compatibility_zip_resources += $(test_suite_notice_txt)
+compatibility_tests_list_zip := $(out_dir)-tests_list.zip
+
compatibility_zip := $(out_dir).zip
+$(compatibility_zip) : .KATI_IMPLICIT_OUTPUTS := $(compatibility_tests_list_zip)
$(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir)
$(compatibility_zip): PRIVATE_TOOLS := $(test_tools) $(test_suite_prebuilt_tools)
$(compatibility_zip): PRIVATE_SUITE_NAME := $(test_suite_name)
$(compatibility_zip): PRIVATE_DYNAMIC_CONFIG := $(test_suite_dynamic_config)
$(compatibility_zip): PRIVATE_RESOURCES := $(compatibility_zip_resources)
$(compatibility_zip): PRIVATE_JDK := $(test_suite_jdk)
+$(compatibility_zip): PRIVATE_tests_list := $(out_dir)-tests_list
+$(compatibility_zip): PRIVATE_tests_list_zip := $(compatibility_tests_list_zip)
$(compatibility_zip): $(compatibility_zip_deps) | $(ADB) $(ACP)
# Make dir structure
mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases
@@ -99,6 +104,11 @@
$(SOONG_ZIP) -d -o $@.tmp -C $(dir $@) -l $@.list
$(MERGE_ZIPS) $@ $@.tmp $(PRIVATE_JDK)
rm -f $@.tmp
+# Build a list of tests
+ rm -f $(PRIVATE_tests_list)
+ $(hide) grep -e .*\\.config$$ $@.list | sed s%$(PRIVATE_OUT_DIR)/testcases/%%g > $(PRIVATE_tests_list)
+ $(SOONG_ZIP) -d -o $(PRIVATE_tests_list_zip) -j -f $(PRIVATE_tests_list)
+ rm -f $(PRIVATE_tests_list)
# Reset all input variables
test_suite_name :=
diff --git a/core/tasks/vts-core-tests.mk b/core/tasks/vts-core-tests.mk
index 95c4d24..3c838b5 100644
--- a/core/tasks/vts-core-tests.mk
+++ b/core/tasks/vts-core-tests.mk
@@ -44,7 +44,7 @@
$(compatibility_zip): $(copy_kernel_tests)
.PHONY: vts
-vts: $(compatibility_zip)
-$(call dist-for-goals, vts, $(compatibility_zip))
+vts: $(compatibility_zip) $(compatibility_tests_list_zip)
+$(call dist-for-goals, vts, $(compatibility_zip) $(compatibility_tests_list_zip))
tests: vts
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index f429d7c..bf19c5c 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -39,51 +39,10 @@
include $(INTERNAL_BUILD_ID_MAKEFILE)
endif
-DEFAULT_PLATFORM_VERSION := SQ1A
-MIN_PLATFORM_VERSION := SQ1A
-MAX_PLATFORM_VERSION := SQ1A
-
-ALLOWED_VERSIONS := $(call allowed-platform-versions,\
- $(MIN_PLATFORM_VERSION),\
- $(MAX_PLATFORM_VERSION),\
- $(DEFAULT_PLATFORM_VERSION))
-
-ifndef TARGET_PLATFORM_VERSION
- TARGET_PLATFORM_VERSION := $(DEFAULT_PLATFORM_VERSION)
-endif
-
-ifeq (,$(filter $(ALLOWED_VERSIONS), $(TARGET_PLATFORM_VERSION)))
- $(warning Invalid TARGET_PLATFORM_VERSION '$(TARGET_PLATFORM_VERSION)', must be one of)
- $(error $(ALLOWED_VERSIONS))
-endif
-ALLOWED_VERSIONS :=
-MIN_PLATFORM_VERSION :=
-MAX_PLATFORM_VERSION :=
-
-.KATI_READONLY := \
- DEFAULT_PLATFORM_VERSION \
- TARGET_PLATFORM_VERSION
-
-# Default versions for each TARGET_PLATFORM_VERSION
-# TODO: PLATFORM_VERSION, PLATFORM_SDK_VERSION, etc. should be conditional
-# on this
-
-# This is the canonical definition of the platform version,
-# which is the version that we reveal to the end user.
-# Update this value when the platform version changes (rather
-# than overriding it somewhere else). Can be an arbitrary string.
-
-# When you change PLATFORM_VERSION for a given PLATFORM_SDK_VERSION
-# please add that PLATFORM_VERSION as well as clean up obsolete PLATFORM_VERSION's
-# in the following text file:
-# cts/tests/tests/os/assets/platform_versions.txt
-
-# Note that there should be one PLATFORM_VERSION and PLATFORM_VERSION_CODENAME
-# entry for each unreleased API level, regardless of
-# MIN_PLATFORM_VERSION/MAX_PLATFORM_VERSION. PLATFORM_VERSION is used to
-# generate the range of allowed SDK versions, so it must have an entry for every
-# unreleased API level targetable by this branch, not just those that are valid
-# lunch targets for this branch.
+DEFAULT_PLATFORM_VERSION := TP1A
+.KATI_READONLY := DEFAULT_PLATFORM_VERSION
+MIN_PLATFORM_VERSION := TP1A
+MAX_PLATFORM_VERSION := TP1A
# The last stable version name of the platform that was released. During
# development, this stays at that previous version, while the codename indicates
@@ -93,50 +52,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.SQ1A := REL
-
-ifndef PLATFORM_VERSION_CODENAME
- PLATFORM_VERSION_CODENAME := $(PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION))
- ifndef PLATFORM_VERSION_CODENAME
- # PLATFORM_VERSION_CODENAME falls back to TARGET_PLATFORM_VERSION
- PLATFORM_VERSION_CODENAME := $(TARGET_PLATFORM_VERSION)
- endif
-
- # This is all of the *active* development codenames.
- # This confusing name is needed because
- # all_codenames has been baked into build.prop for ages.
- #
- # Should be either the same as PLATFORM_VERSION_CODENAME or a comma-separated
- # list of additional codenames after PLATFORM_VERSION_CODENAME.
- PLATFORM_VERSION_ALL_CODENAMES :=
-
- # Build a list of all active code names. Avoid duplicates, and stop when we
- # reach a codename that matches PLATFORM_VERSION_CODENAME (anything beyond
- # that is not included in our build).
- _versions_in_target := \
- $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION))
- $(foreach version,$(_versions_in_target),\
- $(eval _codename := $(PLATFORM_VERSION_CODENAME.$(version)))\
- $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\
- $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename))))
-
- # And convert from space separated to comma separated.
- PLATFORM_VERSION_ALL_CODENAMES := \
- $(subst $(space),$(comma),$(strip $(PLATFORM_VERSION_ALL_CODENAMES)))
-
-endif
-.KATI_READONLY := \
- PLATFORM_VERSION_CODENAME \
- PLATFORM_VERSION_ALL_CODENAMES
-
-ifndef PLATFORM_VERSION
- ifeq (REL,$(PLATFORM_VERSION_CODENAME))
- PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE)
- else
- PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME)
- endif
-endif
-.KATI_READONLY := PLATFORM_VERSION
+PLATFORM_VERSION_CODENAME.TP1A := Tiramisu
ifndef PLATFORM_SDK_VERSION
# This is the canonical definition of the SDK version, which defines
@@ -155,84 +71,13 @@
endif
.KATI_READONLY := PLATFORM_SDK_VERSION
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
- PLATFORM_PREVIEW_SDK_VERSION := 0
-else
- ifndef PLATFORM_PREVIEW_SDK_VERSION
- # This is the definition of a preview SDK version over and above the current
- # platform SDK version. Unlike the platform SDK version, a higher value
- # for preview SDK version does NOT mean that all prior preview APIs are
- # included. Packages reading this value to determine compatibility with
- # known APIs should check that this value is precisely equal to the preview
- # SDK version the package was built for, otherwise it should fall back to
- # assuming the device can only support APIs as of the previous official
- # public release.
- # This value will always be forced to 0 for release builds by the logic
- # in the "ifeq" block above, so the value below will be used on any
- # non-release builds, and it should always be at least 1, to indicate that
- # APIs may have changed since the claimed PLATFORM_SDK_VERSION.
- PLATFORM_PREVIEW_SDK_VERSION := 1
- endif
-endif
-.KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION
+# This is the sdk extension version of this tree.
+PLATFORM_SDK_EXTENSION_VERSION := 1
+.KATI_READONLY := PLATFORM_SDK_EXTENSION_VERSION
-ifndef DEFAULT_APP_TARGET_SDK
- # This is the default minSdkVersion and targetSdkVersion to use for
- # all .apks created by the build system. It can be overridden by explicitly
- # setting these in the .apk's AndroidManifest.xml. It is either the code
- # name of the development build or, if this is a release build, the official
- # SDK version of this release.
- ifeq (REL,$(PLATFORM_VERSION_CODENAME))
- DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION)
- else
- DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME)
- endif
-endif
-.KATI_READONLY := DEFAULT_APP_TARGET_SDK
-
-ifndef PLATFORM_VNDK_VERSION
- # This is the definition of the VNDK version for the current VNDK libraries.
- # The version is only available when PLATFORM_VERSION_CODENAME == REL.
- # Otherwise, it will be set to a CODENAME version. The ABI is allowed to be
- # changed only before the Android version is released. Once
- # PLATFORM_VNDK_VERSION is set to actual version, the ABI for this version
- # will be frozon and emit build errors if any ABI for the VNDK libs are
- # changed.
- # After that the snapshot of the VNDK with this version will be generated.
- #
- # The VNDK version follows PLATFORM_SDK_VERSION.
- ifeq (REL,$(PLATFORM_VERSION_CODENAME))
- PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
- else
- PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
- endif
-endif
-.KATI_READONLY := PLATFORM_VNDK_VERSION
-
-ifndef PLATFORM_SYSTEMSDK_MIN_VERSION
- # This is the oldest version of system SDK that the platform supports. Contrary
- # to the public SDK where platform essentially supports all previous SDK versions,
- # platform supports only a few number of recent system SDK versions as some of
- # old system APIs are gradually deprecated, removed and then deleted.
- PLATFORM_SYSTEMSDK_MIN_VERSION := 28
-endif
-.KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION
-
-# This is the list of system SDK versions that the current platform supports.
-PLATFORM_SYSTEMSDK_VERSIONS :=
-ifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION))
- $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\
- $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION)))
- PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION))
-endif
-# Platform always supports the current version
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
- PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION)
-else
- PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_VERSION_CODENAME)
-endif
-PLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS)))
-.KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS
+# This is the sdk extension version that PLATFORM_SDK_VERSION ships with.
+PLATFORM_BASE_SDK_EXTENSION_VERSION := 1
+.KATI_READONLY := PLATFORM_BASE_SDK_EXTENSION_VERSION
ifndef PLATFORM_SECURITY_PATCH
# Used to indicate the security patch that has been applied to the device.
@@ -244,65 +89,5 @@
endif
.KATI_READONLY := PLATFORM_SECURITY_PATCH
-ifndef PLATFORM_SECURITY_PATCH_TIMESTAMP
- # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH.
- PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ="GMT" $(PLATFORM_SECURITY_PATCH)' +%s)
-endif
-.KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP
+include $(BUILD_SYSTEM)/version_util.mk
-ifndef PLATFORM_BASE_OS
- # Used to indicate the base os applied to the device.
- # Can be an arbitrary string, but must be a single word.
- #
- # If there is no $PLATFORM_BASE_OS set, keep it empty.
- PLATFORM_BASE_OS :=
-endif
-.KATI_READONLY := PLATFORM_BASE_OS
-
-ifndef BUILD_ID
- # Used to signify special builds. E.g., branches and/or releases,
- # like "M5-RC7". Can be an arbitrary string, but must be a single
- # word and a valid file name.
- #
- # If there is no BUILD_ID set, make it obvious.
- BUILD_ID := UNKNOWN
-endif
-.KATI_READONLY := BUILD_ID
-
-ifndef BUILD_DATETIME
- # Used to reproduce builds by setting the same time. Must be the number
- # of seconds since the Epoch.
- BUILD_DATETIME := $(shell date +%s)
-endif
-
-DATE := date -d @$(BUILD_DATETIME)
-.KATI_READONLY := DATE
-
-# Everything should be using BUILD_DATETIME_FROM_FILE instead.
-# BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves
-# to soong_ui.
-$(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE)
-
-HAS_BUILD_NUMBER := true
-ifndef BUILD_NUMBER
- # BUILD_NUMBER should be set to the source control value that
- # represents the current state of the source code. E.g., a
- # perforce changelist number or a git hash. Can be an arbitrary string
- # (to allow for source control that uses something other than numbers),
- # but must be a single word and a valid file name.
- #
- # If no BUILD_NUMBER is set, create a useful "I am an engineering build
- # from this date/time" value. Make it start with a non-digit so that
- # anyone trying to parse it as an integer will probably get "0".
- BUILD_NUMBER := eng.$(shell echo $${BUILD_USERNAME:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S)
- HAS_BUILD_NUMBER := false
-endif
-.KATI_READONLY := BUILD_NUMBER HAS_BUILD_NUMBER
-
-ifndef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
- # Used to set minimum supported target sdk version. Apps targeting sdk
- # version lower than the set value will result in a warning being shown
- # when any activity from the app is started.
- PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 23
-endif
-.KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
diff --git a/core/version_util.mk b/core/version_util.mk
new file mode 100644
index 0000000..b7c4e48
--- /dev/null
+++ b/core/version_util.mk
@@ -0,0 +1,245 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+
+ALLOWED_VERSIONS := $(call allowed-platform-versions,\
+ $(MIN_PLATFORM_VERSION),\
+ $(MAX_PLATFORM_VERSION),\
+ $(DEFAULT_PLATFORM_VERSION))
+
+ifndef TARGET_PLATFORM_VERSION
+ TARGET_PLATFORM_VERSION := $(DEFAULT_PLATFORM_VERSION)
+endif
+
+ifeq (,$(filter $(ALLOWED_VERSIONS), $(TARGET_PLATFORM_VERSION)))
+ $(warning Invalid TARGET_PLATFORM_VERSION '$(TARGET_PLATFORM_VERSION)', must be one of)
+ $(error $(ALLOWED_VERSIONS))
+endif
+ALLOWED_VERSIONS :=
+MIN_PLATFORM_VERSION :=
+MAX_PLATFORM_VERSION :=
+
+.KATI_READONLY := TARGET_PLATFORM_VERSION
+
+# Default versions for each TARGET_PLATFORM_VERSION
+# TODO: PLATFORM_VERSION, PLATFORM_SDK_VERSION, etc. should be conditional
+# on this
+
+# This is the canonical definition of the platform version,
+# which is the version that we reveal to the end user.
+# Update this value when the platform version changes (rather
+# than overriding it somewhere else). Can be an arbitrary string.
+
+# When you change PLATFORM_VERSION for a given PLATFORM_SDK_VERSION
+# please add that PLATFORM_VERSION as well as clean up obsolete PLATFORM_VERSION's
+# in the following text file:
+# cts/tests/tests/os/assets/platform_versions.txt
+
+# Note that there should be one PLATFORM_VERSION and PLATFORM_VERSION_CODENAME
+# entry for each unreleased API level, regardless of
+# MIN_PLATFORM_VERSION/MAX_PLATFORM_VERSION. PLATFORM_VERSION is used to
+# generate the range of allowed SDK versions, so it must have an entry for every
+# unreleased API level targetable by this branch, not just those that are valid
+# lunch targets for this branch.
+
+ifndef PLATFORM_VERSION_CODENAME
+ PLATFORM_VERSION_CODENAME := $(PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION))
+ ifndef PLATFORM_VERSION_CODENAME
+ # PLATFORM_VERSION_CODENAME falls back to TARGET_PLATFORM_VERSION
+ PLATFORM_VERSION_CODENAME := $(TARGET_PLATFORM_VERSION)
+ endif
+
+ # This is all of the *active* development codenames.
+ # This confusing name is needed because
+ # all_codenames has been baked into build.prop for ages.
+ #
+ # Should be either the same as PLATFORM_VERSION_CODENAME or a comma-separated
+ # list of additional codenames after PLATFORM_VERSION_CODENAME.
+ PLATFORM_VERSION_ALL_CODENAMES :=
+
+ # Build a list of all active code names. Avoid duplicates, and stop when we
+ # reach a codename that matches PLATFORM_VERSION_CODENAME (anything beyond
+ # that is not included in our build).
+ _versions_in_target := \
+ $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION))
+ $(foreach version,$(_versions_in_target),\
+ $(eval _codename := $(PLATFORM_VERSION_CODENAME.$(version)))\
+ $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\
+ $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename))))
+
+ # And convert from space separated to comma separated.
+ PLATFORM_VERSION_ALL_CODENAMES := \
+ $(subst $(space),$(comma),$(strip $(PLATFORM_VERSION_ALL_CODENAMES)))
+
+endif
+.KATI_READONLY := \
+ PLATFORM_VERSION_CODENAME \
+ PLATFORM_VERSION_ALL_CODENAMES
+
+ifndef PLATFORM_VERSION
+ ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+ PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE)
+ else
+ PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME)
+ endif
+endif
+.KATI_READONLY := PLATFORM_VERSION
+
+
+ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+ PLATFORM_PREVIEW_SDK_VERSION := 0
+else
+ ifndef PLATFORM_PREVIEW_SDK_VERSION
+ # This is the definition of a preview SDK version over and above the current
+ # platform SDK version. Unlike the platform SDK version, a higher value
+ # for preview SDK version does NOT mean that all prior preview APIs are
+ # included. Packages reading this value to determine compatibility with
+ # known APIs should check that this value is precisely equal to the preview
+ # SDK version the package was built for, otherwise it should fall back to
+ # assuming the device can only support APIs as of the previous official
+ # public release.
+ # This value will always be forced to 0 for release builds by the logic
+ # in the "ifeq" block above, so the value below will be used on any
+ # non-release builds, and it should always be at least 1, to indicate that
+ # APIs may have changed since the claimed PLATFORM_SDK_VERSION.
+ PLATFORM_PREVIEW_SDK_VERSION := 1
+ endif
+endif
+.KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION
+
+ifndef DEFAULT_APP_TARGET_SDK
+ # This is the default minSdkVersion and targetSdkVersion to use for
+ # all .apks created by the build system. It can be overridden by explicitly
+ # setting these in the .apk's AndroidManifest.xml. It is either the code
+ # name of the development build or, if this is a release build, the official
+ # SDK version of this release.
+ ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+ DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION)
+ else
+ DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME)
+ endif
+endif
+.KATI_READONLY := DEFAULT_APP_TARGET_SDK
+
+ifndef PLATFORM_VNDK_VERSION
+ # This is the definition of the VNDK version for the current VNDK libraries.
+ # The version is only available when PLATFORM_VERSION_CODENAME == REL.
+ # Otherwise, it will be set to a CODENAME version. The ABI is allowed to be
+ # changed only before the Android version is released. Once
+ # PLATFORM_VNDK_VERSION is set to actual version, the ABI for this version
+ # will be frozon and emit build errors if any ABI for the VNDK libs are
+ # changed.
+ # After that the snapshot of the VNDK with this version will be generated.
+ #
+ # The VNDK version follows PLATFORM_SDK_VERSION.
+ ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+ PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
+ else
+ PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
+ endif
+endif
+.KATI_READONLY := PLATFORM_VNDK_VERSION
+
+ifndef PLATFORM_SYSTEMSDK_MIN_VERSION
+ # This is the oldest version of system SDK that the platform supports. Contrary
+ # to the public SDK where platform essentially supports all previous SDK versions,
+ # platform supports only a few number of recent system SDK versions as some of
+ # old system APIs are gradually deprecated, removed and then deleted.
+ PLATFORM_SYSTEMSDK_MIN_VERSION := 28
+endif
+.KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION
+
+# This is the list of system SDK versions that the current platform supports.
+PLATFORM_SYSTEMSDK_VERSIONS :=
+ifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION))
+ $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\
+ $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION)))
+ PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION))
+endif
+# Platform always supports the current version
+ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+ PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION)
+else
+ PLATFORM_SYSTEMSDK_VERSIONS += $(subst $(comma),$(space),$(PLATFORM_VERSION_ALL_CODENAMES))
+endif
+PLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS)))
+.KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS
+
+.KATI_READONLY := PLATFORM_SECURITY_PATCH
+
+ifndef PLATFORM_SECURITY_PATCH_TIMESTAMP
+ # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH.
+ PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ="GMT" $(PLATFORM_SECURITY_PATCH)' +%s)
+endif
+.KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP
+
+ifndef PLATFORM_BASE_OS
+ # Used to indicate the base os applied to the device.
+ # Can be an arbitrary string, but must be a single word.
+ #
+ # If there is no $PLATFORM_BASE_OS set, keep it empty.
+ PLATFORM_BASE_OS :=
+endif
+.KATI_READONLY := PLATFORM_BASE_OS
+
+ifndef BUILD_ID
+ # Used to signify special builds. E.g., branches and/or releases,
+ # like "M5-RC7". Can be an arbitrary string, but must be a single
+ # word and a valid file name.
+ #
+ # If there is no BUILD_ID set, make it obvious.
+ BUILD_ID := UNKNOWN
+endif
+.KATI_READONLY := BUILD_ID
+
+ifndef BUILD_DATETIME
+ # Used to reproduce builds by setting the same time. Must be the number
+ # of seconds since the Epoch.
+ BUILD_DATETIME := $(shell date +%s)
+endif
+
+DATE := date -d @$(BUILD_DATETIME)
+.KATI_READONLY := DATE
+
+# Everything should be using BUILD_DATETIME_FROM_FILE instead.
+# BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves
+# to soong_ui.
+$(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE)
+
+HAS_BUILD_NUMBER := true
+ifndef BUILD_NUMBER
+ # BUILD_NUMBER should be set to the source control value that
+ # represents the current state of the source code. E.g., a
+ # perforce changelist number or a git hash. Can be an arbitrary string
+ # (to allow for source control that uses something other than numbers),
+ # but must be a single word and a valid file name.
+ #
+ # If no BUILD_NUMBER is set, create a useful "I am an engineering build
+ # from this date/time" value. Make it start with a non-digit so that
+ # anyone trying to parse it as an integer will probably get "0".
+ BUILD_NUMBER := eng.$(shell echo $${BUILD_USERNAME:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S)
+ HAS_BUILD_NUMBER := false
+endif
+.KATI_READONLY := BUILD_NUMBER HAS_BUILD_NUMBER
+
+ifndef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
+ # Used to set minimum supported target sdk version. Apps targeting sdk
+ # version lower than the set value will result in a warning being shown
+ # when any activity from the app is started.
+ PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 23
+endif
+.KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
diff --git a/envsetup.sh b/envsetup.sh
index 8a995c7..a23bbad 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -240,7 +240,7 @@
export ANDROID_TOOLCHAIN_2ND_ARCH=$gccprebuiltdir/$toolchaindir2
fi
- export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools:$T/external/selinux/prebuilts/bin
+ export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools
# add kernel specific binaries
case $(uname -s) in
@@ -252,9 +252,7 @@
esac
ANDROID_BUILD_PATHS=$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_TOOLCHAIN
- if [ -n "$ANDROID_TOOLCHAIN_2ND_ARCH" ]; then
- ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_TOOLCHAIN_2ND_ARCH
- fi
+ ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_TOOLCHAIN_2ND_ARCH
ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_DEV_SCRIPTS
# Append llvm binutils prebuilts path to ANDROID_BUILD_PATHS.
@@ -287,8 +285,9 @@
local ACLOUD_PATH="$T/prebuilts/asuite/acloud/$os_arch"
local AIDEGEN_PATH="$T/prebuilts/asuite/aidegen/$os_arch"
local ATEST_PATH="$T/prebuilts/asuite/atest/$os_arch"
- export ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ACLOUD_PATH:$AIDEGEN_PATH:$ATEST_PATH:
+ ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ACLOUD_PATH:$AIDEGEN_PATH:$ATEST_PATH
+ export ANDROID_BUILD_PATHS=$(tr -s : <<<"${ANDROID_BUILD_PATHS}:")
export PATH=$ANDROID_BUILD_PATHS$PATH
# out with the duplicate old
@@ -331,15 +330,15 @@
function bazel()
{
- local T="$(gettop)"
- if [ ! "$T" ]; then
- echo "Couldn't locate the top of the tree. Try setting TOP."
- return
+ if which bazel &>/dev/null; then
+ >&2 echo "NOTE: bazel() function sourced from Android's envsetup.sh is being used instead of $(which bazel)"
+ >&2 echo
fi
- if which bazel &>/dev/null; then
- >&2 echo "NOTE: bazel() function sourced from envsetup.sh is being used instead of $(which bazel)"
- >&2 echo
+ local T="$(gettop)"
+ if [ ! "$T" ]; then
+ >&2 echo "Couldn't locate the top of the Android tree. Try setting TOP. This bazel() function cannot be used outside of the AOSP directory."
+ return
fi
"$T/tools/bazel" "$@"
@@ -703,6 +702,10 @@
build_build_var_cache
if [ $? -ne 0 ]
then
+ if [[ "$product" =~ .*_(eng|user|userdebug) ]]
+ then
+ echo "Did you mean -${product/*_/}? (dash instead of underscore)"
+ fi
return 1
fi
export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
@@ -1455,7 +1458,7 @@
> $ANDROID_PRODUCT_OUT/module-info.json.build.log 2>&1
}
-# Verifies that module-info.txt exists, creating it if it doesn't.
+# Verifies that module-info.txt exists, returning nonzero if it doesn't.
function verifymodinfo() {
if [ ! "$ANDROID_PRODUCT_OUT" ]; then
if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
@@ -1466,7 +1469,7 @@
if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then
if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
- echo "Could not find module-info.json. It will only be built once, and it can be updated with 'refreshmod'" >&2
+ echo "Could not find module-info.json. Please run 'refreshmod' first." >&2
fi
return 1
fi
@@ -1585,6 +1588,10 @@
function installmod() {
if [[ $# -eq 0 ]]; then
echo "usage: installmod [adb install arguments] <module>" >&2
+ echo "" >&2
+ echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
+ echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
+ echo "-s must be the first flag passed if it exists." >&2
return 1
fi
@@ -1599,9 +1606,18 @@
echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
return 1
fi
+ local serial_device=""
+ if [[ "$1" == "-s" ]]; then
+ if [[ $# -le 2 ]]; then
+ echo "-s requires an argument" >&2
+ return 1
+ fi
+ serial_device="-s $2"
+ shift 2
+ fi
local length=$(( $# - 1 ))
- echo adb install ${@:1:$length} $_path
- adb install ${@:1:$length} $_path
+ echo adb $serial_device install ${@:1:$length} $_path
+ adb $serial_device install ${@:1:$length} $_path
}
function _complete_android_module_names() {
@@ -1657,12 +1673,19 @@
if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
color_failed=$'\E'"[0;31m"
color_success=$'\E'"[0;32m"
+ color_warning=$'\E'"[0;33m"
color_reset=$'\E'"[00m"
else
color_failed=""
color_success=""
color_reset=""
fi
+
+ if [[ "x${USE_RBE}" == "x" && $mins -gt 15 && "${ANDROID_BUILD_ENVIRONMENT_CONFIG}" == "googler" ]]; then
+ echo
+ echo "${color_warning}Start using RBE (http://go/build-fast) to get faster builds!${color_reset}"
+ fi
+
echo
if [ $ret -eq 0 ] ; then
echo -n "${color_success}#### build completed successfully "
@@ -1687,7 +1710,23 @@
if T="$(gettop)"; then
_wrap_build "$T/build/soong/soong_ui.bash" --build-mode --${bc} --dir="$(pwd)" "$@"
else
- echo "Couldn't locate the top of the tree. Try setting TOP."
+ >&2 echo "Couldn't locate the top of the tree. Try setting TOP."
+ return 1
+ fi
+)
+
+# Convenience entry point (like m) to use Bazel in AOSP.
+function b()
+(
+ # Generate BUILD, bzl files into the synthetic Bazel workspace (out/soong/workspace).
+ _trigger_build "all-modules" bp2build USE_BAZEL_ANALYSIS= || return 1
+ # Then, run Bazel using the synthetic workspace as the --package_path.
+ if [[ -z "$@" ]]; then
+ # If there are no args, show help.
+ bazel help
+ else
+ # Else, always run with the bp2build configuration, which sets Bazel's package path to the synthetic workspace.
+ bazel "$@" --config=bp2build
fi
)
diff --git a/rbesetup.sh b/rbesetup.sh
index ec39e6e..3b0e7cf 100644
--- a/rbesetup.sh
+++ b/rbesetup.sh
@@ -24,8 +24,11 @@
}
# This function needs to run first as the remaining defining functions may be
-# using the envsetup.sh defined functions.
-_source_env_setup_script || return
+# using the envsetup.sh defined functions. Skip this part if this script is already
+# being invoked from envsetup.sh.
+if [[ "$1" != "--skip-envsetup" ]]; then
+ _source_env_setup_script || return
+fi
# This function prefixes the given command with appropriate variables needed
# for the build to be executed with RBE.
diff --git a/target/board/Android.mk b/target/board/Android.mk
index 4dd6b17..142270e 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -24,8 +24,10 @@
$(call pretty,"Generated: ($@)")
ifdef board_info_txt
$(hide) grep -v '#' $< > $@
-else
+else ifdef TARGET_BOOTLOADER_BOARD_NAME
$(hide) echo "board=$(TARGET_BOOTLOADER_BOARD_NAME)" > $@
+else
+ $(hide) echo "" > $@
endif
# Copy compatibility metadata to the device.
diff --git a/target/board/BoardConfigGkiCommon.mk b/target/board/BoardConfigGkiCommon.mk
index f480b93..c0f5db9 100644
--- a/target/board/BoardConfigGkiCommon.mk
+++ b/target/board/BoardConfigGkiCommon.mk
@@ -32,9 +32,6 @@
BOARD_USES_RECOVERY_AS_BOOT :=
TARGET_NO_KERNEL := false
BOARD_USES_GENERIC_KERNEL_IMAGE := true
-BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := \
- 5.4-android12-unstable \
- 5.10-android12-unstable \
# Copy boot image in $OUT to target files. This is defined for targets where
# the installed GKI APEXes are built from source.
diff --git a/target/board/BoardConfigModuleCommon.mk b/target/board/BoardConfigModuleCommon.mk
index 9832474..24c01a5 100644
--- a/target/board/BoardConfigModuleCommon.mk
+++ b/target/board/BoardConfigModuleCommon.mk
@@ -4,7 +4,3 @@
# Required for all module devices.
TARGET_USES_64_BIT_BINDER := true
-
-# Necessary to make modules able to use the VNDK via 'use_vendor: true'
-# TODO(b/185769808): look into whether this is still used.
-BOARD_VNDK_VERSION := current
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index 49ae216..b0c9950 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -57,12 +57,6 @@
BOARD_KERNEL-4.19-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
BOARD_KERNEL-4.19-GZ-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 47185920
-BOARD_KERNEL-5.4_BOOTIMAGE_PARTITION_SIZE := 67108864
-BOARD_KERNEL-5.4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
-BOARD_KERNEL-5.4-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
-BOARD_KERNEL-5.4-GZ-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 47185920
-BOARD_KERNEL-5.4-LZ4_BOOTIMAGE_PARTITION_SIZE := 53477376
-BOARD_KERNEL-5.4-LZ4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 53477376
BOARD_KERNEL-5.10_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_KERNEL-5.10-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_KERNEL-5.10-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
@@ -74,13 +68,11 @@
BOARD_KERNEL_BINARIES := \
kernel-4.19-gz \
- kernel-5.4 kernel-5.4-gz kernel-5.4-lz4 \
kernel-5.10 kernel-5.10-gz kernel-5.10-lz4 \
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
BOARD_KERNEL_BINARIES += \
kernel-4.19-gz-allsyms \
- kernel-5.4-allsyms kernel-5.4-gz-allsyms kernel-5.4-lz4-allsyms \
kernel-5.10-allsyms kernel-5.10-gz-allsyms kernel-5.10-lz4-allsyms \
endif
diff --git a/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index b331af4..0a05d9c 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -16,23 +16,16 @@
PRODUCT_COPY_FILES += \
kernel/prebuilts/4.19/arm64/kernel-4.19-gz:kernel-4.19-gz \
- kernel/prebuilts/5.4/arm64/kernel-5.4:kernel-5.4 \
- kernel/prebuilts/5.4/arm64/kernel-5.4-gz:kernel-5.4-gz \
- kernel/prebuilts/5.4/arm64/kernel-5.4-lz4:kernel-5.4-lz4 \
kernel/prebuilts/5.10/arm64/kernel-5.10:kernel-5.10 \
kernel/prebuilts/5.10/arm64/kernel-5.10-gz:kernel-5.10-gz \
kernel/prebuilts/5.10/arm64/kernel-5.10-lz4:kernel-5.10-lz4 \
$(call dist-for-goals, dist_files, kernel/prebuilts/4.19/arm64/prebuilt-info.txt:kernel/4.19/prebuilt-info.txt)
-$(call dist-for-goals, dist_files, kernel/prebuilts/5.4/arm64/prebuilt-info.txt:kernel/5.4/prebuilt-info.txt)
$(call dist-for-goals, dist_files, kernel/prebuilts/5.10/arm64/prebuilt-info.txt:kernel/5.10/prebuilt-info.txt)
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
PRODUCT_COPY_FILES += \
kernel/prebuilts/4.19/arm64/kernel-4.19-gz-allsyms:kernel-4.19-gz-allsyms \
- kernel/prebuilts/5.4/arm64/kernel-5.4-allsyms:kernel-5.4-allsyms \
- kernel/prebuilts/5.4/arm64/kernel-5.4-gz-allsyms:kernel-5.4-gz-allsyms \
- kernel/prebuilts/5.4/arm64/kernel-5.4-lz4-allsyms:kernel-5.4-lz4-allsyms \
kernel/prebuilts/5.10/arm64/kernel-5.10-allsyms:kernel-5.10-allsyms \
kernel/prebuilts/5.10/arm64/kernel-5.10-gz-allsyms:kernel-5.10-gz-allsyms \
kernel/prebuilts/5.10/arm64/kernel-5.10-lz4-allsyms:kernel-5.10-lz4-allsyms \
diff --git a/target/board/generic_x86_64/BoardConfig.mk b/target/board/generic_x86_64/BoardConfig.mk
index bdc862e..640216c 100755
--- a/target/board/generic_x86_64/BoardConfig.mk
+++ b/target/board/generic_x86_64/BoardConfig.mk
@@ -28,19 +28,16 @@
include build/make/target/board/BoardConfigGkiCommon.mk
BOARD_KERNEL-5.4_BOOTIMAGE_PARTITION_SIZE := 67108864
-BOARD_KERNEL-5.4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_KERNEL-5.10_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_KERNEL-5.10-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
BOARD_KERNEL_BINARIES := \
- kernel-5.4 \
kernel-5.10 \
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
BOARD_KERNEL_BINARIES += \
- kernel-5.4-allsyms \
kernel-5.10-allsyms \
endif
diff --git a/target/board/generic_x86_64/device.mk b/target/board/generic_x86_64/device.mk
index f31a491..d28ace7 100755
--- a/target/board/generic_x86_64/device.mk
+++ b/target/board/generic_x86_64/device.mk
@@ -15,15 +15,12 @@
#
PRODUCT_COPY_FILES += \
- kernel/prebuilts/5.4/x86_64/kernel-5.4:kernel-5.4 \
kernel/prebuilts/5.10/x86_64/kernel-5.10:kernel-5.10 \
-$(call dist-for-goals, dist_files, kernel/prebuilts/5.4/x86_64/prebuilt-info.txt:kernel/5.4/prebuilt-info.txt)
$(call dist-for-goals, dist_files, kernel/prebuilts/5.10/x86_64/prebuilt-info.txt:kernel/5.10/prebuilt-info.txt)
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
PRODUCT_COPY_FILES += \
- kernel/prebuilts/5.4/x86_64/kernel-5.4-allsyms:kernel-5.4-allsyms \
kernel/prebuilts/5.10/x86_64/kernel-5.10-allsyms:kernel-5.10-allsyms \
endif
diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk
index 38f82a2..01897b7 100644
--- a/target/product/aosp_arm64.mk
+++ b/target/product/aosp_arm64.mk
@@ -53,6 +53,7 @@
#
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
#
# Special settings for GSI releasing
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 5d78264..b3cfae4 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -56,6 +56,7 @@
$(call inherit-product-if-exists, device/generic/goldfish/x86_64-vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
#
# Special settings for GSI releasing
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 14ce1af..a995b8b 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -52,6 +52,7 @@
com.android.adbd \
com.android.appsearch \
com.android.conscrypt \
+ com.android.cronet \
com.android.extservices \
com.android.i18n \
com.android.ipsec \
@@ -78,6 +79,7 @@
dnsmasq \
DownloadProvider \
dpm \
+ dump.erofs \
dumpstate \
dumpsys \
DynamicSystemInstallationService \
@@ -88,6 +90,7 @@
framework-minus-apex \
framework-res \
framework-sysconfig.xml \
+ fsck.erofs \
fsck_msdos \
fsverity-release-cert-der \
fs_config_files_system \
@@ -132,6 +135,7 @@
libaudioeffect_jni \
libbinder \
libbinder_ndk \
+ libbinder_rpc_unstable \
libc.bootstrap \
libcamera2ndk \
libcutils \
@@ -211,11 +215,12 @@
MediaProviderLegacy \
mediaserver \
mke2fs \
+ mkfs.erofs \
monkey \
mtpd \
ndc \
netd \
- NetworkStack \
+ NetworkStackNext \
odsign \
org.apache.http.legacy \
otacerts \
@@ -316,18 +321,20 @@
atest \
bcc \
bit \
+ dump.erofs \
e2fsck \
fastboot \
flags_health_check \
+ fsck.erofs \
icu-data_host_i18n_apex \
icu_tzdata.dat_host_tzdata_apex \
idmap2 \
incident_report \
ld.mc \
lpdump \
- mdnsd \
minigzip \
mke2fs \
+ mkfs.erofs \
resize2fs \
sgdisk \
sqlite3 \
@@ -361,7 +368,6 @@
adb_keys \
arping \
dmuserd \
- gdbserver \
idlcli \
init-debug.rc \
iotop \
@@ -373,6 +379,7 @@
profcollectd \
profcollectctl \
remount \
+ servicedispatcher \
showmap \
sqlite3 \
ss \
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 07b3361..5004b85 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -25,6 +25,7 @@
linker.recovery \
otacerts.recovery \
recovery \
+ servicemanager.recovery \
shell_and_utilities_recovery \
watchdogd.recovery \
@@ -78,7 +79,8 @@
PRODUCT_PACKAGES += \
vendor_compatibility_matrix.xml \
-# Packages to update the recovery partition, which will be installed on
-# /vendor. TODO(b/141648565): Don't install these unless they're needed.
+# Base modules and settings for the debug ramdisk, which is then packed
+# into a boot-debug.img and a vendor_boot-debug.img.
PRODUCT_PACKAGES += \
- applypatch
+ adb_debug.prop \
+ userdebug_plat_sepolicy.cil
diff --git a/target/product/core_64_bit_only.mk b/target/product/core_64_bit_only.mk
index 53c9c74..061728f 100644
--- a/target/product/core_64_bit_only.mk
+++ b/target/product/core_64_bit_only.mk
@@ -25,6 +25,9 @@
# Set the zygote property to select the 64-bit script.
# This line must be parsed before the one in core_minimal.mk
PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64
+# A 64-bit-only platform does not have dex2oat32, so make sure dex2oat64 is
+# used for dexopt.
+PRODUCT_VENDOR_PROPERTIES += dalvik.vm.dex2oat64.enabled=true
TARGET_SUPPORTS_32_BIT_APPS := false
TARGET_SUPPORTS_64_BIT_APPS := true
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 0fa9058..f98f7e2 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -14,6 +14,9 @@
# limitations under the License.
#
+# This file contains product config for the ART module that is common for
+# platform and unbundled builds.
+
ifeq ($(ART_APEX_JARS),)
$(error ART_APEX_JARS is empty; cannot initialize PRODUCT_BOOT_JARS variable)
endif
@@ -25,9 +28,8 @@
# 4. Non-updatable APEX jars
# 5. Updatable APEX jars
#
-# ART APEX jars (1) are defined in ART_APEX_JARS. System, system_ext, and non updatable boot jars
-# are defined below in PRODUCT_BOOT_JARS. All updatable APEX boot jars are part of
-# PRODUCT_UPDATABLE_BOOT_JARS.
+# ART APEX jars (1) are defined in ART_APEX_JARS. System and system_ext boot jars are defined below
+# in PRODUCT_BOOT_JARS. All other non-art APEX boot jars are part of the PRODUCT_APEX_BOOT_JARS.
#
# The actual runtime ordering matching above is determined by derive_classpath service at runtime.
# See packages/modules/SdkExtensions/README.md for more details.
@@ -45,14 +47,12 @@
voip-common \
ims-common
-# Non-updatable APEX jars. Keep the list sorted.
-PRODUCT_BOOT_JARS += \
- com.android.i18n:core-icu4j
-
-# Updatable APEX boot jars. Keep the list sorted by module names and then library names.
-PRODUCT_UPDATABLE_BOOT_JARS := \
+# APEX boot jars. Keep the list sorted by module names and then library names.
+# Note: core-icu4j is moved back to PRODUCT_BOOT_JARS in product_config.mk at a later stage.
+PRODUCT_APEX_BOOT_JARS := \
com.android.appsearch:framework-appsearch \
com.android.conscrypt:conscrypt \
+ com.android.i18n:core-icu4j \
com.android.ipsec:android.net.ipsec.ike \
com.android.media:updatable-media \
com.android.mediaprovider:framework-mediaprovider \
@@ -65,12 +65,28 @@
com.android.tethering:framework-tethering \
com.android.wifi:framework-wifi
-# Updatable APEX system server jars. Keep the list sorted by module names and then library names.
-PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS := \
+# List of system_server classpath jars delivered via apex.
+# Keep the list sorted by module names and then library names.
+PRODUCT_APEX_SYSTEM_SERVER_JARS := \
com.android.appsearch:service-appsearch \
+ com.android.art:service-art \
com.android.media:service-media-s \
com.android.permission:service-permission \
+PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += art/build/boot/boot-image-profile.txt
+
+# List of jars on the platform that system_server loads dynamically using separate classloaders.
+# Keep the list sorted library names.
+PRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \
+
+# List of jars delivered via apex that system_server loads dynamically using separate classloaders.
+# Keep the list sorted by module names and then library names.
+PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS := \
+ com.android.os.statsd:service-statsd \
+ com.android.scheduling:service-scheduling \
+ com.android.tethering:service-connectivity \
+ com.android.wifi:service-wifi \
+
# Minimal configuration for running dex2oat (default argument values).
# PRODUCT_USES_DEFAULT_ART_CONFIG must be true to enable boot image compilation.
PRODUCT_USES_DEFAULT_ART_CONFIG := true
diff --git a/target/product/emulator_vendor.mk b/target/product/emulator_vendor.mk
index b9f33ab..f71b275 100644
--- a/target/product/emulator_vendor.mk
+++ b/target/product/emulator_vendor.mk
@@ -37,7 +37,7 @@
#watchdog tiggers reboot because location service is not
#responding, disble it for now.
-#still keep it on internal master as it is still working
+#still keep it on internal main (master) as it is still working
#once it is fixed in aosp, remove this block of comment.
#PRODUCT_VENDOR_PROPERTIES += \
#config.disable_location=true
diff --git a/target/product/generic_ramdisk.mk b/target/product/generic_ramdisk.mk
index ae81329..80d34be 100644
--- a/target/product/generic_ramdisk.mk
+++ b/target/product/generic_ramdisk.mk
@@ -25,6 +25,7 @@
# Debug ramdisk
PRODUCT_PACKAGES += \
+ adb_debug.prop \
userdebug_plat_sepolicy.cil \
_my_paths := \
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index cb4fdcb..0d788fa 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -50,11 +50,21 @@
_vndk_check_failure_message += " Run \`update-vndk-list.sh\` to update $(LATEST_VNDK_LIB_LIST)"
endif
+# The *-ndk_platform.so libraries no longer exist and are removed from the VNDK set. However, they
+# can exist if NEED_AIDL_NDK_PLATFORM_BACKEND is set to true for legacy devices. Don't be bothered
+# with the extraneous libraries.
+ifeq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),true)
+ _READ_INTERNAL_VNDK_LIB_LIST := sed /ndk_platform.so/d $(INTERNAL_VNDK_LIB_LIST)
+else
+ _READ_INTERNAL_VNDK_LIB_LIST := cat $(INTERNAL_VNDK_LIB_LIST)
+endif
+
$(check-vndk-list-timestamp): $(INTERNAL_VNDK_LIB_LIST) $(LATEST_VNDK_LIB_LIST) $(HOST_OUT_EXECUTABLES)/update-vndk-list.sh
- $(hide) ( diff --old-line-format="Removed %L" \
+ $(hide) ($(_READ_INTERNAL_VNDK_LIB_LIST) | \
+ diff --old-line-format="Removed %L" \
--new-line-format="Added %L" \
--unchanged-line-format="" \
- $(LATEST_VNDK_LIB_LIST) $(INTERNAL_VNDK_LIB_LIST) \
+ $(LATEST_VNDK_LIB_LIST) - \
|| ( echo -e $(_vndk_check_failure_message); exit 1 ))
$(hide) mkdir -p $(dir $@)
$(hide) touch $@
@@ -84,9 +94,13 @@
echo " echo Run lunch or choosecombo first" >> $@; \
echo " exit 1" >> $@; \
echo "fi" >> $@; \
- echo "cd \$${ANDROID_BUILD_TOP}" >> $@; \
- echo "cp $(PRIVATE_INTERNAL_VNDK_LIB_LIST) $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@; \
- echo "echo $(PRIVATE_LATEST_VNDK_LIB_LIST) updated." >> $@
+ echo "cd \$${ANDROID_BUILD_TOP}" >> $@
+ifeq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),true)
+ $(hide) echo "sed /ndk_platform.so/d $(PRIVATE_INTERNAL_VNDK_LIB_LIST) > $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@
+else
+ $(hide) echo "cp $(PRIVATE_INTERNAL_VNDK_LIB_LIST) $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@
+endif
+ $(hide) echo "echo $(PRIVATE_LATEST_VNDK_LIB_LIST) updated." >> $@
endif
@chmod a+x $@
@@ -237,6 +251,6 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := init
+LOCAL_MODULE_RELATIVE_PATH := gsi
include $(BUILD_PREBUILT)
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 971ec92..e618226 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -18,9 +18,9 @@
LLNDK: libsync.so
LLNDK: libvndksupport.so
LLNDK: libvulkan.so
-VNDK-SP: android.hardware.common-V2-ndk_platform.so
-VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so
-VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so
+VNDK-SP: android.hardware.common-V2-ndk.so
+VNDK-SP: android.hardware.common.fmq-V1-ndk.so
+VNDK-SP: android.hardware.graphics.common-V2-ndk.so
VNDK-SP: android.hardware.graphics.common@1.0.so
VNDK-SP: android.hardware.graphics.common@1.1.so
VNDK-SP: android.hardware.graphics.common@1.2.so
@@ -57,41 +57,55 @@
VNDK-SP: libutilscallstack.so
VNDK-SP: libz.so
VNDK-core: android.hardware.audio.common@2.0.so
-VNDK-core: android.hardware.authsecret-V1-ndk_platform.so
-VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so
+VNDK-core: android.hardware.authsecret-V1-ndk.so
+VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so
VNDK-core: android.hardware.configstore-utils.so
VNDK-core: android.hardware.configstore@1.0.so
VNDK-core: android.hardware.configstore@1.1.so
VNDK-core: android.hardware.confirmationui-support-lib.so
-VNDK-core: android.hardware.gnss-V1-ndk_platform.so
+VNDK-core: android.hardware.dumpstate-V1-ndk.so
+VNDK-core: android.hardware.gnss-V1-ndk.so
VNDK-core: android.hardware.graphics.allocator@2.0.so
VNDK-core: android.hardware.graphics.allocator@3.0.so
VNDK-core: android.hardware.graphics.allocator@4.0.so
VNDK-core: android.hardware.graphics.bufferqueue@1.0.so
VNDK-core: android.hardware.graphics.bufferqueue@2.0.so
-VNDK-core: android.hardware.health.storage-V1-ndk_platform.so
-VNDK-core: android.hardware.identity-V3-ndk_platform.so
-VNDK-core: android.hardware.keymaster-V3-ndk_platform.so
-VNDK-core: android.hardware.light-V1-ndk_platform.so
+VNDK-core: android.hardware.health-V1-ndk.so
+VNDK-core: android.hardware.health.storage-V1-ndk.so
+VNDK-core: android.hardware.identity-V3-ndk.so
+VNDK-core: android.hardware.keymaster-V3-ndk.so
+VNDK-core: android.hardware.light-V1-ndk.so
VNDK-core: android.hardware.media.bufferpool@2.0.so
VNDK-core: android.hardware.media.omx@1.0.so
VNDK-core: android.hardware.media@1.0.so
-VNDK-core: android.hardware.memtrack-V1-ndk_platform.so
+VNDK-core: android.hardware.memtrack-V1-ndk.so
VNDK-core: android.hardware.memtrack@1.0.so
-VNDK-core: android.hardware.oemlock-V1-ndk_platform.so
-VNDK-core: android.hardware.power-V2-ndk_platform.so
-VNDK-core: android.hardware.power.stats-V1-ndk_platform.so
-VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so
-VNDK-core: android.hardware.security.keymint-V1-ndk_platform.so
-VNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so
-VNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so
+VNDK-core: android.hardware.oemlock-V1-ndk.so
+VNDK-core: android.hardware.power-V2-ndk.so
+VNDK-core: android.hardware.power.stats-V1-ndk.so
+VNDK-core: android.hardware.radio-V1-ndk.so
+VNDK-core: android.hardware.radio.config-V1-ndk.so
+VNDK-core: android.hardware.radio.data-V1-ndk.so
+VNDK-core: android.hardware.radio.messaging-V1-ndk.so
+VNDK-core: android.hardware.radio.modem-V1-ndk.so
+VNDK-core: android.hardware.radio.network-V1-ndk.so
+VNDK-core: android.hardware.radio.sim-V1-ndk.so
+VNDK-core: android.hardware.radio.voice-V1-ndk.so
+VNDK-core: android.hardware.rebootescrow-V1-ndk.so
+VNDK-core: android.hardware.security.dice-V1-ndk.so
+VNDK-core: android.hardware.security.keymint-V1-ndk.so
+VNDK-core: android.hardware.security.secureclock-V1-ndk.so
+VNDK-core: android.hardware.security.sharedsecret-V1-ndk.so
VNDK-core: android.hardware.soundtrigger@2.0-core.so
VNDK-core: android.hardware.soundtrigger@2.0.so
-VNDK-core: android.hardware.vibrator-V2-ndk_platform.so
-VNDK-core: android.hardware.weaver-V1-ndk_platform.so
+VNDK-core: android.hardware.vibrator-V2-ndk.so
+VNDK-core: android.hardware.weaver-V1-ndk.so
+VNDK-core: android.hardware.wifi.hostapd-V1-ndk.so
+VNDK-core: android.hardware.wifi.supplicant-V1-ndk.so
VNDK-core: android.hidl.token@1.0-utils.so
VNDK-core: android.hidl.token@1.0.so
-VNDK-core: android.system.keystore2-V1-ndk_platform.so
+VNDK-core: android.system.keystore2-V1-ndk.so
+VNDK-core: android.system.suspend-V1-ndk.so
VNDK-core: android.system.suspend@1.0.so
VNDK-core: libaudioroute.so
VNDK-core: libaudioutils.so
diff --git a/target/product/gsi/init.gsi.rc b/target/product/gsi/init.gsi.rc
index f482843..69c8e46 100644
--- a/target/product/gsi/init.gsi.rc
+++ b/target/product/gsi/init.gsi.rc
@@ -2,4 +2,4 @@
# Android init script for GSI required initialization
#
-import /system/system_ext/etc/init/init.vndk-${ro.vndk.version:-nodef}.rc
+import /system/system_ext/etc/gsi/init.vndk-${ro.vndk.version:-nodef}.rc
diff --git a/target/product/gsi/init.vndk-nodef.rc b/target/product/gsi/init.vndk-nodef.rc
index efeef11..1b141a0 100644
--- a/target/product/gsi/init.vndk-nodef.rc
+++ b/target/product/gsi/init.vndk-nodef.rc
@@ -1,3 +1,3 @@
on early-init
- # Must define BOARD_VNDK_VERSION
+ # Reboot if BOARD_VNDK_VERSION is not defined
exec - root -- /system/bin/reboot bootloader
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index d924d0b..b1266ee 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -34,7 +34,7 @@
# GSI should always support up-to-date platform features.
# Keep this value at the latest API level to ensure latest build system
# default configs are applied.
-PRODUCT_SHIPPING_API_LEVEL := 30
+PRODUCT_SHIPPING_API_LEVEL := 31
# Enable dynamic partitions to facilitate mixing onto Cuttlefish
PRODUCT_USE_DYNAMIC_PARTITIONS := true
@@ -62,15 +62,26 @@
init.gsi.rc \
init.vndk-nodef.rc \
-# Support additional P, Q and R VNDK packages
-PRODUCT_EXTRA_VNDK_VERSIONS := 28 29 30
+# Support additional VNDK snapshots
+PRODUCT_EXTRA_VNDK_VERSIONS := \
+ 28 \
+ 29 \
+ 30 \
+ 31 \
# Do not build non-GSI partition images.
PRODUCT_BUILD_CACHE_IMAGE := false
+PRODUCT_BUILD_DEBUG_BOOT_IMAGE := false
+PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false
PRODUCT_BUILD_USERDATA_IMAGE := false
PRODUCT_BUILD_VENDOR_IMAGE := false
PRODUCT_BUILD_SUPER_PARTITION := false
PRODUCT_BUILD_SUPER_EMPTY_IMAGE := false
+PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true
# Always build modules from source
MODULE_BUILD_FROM_SOURCE := true
+
+# Additional settings used in all GSI builds
+PRODUCT_PRODUCT_PROPERTIES += \
+ ro.crypto.metadata_init_delete_all_keys.enabled=false \
diff --git a/target/product/non_ab_device.mk b/target/product/non_ab_device.mk
new file mode 100644
index 0000000..6dc4506
--- /dev/null
+++ b/target/product/non_ab_device.mk
@@ -0,0 +1,5 @@
+# Packages to update the recovery partition, which will be installed on
+# /vendor. Don't install these unless they're needed.
+PRODUCT_PACKAGES += \
+ applypatch
+
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index b511aa6..ee63757 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -61,7 +61,7 @@
apex_test_module := art-check-release-apex-gen-fakebin
endif
-ifeq (true,$(SOONG_CONFIG_art_module_source_build)
+ifeq (true,$(call soong_config_get,art_module,source_build))
PRODUCT_HOST_PACKAGES += $(apex_test_module)
endif
@@ -75,6 +75,14 @@
PRODUCT_PACKAGES += \
hiddenapi-package-whitelist.xml \
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
+ # Don't depend on the framework boot image profile in unbundled builds where
+ # frameworks/base may not be present.
+ # TODO(b/179900989): We may not need this check once we stop using full
+ # platform products on the thin ART manifest branch.
+ PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += frameworks/base/boot/boot-image-profile.txt
+endif
+
# The dalvik.vm.dexopt.thermal-cutoff property must contain one of the values
# listed here:
#
@@ -93,7 +101,7 @@
dalvik.vm.appimageformat=lz4
PRODUCT_SYSTEM_PROPERTIES += \
- ro.dalvik.vm.native.bridge=0
+ ro.dalvik.vm.native.bridge?=0
# Different dexopt types for different package update/install times.
# On eng builds, make "boot" reasons only extract for faster turnaround.
@@ -127,10 +135,6 @@
pm.dexopt.cmdline?=verify \
pm.dexopt.shared?=speed
-# Pass file with the list of updatable boot class path packages to dex2oat.
-PRODUCT_SYSTEM_PROPERTIES += \
- dalvik.vm.dex2oat-updatable-bcp-packages-file=/system/etc/updatable-bcp-packages.txt
-
# Enable resolution of startup const strings.
PRODUCT_SYSTEM_PROPERTIES += \
dalvik.vm.dex2oat-resolve-startup-strings=true
diff --git a/target/product/sdk_phone_arm64.mk b/target/product/sdk_phone_arm64.mk
index 0831b54..4203d45 100644
--- a/target/product/sdk_phone_arm64.mk
+++ b/target/product/sdk_phone_arm64.mk
@@ -50,10 +50,6 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_arm64/device.mk)
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product, sdk/build/product_sdk.mk)
-$(call inherit-product, development/build/product_sdk.mk)
-
# keep this apk for sdk targets for now
PRODUCT_PACKAGES += \
EmulatorSmokeTests
diff --git a/target/product/sdk_phone_armv7.mk b/target/product/sdk_phone_armv7.mk
index f649980..6c88b44 100644
--- a/target/product/sdk_phone_armv7.mk
+++ b/target/product/sdk_phone_armv7.mk
@@ -49,10 +49,6 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_arm/device.mk)
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product, sdk/build/product_sdk.mk)
-$(call inherit-product, development/build/product_sdk.mk)
-
# keep this apk for sdk targets for now
PRODUCT_PACKAGES += \
EmulatorSmokeTests
diff --git a/target/product/sdk_phone_x86.mk b/target/product/sdk_phone_x86.mk
index 0e1bca4..a324e5f 100644
--- a/target/product/sdk_phone_x86.mk
+++ b/target/product/sdk_phone_x86.mk
@@ -49,10 +49,6 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_x86/device.mk)
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product-if-exists, sdk/build/product_sdk.mk)
-$(call inherit-product-if-exists, development/build/product_sdk.mk)
-
# Overrides
PRODUCT_BRAND := Android
PRODUCT_NAME := sdk_phone_x86
diff --git a/target/product/sdk_phone_x86_64.mk b/target/product/sdk_phone_x86_64.mk
index fffac04..ff9018d 100644
--- a/target/product/sdk_phone_x86_64.mk
+++ b/target/product/sdk_phone_x86_64.mk
@@ -50,10 +50,6 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_x86_64/device.mk)
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product-if-exists, sdk/build/product_sdk.mk)
-$(call inherit-product-if-exists, development/build/product_sdk.mk)
-
# Overrides
PRODUCT_BRAND := Android
PRODUCT_NAME := sdk_phone_x86_64
diff --git a/target/product/virtual_ab_ota/compression.mk b/target/product/virtual_ab_ota/compression.mk
index 8301047..88c58b8 100644
--- a/target/product/virtual_ab_ota/compression.mk
+++ b/target/product/virtual_ab_ota/compression.mk
@@ -17,6 +17,7 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk)
PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true
PRODUCT_VIRTUAL_AB_COMPRESSION := true
PRODUCT_PACKAGES += \
snapuserd.vendor_ramdisk \
diff --git a/core/combo/HOST_CROSS_linux_bionic-arm64.mk b/target/product/virtual_ab_ota/compression_with_xor.mk
similarity index 69%
rename from core/combo/HOST_CROSS_linux_bionic-arm64.mk
rename to target/product/virtual_ab_ota/compression_with_xor.mk
index df6865f..7d92532 100644
--- a/core/combo/HOST_CROSS_linux_bionic-arm64.mk
+++ b/target/product/virtual_ab_ota/compression_with_xor.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2020 The Android Open Source Project
+# Copyright (C) 2021 The Android Open-Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,9 +14,8 @@
# limitations under the License.
#
-# Configuration for builds hosted on linux_arm-arm64
-# Included by combo/select.mk
-define $(combo_var_prefix)transform-shared-lib-to-toc
-$(call _gen_toc_command_for_elf,$(1),$(2))
-endef
+$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)
+
+
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true
diff --git a/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk b/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
index bc81b33..de1f07d 100644
--- a/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
+++ b/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
@@ -24,3 +24,4 @@
PRODUCT_PACKAGES += \
linker.vendor_ramdisk \
e2fsck.vendor_ramdisk \
+ fsck.f2fs.vendor_ramdisk \
diff --git a/core/build_id.rbc b/tests/board.rbc
similarity index 65%
rename from core/build_id.rbc
rename to tests/board.rbc
index 4f33833..8696e40 100644
--- a/core/build_id.rbc
+++ b/tests/board.rbc
@@ -1,4 +1,3 @@
-
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This file has been manually converted from build_id.mk
-def init(g):
+load("//build/make/core:product_config.rbc", "rblf")
- # BUILD_ID is usually used to specify the branch name (like "MAIN") or a branch name and a release candidate
- # (like "CRB01"). It must be a single word, and is capitalized by convention.
- g["BUILD_ID"]="AOSP.MASTER"
\ No newline at end of file
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["A_LIST_VARIABLE"] += ["bar"]
diff --git a/core/build_id.rbc b/tests/board_input_vars.rbc
similarity index 65%
copy from core/build_id.rbc
copy to tests/board_input_vars.rbc
index 4f33833..69d9cd6 100644
--- a/core/build_id.rbc
+++ b/tests/board_input_vars.rbc
@@ -1,4 +1,3 @@
-
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This file has been manually converted from build_id.mk
-def init(g):
+load("//build/make/core:product_config.rbc", "rblf")
- # BUILD_ID is usually used to specify the branch name (like "MAIN") or a branch name and a release candidate
- # (like "CRB01"). It must be a single word, and is capitalized by convention.
- g["BUILD_ID"]="AOSP.MASTER"
\ No newline at end of file
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["A_LIST_VARIABLE"] = ["foo"]
diff --git a/tests/conversion_error.rbc b/tests/conversion_error.rbc
new file mode 100644
index 0000000..5212378
--- /dev/null
+++ b/tests/conversion_error.rbc
@@ -0,0 +1,27 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# Run test configuration and verify its result.
+# The main configuration file is device.rbc.
+# It inherits part1.rbc and also includes include1.rbc
+# TODO(asmundak): more tests are needed to verify that:
+# * multi-level inheritance works as expected
+# * all runtime functions (wildcard, regex, etc.) work
+
+load("//build/make/core:product_config.rbc", "rblf")
+load(":version_defaults.rbc", "version_defaults")
+load(":device.rbc", "init")
+
+rblf.mk2rbc_error("file.mk:123", "cannot convert")
diff --git a/tests/device.rbc b/tests/device.rbc
deleted file mode 100644
index 5d4e70c..0000000
--- a/tests/device.rbc
+++ /dev/null
@@ -1,42 +0,0 @@
-
-# Copyright 2021 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Top-level test configuration.
-# Converted from the following makefile
-### PRODUCT_PACKAGES += dev
-### PRODUCT_HOST_PACKAGES += host
-### $(call inherit-product, $(LOCAL_PATH)/part1.mk)
-### PRODUCT_COPY_FILES += device_from:device_to
-### include $(LOCAL_PATH)/include1.mk
-### PRODUCT_PACKAGES += dev_after
-### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz
-
-load("//build/make/core:product_config.rbc", "rblf")
-load(":part1.rbc", _part1_init = "init")
-load(":include1.rbc", _include1_init = "init")
-
-def init(g, handle):
- cfg = rblf.cfg(handle)
- rblf.setdefault(handle, "PRODUCT_PACKAGES")
- cfg["PRODUCT_PACKAGES"] += ["dev"]
- rblf.setdefault(handle, "PRODUCT_HOST_PACKAGES")
- cfg["PRODUCT_HOST_PACKAGES"] += ["host"]
- rblf.inherit(handle, "test/part1", _part1_init)
- rblf.setdefault(handle, "PRODUCT_COPY_FILES")
- cfg["PRODUCT_COPY_FILES"] += ["device_from:device_to"]
- _include1_init(g, handle)
- cfg["PRODUCT_PACKAGES"] += ["dev_after"]
- cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin/audio", "||VENDOR-PATH-PH||/etc") +
- ["xyz"])
diff --git a/tests/input_variables.rbc b/tests/input_variables.rbc
new file mode 100644
index 0000000..0bb100f
--- /dev/null
+++ b/tests/input_variables.rbc
@@ -0,0 +1,28 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file was generated by running `m RBC_PRODUCT_CONFIG=1 nothing`
+# and then copying it from out/rbc/out/rbc/make_vars_pre_product_config.rbc.
+# It was manually trimmed down afterwards to just the variables we need.
+
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["PLATFORM_VERSION_CODENAME"] = "Tiramisu"
+ g["PLATFORM_VERSION"] = "Tiramisu"
+ g["TARGET_BUILD_VARIANT"] = "userdebug"
+ g["TARGET_BUILD_TYPE"] = "release"
+ g["TARGET_PRODUCT"] = "aosp_arm64"
+ g["PLATFORM_SDK_VERSION"] = "31"
diff --git a/tests/part1.rbc b/tests/part1.rbc
index 3e50751..ae79d32 100644
--- a/tests/part1.rbc
+++ b/tests/part1.rbc
@@ -26,3 +26,5 @@
cfg["PRODUCT_COPY_FILES"] += ["part_from:part_to"]
rblf.setdefault(handle, "PRODUCT_PRODUCT_PROPERTIES")
cfg["PRODUCT_PRODUCT_PROPERTIES"] += ["part_properties"]
+ rblf.soong_config_namespace(g, "NS1")
+ rblf.soong_config_append(g, "NS1", "v1", "abc_part1")
diff --git a/tests/product.rbc b/tests/product.rbc
new file mode 100644
index 0000000..9ae6393
--- /dev/null
+++ b/tests/product.rbc
@@ -0,0 +1,71 @@
+
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Top-level test configuration.
+# Converted from the following makefile
+### PRODUCT_PACKAGES += dev
+### PRODUCT_HOST_PACKAGES += host
+### $(call inherit-product, $(LOCAL_PATH)/part1.mk)
+### PRODUCT_COPY_FILES += device_from:device_to
+### include $(LOCAL_PATH)/include1.mk
+### PRODUCT_PACKAGES += dev_after
+### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz:/etc/xyz
+### PRODUCT_COPY_FILES += $(call copy-files,x.xml y.xml,/etc)
+### $(call add_soong_config_namespace,NS1)
+### $(call soong_config_append,NS1,v1,abc)
+### $(call soong_config_append,NS1,v2,def)
+### $(call add_soong_config_var_value,NS2,v3,abc)
+### $(call soong_config_set,NS2,v3,xyz)
+
+load("//build/make/core:product_config.rbc", "rblf")
+load(":part1.rbc", _part1_init = "init")
+load(":include1.rbc", _include1_init = "init")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.setdefault(handle, "PRODUCT_PACKAGES")
+ cfg["PRODUCT_PACKAGES"] += ["dev"]
+ rblf.setdefault(handle, "PRODUCT_HOST_PACKAGES")
+ cfg["PRODUCT_HOST_PACKAGES"] += ["host"]
+ rblf.inherit(handle, "test/part1", _part1_init)
+ rblf.setdefault(handle, "PRODUCT_COPY_FILES")
+ cfg["PRODUCT_COPY_FILES"] += ["device_from:device_to"]
+ _include1_init(g, handle)
+ cfg["PRODUCT_PACKAGES"] += ["dev_after"]
+ cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin", "||VENDOR-PATH-PH||/etc") +
+ ["xyz:/etc/xyz"])
+ cfg["PRODUCT_COPY_FILES"] += rblf.copy_files("x.xml y.xml", "/etc")
+ cfg["PRODUCT_COPY_FILES"] += rblf.copy_files(["from/sub/x", "from/sub/y"], "to")
+
+ rblf.soong_config_namespace(g, "NS1")
+ rblf.soong_config_append(g, "NS1", "v1", "abc")
+ rblf.soong_config_append(g, "NS1", "v2", "def")
+ rblf.soong_config_set(g, "NS2", "v3", "abc")
+ rblf.soong_config_set(g, "NS2", "v3", "xyz")
+
+ rblf.mkdist_for_goals(g, "goal", "dir1/file1:out1 dir1/file2:out2")
+ rblf.mkdist_for_goals(g, "goal", "dir2/file2:")
+
+ if rblf.board_platform_in(g, "board1 board2"):
+ cfg["PRODUCT_PACKAGES"] += ["bad_package"]
+ g["TARGET_BOARD_PLATFORM"] = "board1"
+ if rblf.board_platform_in(g, "board1 board2"):
+ cfg["PRODUCT_PACKAGES"] += ["board1_in"]
+ if rblf.board_platform_in(g, ["board3","board2"]):
+ cfg["PRODUCT_PACKAGES"] += ["bad_board_in"]
+ if rblf.board_platform_is(g, "board1"):
+ cfg["PRODUCT_PACKAGES"] += ["board1_is"]
+ if rblf.board_platform_is(g, "board2"):
+ cfg["PRODUCT_PACKAGES"] += ["bad_board1_is"]
diff --git a/tests/run.rbc b/tests/run.rbc
index b13f835..b82887f 100644
--- a/tests/run.rbc
+++ b/tests/run.rbc
@@ -1,4 +1,3 @@
-
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,37 +13,119 @@
# limitations under the License.
-# Run test configuration and verify its result.
-# The main configuration file is device.rbc.
+# Run test product configuration and verify its result.
+# The main configuration file is product.rbc.
# It inherits part1.rbc and also includes include1.rbc
# TODO(asmundak): more tests are needed to verify that:
# * multi-level inheritance works as expected
# * all runtime functions (wildcard, regex, etc.) work
load("//build/make/core:product_config.rbc", "rblf")
-load(":device.rbc", "init")
+load(":input_variables.rbc", input_variables_init = "init")
+load(":product.rbc", "init")
+load(":board.rbc", board_init = "init")
+load(":board_input_vars.rbc", board_input_vars_init = "init")
def assert_eq(expected, actual):
if expected != actual:
- fail("Expected %s, got %s" % (expected, actual))
+ fail("Expected '%s', got '%s'" % (expected, actual))
+# Unit tests for non-trivial runtime functions
+assert_eq("", rblf.mkstrip(" \n \t "))
+assert_eq("a b c", rblf.mkstrip(" a b \n c \t"))
+assert_eq(1, rblf.mkstrip(1))
-globals, config = rblf.product_configuration("test/device", init)
+assert_eq("b1 b2", rblf.mksubst("a", "b", "a1 a2"))
+assert_eq(["b1", "x2"], rblf.mksubst("a", "b", ["a1", "x2"]))
+
+assert_eq("ABcdYZ", rblf.mkpatsubst("ab%yz", "AB%YZ", "abcdyz"))
+assert_eq("bcz", rblf.mkpatsubst("a%z", "A%Z", "bcz"))
+assert_eq(["Ay", "Az"], rblf.mkpatsubst("a%", "A%", ["ay", "az"]))
+assert_eq("AcZ bcz", rblf.mkpatsubst("a%z", "A%Z", "acz bcz"))
+assert_eq("Abcd", rblf.mkpatsubst("a%", "A%", "abcd"))
+assert_eq("abcZ", rblf.mkpatsubst("%z", "%Z", "abcz"))
+assert_eq("azx b", rblf.mkpatsubst("az", "AZ", "azx b"))
+assert_eq(["azx", "b"], rblf.mkpatsubst("az", "AZ", ["azx", "b"]))
+assert_eq("ABC", rblf.mkpatsubst("abc", "ABC", "abc"))
+assert_eq(["%/foo"], rblf.mkpatsubst("%", "\\%/%", ["foo"]))
+assert_eq(["foo/%"], rblf.mkpatsubst("%", "%/%", ["foo"]))
+assert_eq(["from/a:to/a", "from/b:to/b"], rblf.product_copy_files_by_pattern("from/%", "to/%", "a b"))
+
+assert_eq([], rblf.filter(["a", "", "b"], "f"))
+assert_eq(["", "b"], rblf.filter_out(["a", "" ], ["a", "", "b"] ))
+
+assert_eq("foo.c no_folder", rblf.notdir(["src/foo.c", "no_folder"]))
+assert_eq("foo.c no_folder", rblf.notdir("src/foo.c no_folder"))
+assert_eq("", rblf.notdir("/"))
+assert_eq("", rblf.notdir(""))
+
+assert_eq(
+ ["build/make/tests/board.rbc", "build/make/tests/board_input_vars.rbc"],
+ rblf.expand_wildcard("build/make/tests/board*.rbc")
+)
+assert_eq(
+ ["build/make/tests/run.rbc", "build/make/tests/product.rbc"],
+ rblf.expand_wildcard("build/make/tests/run.rbc build/make/tests/product.rbc")
+)
+assert_eq(
+ ["build/make/tests/run.rbc"],
+ rblf.expand_wildcard("build/make/tests/run.rbc build/make/tests/nonexistent.rbc")
+)
+
+(globals, config, globals_base) = rblf.product_configuration("test/device", init, input_variables_init)
assert_eq(
{
"PRODUCT_COPY_FILES": [
"part_from:part_to",
"device_from:device_to",
- "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio_platform_info_noextcodec_snd.xml",
- "xyz"
+ "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio/audio_platform_info_noextcodec_snd.xml",
+ "xyz:/etc/xyz",
+ "x.xml:/etc/x.xml",
+ "y.xml:/etc/y.xml",
+ "from/sub/x:to/x",
+ "from/sub/y:to/y",
],
"PRODUCT_HOST_PACKAGES": ["host"],
"PRODUCT_PACKAGES": [
"dev",
"inc",
- "dev_after"
+ "dev_after",
+ "board1_in",
+ "board1_is",
],
"PRODUCT_PRODUCT_PROPERTIES": ["part_properties"]
},
{ k:v for k, v in sorted(config.items()) }
)
+
+ns = globals["$SOONG_CONFIG_NAMESPACES"]
+assert_eq(
+ {
+ "NS1": {
+ "v1": "abc abc_part1",
+ "v2": "def"
+ },
+ "NS2": {
+ "v3": "xyz"
+ }
+ },
+ {k:v for k, v in sorted(ns.items()) }
+)
+
+assert_eq("Tiramisu", globals["PLATFORM_VERSION"])
+assert_eq("31", globals["PLATFORM_SDK_VERSION"])
+
+assert_eq("xyz", rblf.soong_config_get(globals, "NS2", "v3"))
+assert_eq(None, rblf.soong_config_get(globals, "NS2", "nonexistant_var"))
+
+goals = globals["$dist_for_goals"]
+assert_eq(
+ {
+ "goal": [("dir1/file1", "out1"), ("dir1/file2", "out2"), ("dir2/file2", "file2")]
+ },
+ { k:v for k,v in sorted(goals.items()) }
+)
+
+(board_globals, board_config, board_globals_base) = rblf.board_configuration(board_init, board_input_vars_init)
+assert_eq({"A_LIST_VARIABLE": ["foo", "bar"]}, board_globals)
+assert_eq({"A_LIST_VARIABLE": ["foo"]}, board_globals_base)
diff --git a/tests/version_defaults.rbc b/tests/version_defaults.rbc
new file mode 100644
index 0000000..9b35b57
--- /dev/null
+++ b/tests/version_defaults.rbc
@@ -0,0 +1,11 @@
+version_defaults = struct(
+ codenames = { "SP1A" : "S" },
+ default_platform_version = "SP1A",
+ max_platform_version = "SP1A",
+ min_platform_version = "SP1A",
+ platform_base_sdk_extension_version = 0,
+ platform_sdk_extension_version = 1,
+ platform_sdk_version = 30,
+ platform_security_patch = "2021-08-05",
+ platform_version_last_stable = 11,
+)
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
new file mode 100644
index 0000000..75b0de6
--- /dev/null
+++ b/tools/BUILD.bazel
@@ -0,0 +1,20 @@
+py_library(
+ name="event_log_tags",
+ srcs = ["event_log_tags.py"],
+)
+
+py_binary(
+ name="java-event-log-tags",
+ srcs=["java-event-log-tags.py"],
+ deps=[":event_log_tags"],
+ visibility = ["//visibility:public"],
+ python_version = "PY2",
+)
+
+py_binary(
+ name="merge-event-log-tags",
+ srcs=["merge-event-log-tags.py"],
+ deps=[":event_log_tags"],
+ visibility = ["//visibility:public"],
+ python_version = "PY2",
+)
diff --git a/tools/build-license-metadata.sh b/tools/build-license-metadata.sh
deleted file mode 100755
index a138dbe..0000000
--- a/tools/build-license-metadata.sh
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/bin/sh
-
-set -u
-
-ME=$(basename $0)
-
-USAGE="Usage: ${ME} {options}
-
-Builds a license metadata specification and outputs it to stdout or {outfile}.
-
-The available options are:
-
--k kind... license kinds
--c condition... license conditions
--p package... license package name
--n notice... license notice file
--d dependency... license metadata file dependency
--t target... targets
--m target:installed... map dependent targets to their installed names
--is_container preserved dependent target name when given
--o outfile output file
-"
-
-# Global flag variables
-license_kinds=
-license_conditions=
-license_package_name=
-license_notice=
-license_deps=
-targets=
-installmap=
-is_container=false
-ofile=
-
-# Global variables
-depfiles=" "
-effective_conditions=
-
-
-# Exits with a message.
-#
-# When the exit status is 2, assumes a usage error and outputs the usage message
-# to stderr before outputting the specific error message to stderr.
-#
-# Parameters:
-# Optional numeric exit status (defaults to 2, i.e. a usage error.)
-# Remaining args treated as an error message sent to stderr.
-die() {
- lstatus=2
- case "${1:-}" in *[^0-9]*) ;; *) lstatus="$1"; shift ;; esac
- case "${lstatus}" in 2) echo "${USAGE}" >&2; echo >&2 ;; esac
- if [ -n "$*" ]; then
- echo -e "$*\n" >&2
- fi
- exit $lstatus
-}
-
-
-# Sets the flag variables based on the command-line.
-#
-# invoke with: process_args "$@"
-process_args() {
- lcurr_flag=
- while [ "$#" -gt '0' ]; do
- case "${1}" in
- -h)
- echo "${USAGE}"
- exit 0
- ;;
- -k)
- lcurr_flag=kind
- ;;
- -c)
- lcurr_flag=condition
- ;;
- -p)
- lcurr_flag=package
- ;;
- -n)
- lcurr_flag=notice
- ;;
- -d)
- lcurr_flag=dependency
- ;;
- -t)
- lcurr_flag=target
- ;;
- -m)
- lcurr_flag=installmap
- ;;
- -o)
- lcurr_flag=ofile
- ;;
- -is_container)
- lcurr_flag=
- is_container=true
- ;;
- -*)
- die "Unknown flag: \"${1}\""
- ;;
- *)
- case "${lcurr_flag}" in
- kind)
- license_kinds="${license_kinds}${license_kinds:+ }${1}"
- ;;
- condition)
- license_conditions="${license_conditions}${license_conditions:+ }${1}"
- ;;
- package)
- license_package_name="${license_package_name}${license_package_name:+ }${1}"
- ;;
- notice)
- license_notice="${license_notice}${license_notice:+ }${1}"
- ;;
- dependency)
- license_deps="${license_deps}${license_deps:+ }${1}"
- ;;
- target)
- targets="${targets}${targets:+ }${1}"
- ;;
- installmap)
- installmap="${installmap}${installmap:+ }${1}"
- ;;
- ofile)
- if [ -n "${ofile}" ]; then
- die "Output file -o appears twice as \"${ofile}\" and \"${1}\""
- fi
- ofile="${1}"
- ;;
- *)
- die "Must precede argument \"${1}\" with type flag."
- ;;
- esac
- ;;
- esac
- shift
- done
-}
-
-# Reads a license metadata file from stdin, and outputs the named dependencies.
-#
-# No parameters.
-extract_deps() {
- awk '$1 == "dep_name:" { sub(/^"/, "", $2); sub(/"$/, "", $2); print $2; }'
-}
-
-# Populates the depfiles variable identifying dependency files.
-#
-# Starting with the dependencies enumerated in license_deps, calculates the
-# transitive closure of all dependencies.
-#
-# Dependency names ending in .meta_module indirectly reference license
-# metadata with 1 license metadata filename per line.
-#
-# No parameters; no output.
-read_deps() {
- lnewdeps=
- for d in ${license_deps}; do
- case "${d}" in
- *.meta_module)
- lnewdeps="${lnewdeps}${lnewdeps:+ }"$(cat "${d}") ;;
- *)
- lnewdeps="${lnewdeps}${lnewdeps:+ }${d}" ;;
- esac
- done
- lnewdeps=$(echo "${lnewdeps}" | tr ' ' '\n' | sort -u)
- lalldeps=
- ldeps=
- lmod=
- ldep=
- while [ "${#lnewdeps}" -gt '0' ]; do
- ldeps="${lnewdeps}"
- lnewdeps=
- for ldep in ${ldeps}; do
- depfiles="${depfiles}${ldep} "
- lalldeps="${lalldeps}${lalldeps:+ }"$(cat "${ldep}" | extract_deps)
- done
- lalldeps=$(for d in ${lalldeps}; do echo "${d}"; done | sort -u)
- for d in ${lalldeps}; do
- ldeps="${d}"
- case "${d}" in *.meta_module) ldeps=$(cat "${d}") ;; esac
- for lmod in ${ldeps}; do
- if ! expr "${depfiles}" : ".* ${lmod} .*" >/dev/null 2>&1; then
- lnewdeps="${lnewdeps}${lnewdeps:+ }${lmod}"
- fi
- done
- done
- lalldeps=
- done
-}
-
-# Returns the effective license conditions for the current license metadata.
-#
-# If a module is restricted or links in a restricted module, the effective
-# license has a restricted condition.
-calculate_effective_conditions() {
- lconditions="${license_conditions}"
- case "${license_conditions}" in
- *restricted*) : do nothing ;;
- *)
- for d in ${depfiles}; do
- if cat "${d}" | egrep -q 'effective_condition\s*:.*restricted' ; then
- lconditions="${lconditions}${lconditions:+ }restricted"
- break
- fi
- done
- ;;
- esac
- echo "${lconditions}"
-}
-
-
-process_args "$@"
-
-if [ -n "${ofile}" ]; then
- # truncate the output file before appending results
- : >"${ofile}"
-else
- ofile=/dev/stdout
-fi
-
-# spit out the license metadata file content
-(
- echo 'license_package_name: "'${license_package_name}'"'
- for kind in ${license_kinds}; do
- echo 'license_kind: "'${kind}'"'
- done
- for condition in ${license_conditions}; do
- echo 'license_condition: "'${condition}'"'
- done
- for f in ${license_notice}; do
- echo 'license_text: "'${f}'"'
- done
- echo "is_container: ${is_container}"
- for t in ${targets}; do
- echo 'target: "'${t}'"'
- done
- for m in ${installmap}; do
- echo 'install_map: "'${m}'"'
- done
-) >>"${ofile}"
-read_deps
-effective_conditions=$(calculate_effective_conditions)
-for condition in ${effective_conditions}; do
- echo 'effective_condition: "'${condition}'"'
-done >>"${ofile}"
-for dep in ${depfiles}; do
- echo 'dep {'
- cat "${dep}" | \
- awk -v name="${dep}" '
- function strip_type() {
- $1 = ""
- sub(/^\s*/, "")
- }
- BEGIN {
- print " dep_name: " name
- }
- $1 == "license_package_name:" {
- strip_type()
- print " dep_package_name: "$0
- }
- $1 == "dep_name:" {
- print " dep_sub_dep: "$2
- }
- $1 == "license_kind:" {
- print " dep_license_kind: "$2
- }
- $1 == "license_condition:" {
- print " dep_license_condition: "$2
- }
- $1 == "is_container:" {
- print " dep_is_container: "$2
- }
- $1 == "license_text:" {
- strip_type()
- print " dep_license_text: "$0
- }
- $1 == "target:" {
- print " dep_target: "$2
- }
- $1 == "install_map:" {
- print " dep_install_map: "$2
- }
- '
- # The restricted license kind is contagious to all linked dependencies.
- dep_conditions=$(echo $(
- cat "${dep}" | awk '
- $1 == "effective_condition:" {
- $1 = ""
- sub(/^\s*/, "")
- gsub(/"/, "")
- print
- }
- '
- ))
- for condition in ${dep_conditions}; do
- echo ' dep_effective_condition: "'${condition}'"'
- done
- if ! ${is_container}; then
- case "${dep_conditions}" in
- *restricted*) : already restricted -- nothing to inherit ;;
- *)
- case "${effective_conditions}" in
- *restricted*)
- # "contagious" restricted infects everything linked to restricted
- echo ' dep_effective_condition: "restricted"'
- ;;
- esac
- ;;
- esac
- fi
- echo '}'
-done >>"${ofile}"
diff --git a/tools/canoninja/README.md b/tools/canoninja/README.md
new file mode 100644
index 0000000..506acf7
--- /dev/null
+++ b/tools/canoninja/README.md
@@ -0,0 +1,151 @@
+# Ninja File Canonicalizer
+
+Suppose we have a tool that generates a Ninja file from some other description (think Kati and makefiles), and during
+the testing we discovered a regression. Furthermore, suppose that the generated Ninja file is large (think millions of
+lines). And, the new Ninja file has build statements and rules in a slightly different order. As the tool generates the
+rule names, the real differences in the output of the `diff` command are drowned in noise. Enter Canoninja.
+
+Canoninja renames each Ninja rule to the hash of its contents. After that, we can just sort the build statements, and a
+simple `comm` command immediately reveal the essential difference between the files.
+
+## Example
+
+Consider the following makefile
+
+```makefile
+second :=
+first: foo
+foo:
+ @echo foo
+second: bar
+bar:
+ @echo bar
+```
+
+Depending on Kati version converting it to Ninja file will yield either:
+
+```
+$ cat /tmp/1.ninja
+# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build first: phony foo
+rule rule0
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: rule0
+build second: phony bar
+rule rule1
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: rule1
+
+default first
+```
+
+or
+
+```
+$ cat 2.ninja
+# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build second: phony bar
+rule rule0
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: rule0
+build first: phony foo
+rule rule1
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: rule1
+
+default first
+```
+
+This is a quirk in Kati, see https://github.com/google/kati/issues/238
+
+Trying to find out the difference between the targets even after sorting them isn't too helpful:
+
+```
+diff <(grep '^build' /tmp/1.ninja|sort) <(grep '^build' /tmp/2.ninja | sort)
+1c1
+< build bar: rule1
+---
+> build bar: rule0
+3c3
+< build foo: rule0
+---
+> build foo: rule1
+```
+
+However, running these files through `canoninja` yields
+
+```
+$ canoninja /tmp/1.ninja
+# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build first: phony foo
+rule R2f9981d3c152fc255370dc67028244f7bed72a03
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: R2f9981d3c152fc255370dc67028244f7bed72a03
+build second: phony bar
+rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+
+default first
+```
+
+and
+
+```
+~/go/bin/canoninja /tmp/2.ninja
+# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build second: phony bar
+rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+build first: phony foo
+rule R2f9981d3c152fc255370dc67028244f7bed72a03
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: R2f9981d3c152fc255370dc67028244f7bed72a03
+
+default first
+```
+
+and when we extract only build statements and sort them, we see that both Ninja files define the same graph:
+
+```shell
+$ diff <(~/go/bin/canoninja /tmp/1.ninja | grep '^build' | sort) \
+ <(~/go/bin/canoninja /tmp/2.ninja | grep '^build' | sort)
+```
+
+# Todo
+
+* Optionally output only the build statements, optionally sorted
+* Handle continuation lines correctly
diff --git a/tools/canoninja/canoninja.go b/tools/canoninja/canoninja.go
new file mode 100644
index 0000000..681a694
--- /dev/null
+++ b/tools/canoninja/canoninja.go
@@ -0,0 +1,130 @@
+package canoninja
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "encoding/hex"
+ "fmt"
+ "io"
+)
+
+var (
+ rulePrefix = []byte("rule ")
+ buildPrefix = []byte("build ")
+ phonyRule = []byte("phony")
+)
+
+func Generate(path string, buffer []byte, sink io.Writer) error {
+ // Break file into lines
+ from := 0
+ var lines [][]byte
+ for from < len(buffer) {
+ line := getLine(buffer[from:])
+ lines = append(lines, line)
+ from += len(line)
+ }
+
+ // FOr each rule, calculate and remember its digest
+ ruleDigest := make(map[string]string)
+ for i := 0; i < len(lines); {
+ if bytes.HasPrefix(lines[i], rulePrefix) {
+ // Find ruleName
+ rn := ruleName(lines[i])
+ if len(rn) == 0 {
+ return fmt.Errorf("%s:%d: rule name is missing or on the next line", path, i+1)
+ }
+ sRuleName := string(rn)
+ if _, ok := ruleDigest[sRuleName]; ok {
+ return fmt.Errorf("%s:%d: the rule %s has been already defined", path, i+1, sRuleName)
+ }
+ // Calculate rule text digest as a digests of line digests.
+ var digests []byte
+ doDigest := func(b []byte) {
+ h := sha1.New()
+ h.Write(b)
+ digests = h.Sum(digests)
+
+ }
+ // For the first line, digest everything after rule's name
+ doDigest(lines[i][cap(lines[i])+len(rn)-cap(rn):])
+ for i++; i < len(lines) && lines[i][0] == ' '; i++ {
+ doDigest(lines[i])
+ }
+ h := sha1.New()
+ h.Write(digests)
+ ruleDigest[sRuleName] = "R" + hex.EncodeToString(h.Sum(nil))
+
+ } else {
+ i++
+ }
+ }
+
+ // Rewrite rule names.
+ for i, line := range lines {
+ if bytes.HasPrefix(line, buildPrefix) {
+ brn := getBuildRuleName(line)
+ if bytes.Equal(brn, phonyRule) {
+ sink.Write(line)
+ continue
+ }
+ if len(brn) == 0 {
+ return fmt.Errorf("%s:%d: build statement lacks rule name", path, i+1)
+ }
+ sink.Write(line[0 : cap(line)-cap(brn)])
+ if digest, ok := ruleDigest[string(brn)]; ok {
+ sink.Write([]byte(digest))
+ } else {
+ return fmt.Errorf("%s:%d: no rule for this build target", path, i+1)
+ }
+ sink.Write(line[cap(line)+len(brn)-cap(brn):])
+ } else if bytes.HasPrefix(line, rulePrefix) {
+ rn := ruleName(line)
+ // Write everything before it
+ sink.Write(line[0 : cap(line)-cap(rn)])
+ sink.Write([]byte(ruleDigest[string(rn)]))
+ sink.Write(line[cap(line)+len(rn)-cap(rn):])
+ } else {
+ //goland:noinspection GoUnhandledErrorResult
+ sink.Write(line)
+ }
+ }
+ return nil
+}
+
+func getLine(b []byte) []byte {
+ if n := bytes.IndexByte(b, '\n'); n >= 0 {
+ return b[:n+1]
+ }
+ return b
+}
+
+// Returns build statement's rule name
+func getBuildRuleName(line []byte) []byte {
+ n := bytes.IndexByte(line, ':')
+ if n <= 0 {
+ return nil
+ }
+ ruleName := line[n+1:]
+ if ruleName[0] == ' ' {
+ ruleName = bytes.TrimLeft(ruleName, " ")
+ }
+ if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 {
+ ruleName = ruleName[0:n]
+ }
+ return ruleName
+}
+
+// Returns rule statement's rule name
+func ruleName(lineAfterRule []byte) []byte {
+ ruleName := lineAfterRule[len(rulePrefix):]
+ if len(ruleName) == 0 {
+ return ruleName
+ }
+ if ruleName[0] == ' ' {
+ ruleName = bytes.TrimLeft(ruleName, " ")
+ }
+ if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 {
+ ruleName = ruleName[0:n]
+ }
+ return ruleName
+}
diff --git a/tools/canoninja/canoninja_test.go b/tools/canoninja/canoninja_test.go
new file mode 100644
index 0000000..3c45f8c
--- /dev/null
+++ b/tools/canoninja/canoninja_test.go
@@ -0,0 +1,47 @@
+package canoninja
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestGenerate(t *testing.T) {
+ tests := []struct {
+ name string
+ in []byte
+ wantSink string
+ wantErr bool
+ }{
+ {
+ name: "1",
+ in: []byte(`
+rule rule1
+ abcd
+rule rule2
+ abcd
+build x: rule1
+`),
+ wantSink: `
+rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b
+ abcd
+rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b
+ abcd
+build x: R9c97aba7f61994be6862f5ea9a62d26130c7f48b
+`,
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ sink := &bytes.Buffer{}
+ err := Generate("<file>", tt.in, sink)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if gotSink := sink.String(); gotSink != tt.wantSink {
+ t.Errorf("Generate() gotSink = %v, want %v", gotSink, tt.wantSink)
+ }
+ })
+ }
+}
diff --git a/tools/canoninja/cmd/canoninja.go b/tools/canoninja/cmd/canoninja.go
new file mode 100644
index 0000000..71802ef
--- /dev/null
+++ b/tools/canoninja/cmd/canoninja.go
@@ -0,0 +1,36 @@
+package main
+
+/*
+ Canoninja reads a Ninja file and changes the rule names to be the digest of the rule contents.
+ Feed it to a filter that extracts only build statements, sort them, and you will have a crude
+ but effective tool to find small differences between two Ninja files.
+*/
+
+import (
+ "canoninja"
+ "flag"
+ "fmt"
+ "os"
+)
+
+func main() {
+ flag.Parse()
+ files := flag.Args()
+ if len(files) == 0 {
+ files = []string{"/dev/stdin"}
+ }
+ rc := 0
+ for _, f := range files {
+ if buffer, err := os.ReadFile(f); err == nil {
+ err = canoninja.Generate(f, buffer, os.Stdout)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ rc = 1
+ }
+ } else {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", f, err)
+ rc = 1
+ }
+ }
+ os.Exit(rc)
+}
diff --git a/tools/canoninja/go.mod b/tools/canoninja/go.mod
new file mode 100644
index 0000000..c5a924e
--- /dev/null
+++ b/tools/canoninja/go.mod
@@ -0,0 +1 @@
+module canoninja
diff --git a/tools/check_elf_file.py b/tools/check_elf_file.py
index 1ff8e65..045cb1d 100755
--- a/tools/check_elf_file.py
+++ b/tools/check_elf_file.py
@@ -195,10 +195,12 @@
@classmethod
def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):
"""Run llvm-readobj and parse the output."""
- proc = subprocess.Popen(
- [llvm_readobj, '-dynamic-table', '-dyn-symbols', elf_file_path],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ cmd = [llvm_readobj, '--dynamic-table', '--dyn-symbols', elf_file_path]
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, _ = proc.communicate()
+ rc = proc.returncode
+ if rc != 0:
+ raise subprocess.CalledProcessError(rc, cmd, out)
lines = out.splitlines()
return cls._parse_llvm_readobj(elf_file_path, header, lines)
diff --git a/tools/compliance/Android.bp b/tools/compliance/Android.bp
new file mode 100644
index 0000000..afb3080
--- /dev/null
+++ b/tools/compliance/Android.bp
@@ -0,0 +1,89 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+ name: "checkshare",
+ srcs: ["cmd/checkshare.go"],
+ deps: ["compliance-module"],
+ testSrcs: ["cmd/checkshare_test.go"],
+}
+
+blueprint_go_binary {
+ name: "listshare",
+ srcs: ["cmd/listshare.go"],
+ deps: ["compliance-module"],
+ testSrcs: ["cmd/listshare_test.go"],
+}
+
+blueprint_go_binary {
+ name: "dumpgraph",
+ srcs: ["cmd/dumpgraph.go"],
+ deps: ["compliance-module"],
+ testSrcs: ["cmd/dumpgraph_test.go"],
+}
+
+blueprint_go_binary {
+ name: "dumpresolutions",
+ srcs: ["cmd/dumpresolutions.go"],
+ deps: ["compliance-module"],
+ testSrcs: ["cmd/dumpresolutions_test.go"],
+}
+
+bootstrap_go_package {
+ name: "compliance-module",
+ srcs: [
+ "actionset.go",
+ "condition.go",
+ "conditionset.go",
+ "doc.go",
+ "graph.go",
+ "policy/policy.go",
+ "policy/resolve.go",
+ "policy/resolvenotices.go",
+ "policy/resolveshare.go",
+ "policy/resolveprivacy.go",
+ "policy/shareprivacyconflicts.go",
+ "policy/shipped.go",
+ "policy/walk.go",
+ "readgraph.go",
+ "resolution.go",
+ "resolutionset.go",
+ ],
+ testSrcs: [
+ "condition_test.go",
+ "conditionset_test.go",
+ "readgraph_test.go",
+ "policy/policy_test.go",
+ "policy/resolve_test.go",
+ "policy/resolvenotices_test.go",
+ "policy/resolveshare_test.go",
+ "policy/resolveprivacy_test.go",
+ "policy/shareprivacyconflicts_test.go",
+ "policy/shipped_test.go",
+ "policy/walk_test.go",
+ "resolutionset_test.go",
+ "test_util.go",
+ ],
+ deps: [
+ "golang-protobuf-proto",
+ "golang-protobuf-encoding-prototext",
+ "license_metadata_proto",
+ ],
+ pkgPath: "compliance",
+}
diff --git a/tools/compliance/actionset.go b/tools/compliance/actionset.go
new file mode 100644
index 0000000..656c5de
--- /dev/null
+++ b/tools/compliance/actionset.go
@@ -0,0 +1,119 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// actionSet maps `actOn` target nodes to the license conditions the actions resolve.
+type actionSet map[*TargetNode]*LicenseConditionSet
+
+// String returns a string representation of the set.
+func (as actionSet) String() string {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "{")
+ osep := ""
+ for actsOn, cs := range as {
+ cl := cs.AsList()
+ sort.Sort(cl)
+ fmt.Fprintf(&sb, "%s%s -> %s", osep, actsOn.name, cl.String())
+ osep = ", "
+ }
+ fmt.Fprintf(&sb, "}")
+ return sb.String()
+}
+
+// byName returns the subset of `as` actions where the condition name is in `names`.
+func (as actionSet) byName(names ConditionNames) actionSet {
+ result := make(actionSet)
+ for actsOn, cs := range as {
+ bn := cs.ByName(names)
+ if bn.IsEmpty() {
+ continue
+ }
+ result[actsOn] = bn
+ }
+ return result
+}
+
+// byActsOn returns the subset of `as` where `actsOn` is in the `reachable` target node set.
+func (as actionSet) byActsOn(reachable *TargetNodeSet) actionSet {
+ result := make(actionSet)
+ for actsOn, cs := range as {
+ if !reachable.Contains(actsOn) || cs.IsEmpty() {
+ continue
+ }
+ result[actsOn] = cs.Copy()
+ }
+ return result
+}
+
+// copy returns another actionSet with the same value as `as`
+func (as actionSet) copy() actionSet {
+ result := make(actionSet)
+ for actsOn, cs := range as {
+ if cs.IsEmpty() {
+ continue
+ }
+ result[actsOn] = cs.Copy()
+ }
+ return result
+}
+
+// addSet adds all of the actions of `other` if not already present.
+func (as actionSet) addSet(other actionSet) {
+ for actsOn, cs := range other {
+ as.add(actsOn, cs)
+ }
+}
+
+// add makes the action on `actsOn` to resolve the conditions in `cs` a member of the set.
+func (as actionSet) add(actsOn *TargetNode, cs *LicenseConditionSet) {
+ if acs, ok := as[actsOn]; ok {
+ acs.AddSet(cs)
+ } else {
+ as[actsOn] = cs.Copy()
+ }
+}
+
+// addCondition makes the action on `actsOn` to resolve `lc` a member of the set.
+func (as actionSet) addCondition(actsOn *TargetNode, lc LicenseCondition) {
+ if _, ok := as[actsOn]; !ok {
+ as[actsOn] = newLicenseConditionSet()
+ }
+ as[actsOn].Add(lc)
+}
+
+// isEmpty returns true if no action to resolve a condition exists.
+func (as actionSet) isEmpty() bool {
+ for _, cs := range as {
+ if !cs.IsEmpty() {
+ return false
+ }
+ }
+ return true
+}
+
+// conditions returns the set of conditions resolved by the action set.
+func (as actionSet) conditions() *LicenseConditionSet {
+ result := newLicenseConditionSet()
+ for _, cs := range as {
+ result.AddSet(cs)
+ }
+ return result
+}
diff --git a/tools/compliance/cmd/checkshare.go b/tools/compliance/cmd/checkshare.go
new file mode 100644
index 0000000..efac8dc
--- /dev/null
+++ b/tools/compliance/cmd/checkshare.go
@@ -0,0 +1,114 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "compliance"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+)
+
+func init() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
+
+Reports on stderr any targets where policy says that the source both
+must and must not be shared. The error report indicates the target, the
+license condition with origin that has a source privacy policy, and the
+license condition with origin that has a source sharing policy.
+
+Any given target may appear multiple times with different combinations
+of conflicting license conditions.
+
+If all the source code that policy says must be shared may be shared,
+outputs "PASS" to stdout and exits with status 0.
+
+If policy says any source must both be shared and not be shared,
+outputs "FAIL" to stdout and exits with status 1.
+`, filepath.Base(os.Args[0]))
+ }
+}
+
+var (
+ failConflicts = fmt.Errorf("conflicts")
+ failNoneRequested = fmt.Errorf("\nNo metadata files requested")
+ failNoLicenses = fmt.Errorf("No licenses")
+)
+
+
+// byError orders conflicts by error string
+type byError []compliance.SourceSharePrivacyConflict
+
+func (l byError) Len() int { return len(l) }
+func (l byError) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+func (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() }
+
+func main() {
+ flag.Parse()
+
+ // Must specify at least one root target.
+ if flag.NArg() == 0 {
+ flag.Usage()
+ os.Exit(2)
+ }
+
+ err := checkShare(os.Stdout, os.Stderr, flag.Args()...)
+ if err != nil {
+ if err != failConflicts {
+ if err == failNoneRequested {
+ flag.Usage()
+ }
+ fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+ }
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
+
+// checkShare implements the checkshare utility.
+func checkShare(stdout, stderr io.Writer, files ...string) error {
+
+ if len(files) < 1 {
+ return failNoneRequested
+ }
+
+ // Read the license graph from the license metadata files (*.meta_lic).
+ licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
+ if err != nil {
+ return fmt.Errorf("Unable to read license metadata file(s) %q: %w\n", files, err)
+ }
+ if licenseGraph == nil {
+ return failNoLicenses
+ }
+
+ // Apply policy to find conflicts and report them to stderr lexicographically ordered.
+ conflicts := compliance.ConflictingSharedPrivateSource(licenseGraph)
+ sort.Sort(byError(conflicts))
+ for _, conflict := range conflicts {
+ fmt.Fprintln(stderr, conflict.Error())
+ }
+
+ // Indicate pass or fail on stdout.
+ if len(conflicts) > 0 {
+ fmt.Fprintln(stdout, "FAIL")
+ return failConflicts
+ }
+ fmt.Fprintln(stdout, "PASS")
+ return nil
+}
diff --git a/tools/compliance/cmd/checkshare_test.go b/tools/compliance/cmd/checkshare_test.go
new file mode 100644
index 0000000..8ea7748
--- /dev/null
+++ b/tools/compliance/cmd/checkshare_test.go
@@ -0,0 +1,299 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+type outcome struct {
+ target string
+ privacyOrigin string
+ privacyCondition string
+ shareOrigin string
+ shareCondition string
+}
+
+func (o *outcome) String() string {
+ return fmt.Sprintf("%s %s from %s and must share from %s %s",
+ o.target, o.privacyCondition, o.privacyOrigin, o.shareCondition, o.shareOrigin)
+}
+
+type outcomeList []*outcome
+
+func (ol outcomeList) String() string {
+ result := ""
+ for _, o := range ol {
+ result = result + o.String() + "\n"
+ }
+ return result
+}
+
+func Test(t *testing.T) {
+ tests := []struct {
+ condition string
+ name string
+ roots []string
+ expectedStdout string
+ expectedOutcomes outcomeList
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin2.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin2.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin2.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "restricted",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin2.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedStdout: "FAIL",
+ expectedOutcomes: outcomeList{
+ &outcome{
+ target: "testdata/proprietary/bin/bin2.meta_lic",
+ privacyOrigin: "testdata/proprietary/bin/bin2.meta_lic",
+ privacyCondition: "proprietary",
+ shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
+ shareCondition: "restricted",
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedStdout: "FAIL",
+ expectedOutcomes: outcomeList{
+ &outcome{
+ target: "testdata/proprietary/bin/bin2.meta_lic",
+ privacyOrigin: "testdata/proprietary/bin/bin2.meta_lic",
+ privacyCondition: "proprietary",
+ shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
+ shareCondition: "restricted",
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedStdout: "FAIL",
+ expectedOutcomes: outcomeList{
+ &outcome{
+ target: "testdata/proprietary/lib/liba.so.meta_lic",
+ privacyOrigin: "testdata/proprietary/lib/liba.so.meta_lic",
+ privacyCondition: "proprietary",
+ shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
+ shareCondition: "restricted",
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin2.meta_lic", "lib/libb.so.meta_lic"},
+ expectedStdout: "FAIL",
+ expectedOutcomes: outcomeList{
+ &outcome{
+ target: "testdata/proprietary/bin/bin2.meta_lic",
+ privacyOrigin: "testdata/proprietary/bin/bin2.meta_lic",
+ privacyCondition: "proprietary",
+ shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
+ shareCondition: "restricted",
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedStdout: "PASS",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+ err := checkShare(stdout, stderr, rootFiles...)
+ if err != nil && err != failConflicts {
+ t.Fatalf("checkshare: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ var actualStdout string
+ for _, s := range strings.Split(stdout.String(), "\n") {
+ ts := strings.TrimLeft(s, " \t")
+ if len(ts) < 1 {
+ continue
+ }
+ if 0 < len(actualStdout) {
+ t.Errorf("checkshare: unexpected multiple output lines %q, want %q", actualStdout+"\n"+ts, tt.expectedStdout)
+ }
+ actualStdout = ts
+ }
+ if actualStdout != tt.expectedStdout {
+ t.Errorf("checkshare: unexpected stdout %q, want %q", actualStdout, tt.expectedStdout)
+ }
+ errList := strings.Split(stderr.String(), "\n")
+ actualOutcomes := make(outcomeList, 0, len(errList))
+ for _, cstring := range errList {
+ ts := strings.TrimLeft(cstring, " \t")
+ if len(ts) < 1 {
+ continue
+ }
+ cFields := strings.Split(ts, " ")
+ actualOutcomes = append(actualOutcomes, &outcome{
+ target: cFields[0],
+ privacyOrigin: cFields[3],
+ privacyCondition: cFields[1],
+ shareOrigin: cFields[9],
+ shareCondition: cFields[8],
+ })
+ }
+ if len(actualOutcomes) != len(tt.expectedOutcomes) {
+ t.Errorf("checkshare: unexpected got %d outcomes %s, want %d outcomes %s",
+ len(actualOutcomes), actualOutcomes, len(tt.expectedOutcomes), tt.expectedOutcomes)
+ return
+ }
+ for i := range actualOutcomes {
+ if actualOutcomes[i].String() != tt.expectedOutcomes[i].String() {
+ t.Errorf("checkshare: unexpected outcome #%d, got %q, want %q",
+ i+1, actualOutcomes[i], tt.expectedOutcomes[i])
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/cmd/dumpgraph.go b/tools/compliance/cmd/dumpgraph.go
new file mode 100644
index 0000000..1ee63b2
--- /dev/null
+++ b/tools/compliance/cmd/dumpgraph.go
@@ -0,0 +1,178 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "compliance"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+var (
+ graphViz = flag.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.")
+ labelConditions = flag.Bool("label_conditions", false, "Whether to label target nodes with conditions.")
+ stripPrefix = flag.String("strip_prefix", "", "Prefix to remove from paths. i.e. path to root")
+
+ failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
+ failNoLicenses = fmt.Errorf("No licenses found")
+)
+
+type context struct {
+ graphViz bool
+ labelConditions bool
+ stripPrefix string
+}
+
+func init() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
+
+Outputs space-separated Target Dependency Annotations tuples for each
+edge in the license graph. When -dot flag given, outputs the nodes and
+edges in graphViz directed graph format.
+
+In plain text mode, multiple values within a field are colon-separated.
+e.g. multiple annotations appear as annotation1:annotation2:annotation3
+or when -label_conditions is requested, Target and Dependency become
+target:condition1:condition2 etc.
+
+Options:
+`, filepath.Base(os.Args[0]))
+ flag.PrintDefaults()
+ }
+}
+
+func main() {
+ flag.Parse()
+
+ // Must specify at least one root target.
+ if flag.NArg() == 0 {
+ flag.Usage()
+ os.Exit(2)
+ }
+
+ ctx := &context{*graphViz, *labelConditions, *stripPrefix}
+
+ err := dumpGraph(ctx, os.Stdout, os.Stderr, flag.Args()...)
+ if err != nil {
+ if err == failNoneRequested {
+ flag.Usage()
+ }
+ fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
+
+// dumpGraph implements the dumpgraph utility.
+func dumpGraph(ctx *context, stdout, stderr io.Writer, files ...string) error {
+ if len(files) < 1 {
+ return failNoneRequested
+ }
+
+ // Read the license graph from the license metadata files (*.meta_lic).
+ licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
+ if err != nil {
+ return fmt.Errorf("Unable to read license metadata file(s) %q: %w\n", files, err)
+ }
+ if licenseGraph == nil {
+ return failNoLicenses
+ }
+
+ // Sort the edges of the graph.
+ edges := licenseGraph.Edges()
+ sort.Sort(edges)
+
+ // nodes maps license metadata file names to graphViz node names when ctx.graphViz is true.
+ var nodes map[string]string
+ n := 0
+
+ // targetOut calculates the string to output for `target` separating conditions as needed using `sep`.
+ targetOut := func(target *compliance.TargetNode, sep string) string {
+ tOut := strings.TrimPrefix(target.Name(), ctx.stripPrefix)
+ if ctx.labelConditions {
+ conditions := target.LicenseConditions().Names()
+ sort.Strings(conditions)
+ if len(conditions) > 0 {
+ tOut += sep + strings.Join(conditions, sep)
+ }
+ }
+ return tOut
+ }
+
+ // makeNode maps `target` to a graphViz node name.
+ makeNode := func(target *compliance.TargetNode) {
+ tName := target.Name()
+ if _, ok := nodes[tName]; !ok {
+ nodeName := fmt.Sprintf("n%d", n)
+ nodes[tName] = nodeName
+ fmt.Fprintf(stdout, "\t%s [label=\"%s\"];\n", nodeName, targetOut(target, "\\n"))
+ n++
+ }
+ }
+
+ // If graphviz output, map targets to node names, and start the directed graph.
+ if ctx.graphViz {
+ nodes = make(map[string]string)
+ targets := licenseGraph.Targets()
+ sort.Sort(targets)
+
+ fmt.Fprintf(stdout, "strict digraph {\n\trankdir=RL;\n")
+ for _, target := range targets {
+ makeNode(target)
+ }
+ }
+
+ // Print the sorted edges to stdout ...
+ for _, e := range edges {
+ // sort the annotations for repeatability/stability
+ annotations := e.Annotations().AsList()
+ sort.Strings(annotations)
+
+ tName := e.Target().Name()
+ dName := e.Dependency().Name()
+
+ if ctx.graphViz {
+ // ... one edge per line labelled with \\n-separated annotations.
+ tNode := nodes[tName]
+ dNode := nodes[dName]
+ fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", dNode, tNode, strings.Join(annotations, "\\n"))
+ } else {
+ // ... one edge per line with annotations in a colon-separated tuple.
+ fmt.Fprintf(stdout, "%s %s %s\n", targetOut(e.Target(), ":"), targetOut(e.Dependency(), ":"), strings.Join(annotations, ":"))
+ }
+ }
+
+ // If graphViz output, rank the root nodes together, and complete the directed graph.
+ if ctx.graphViz {
+ fmt.Fprintf(stdout, "\t{rank=same;")
+ for _, f := range files {
+ fName := f
+ if !strings.HasSuffix(fName, ".meta_lic") {
+ fName += ".meta_lic"
+ }
+ if fNode, ok := nodes[fName]; ok {
+ fmt.Fprintf(stdout, " %s", fNode)
+ }
+ }
+ fmt.Fprintf(stdout, "}\n}\n")
+ }
+ return nil
+}
diff --git a/tools/compliance/cmd/dumpgraph_test.go b/tools/compliance/cmd/dumpgraph_test.go
new file mode 100644
index 0000000..b7d66f7
--- /dev/null
+++ b/tools/compliance/cmd/dumpgraph_test.go
@@ -0,0 +1,1258 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func Test_plaintext(t *testing.T) {
+ tests := []struct {
+ condition string
+ name string
+ roots []string
+ ctx context
+ expectedOut []string
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static",
+ "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic",
+ "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic static",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic static",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic static",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic static",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic static",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic",
+ "highest.apex.meta_lic bin/bin1.meta_lic static",
+ "highest.apex.meta_lic bin/bin2.meta_lic static",
+ "highest.apex.meta_lic lib/liba.so.meta_lic static",
+ "highest.apex.meta_lic lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static",
+ "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic",
+ "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static",
+ "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic",
+ "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic static",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic static",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic static",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/application.meta_lic testdata/firstparty/bin/bin3.meta_lic toolchain",
+ "testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic static",
+ "testdata/firstparty/application.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{},
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static",
+ "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic",
+ "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic static",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic static",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic static",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic static",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic static",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic",
+ "highest.apex.meta_lic bin/bin1.meta_lic static",
+ "highest.apex.meta_lic bin/bin2.meta_lic static",
+ "highest.apex.meta_lic lib/liba.so.meta_lic static",
+ "highest.apex.meta_lic lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static",
+ "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic",
+ "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static",
+ },
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static",
+ "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic",
+ "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic",
+ "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic static",
+ "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic static",
+ "testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic static",
+ "testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/application.meta_lic testdata/notice/bin/bin3.meta_lic toolchain",
+ "testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic static",
+ "testdata/notice/application.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic",
+ },
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static",
+ },
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{},
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static",
+ "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic",
+ "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic static",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic static",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic static",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic static",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic",
+ "highest.apex.meta_lic bin/bin1.meta_lic static",
+ "highest.apex.meta_lic bin/bin2.meta_lic static",
+ "highest.apex.meta_lic lib/liba.so.meta_lic static",
+ "highest.apex.meta_lic lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal static",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static",
+ "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic",
+ "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal static",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static",
+ "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic",
+ "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic static",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic static",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/application.meta_lic testdata/reciprocal/bin/bin3.meta_lic toolchain",
+ "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static",
+ "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{},
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic static",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic static",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic static",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic static",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic static",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic",
+ "highest.apex.meta_lic bin/bin1.meta_lic static",
+ "highest.apex.meta_lic bin/bin2.meta_lic static",
+ "highest.apex.meta_lic lib/liba.so.meta_lic static",
+ "highest.apex.meta_lic lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted static",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static",
+ "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic",
+ "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted static",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic static",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic static",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic static",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/application.meta_lic testdata/restricted/bin/bin3.meta_lic toolchain",
+ "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic static",
+ "testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{},
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic static",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic static",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic static",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic static",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic static",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic",
+ "highest.apex.meta_lic bin/bin1.meta_lic static",
+ "highest.apex.meta_lic bin/bin2.meta_lic static",
+ "highest.apex.meta_lic lib/liba.so.meta_lic static",
+ "highest.apex.meta_lic lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary static",
+ "bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted dynamic",
+ "bin/bin2.meta_lic:by_exception_only:proprietary lib/libd.so.meta_lic:notice dynamic",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary static",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic static",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic static",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic static",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic static",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/application.meta_lic testdata/proprietary/bin/bin3.meta_lic toolchain",
+ "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic static",
+ "testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ expectedOut := &bytes.Buffer{}
+ for _, eo := range tt.expectedOut {
+ expectedOut.WriteString(eo)
+ expectedOut.WriteString("\n")
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+ err := dumpGraph(&tt.ctx, stdout, stderr, rootFiles...)
+ if err != nil {
+ t.Fatalf("dumpgraph: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ if stderr.Len() > 0 {
+ t.Errorf("dumpgraph: gotStderr = %v, want none", stderr)
+ }
+ out := stdout.String()
+ expected := expectedOut.String()
+ if out != expected {
+ outList := strings.Split(out, "\n")
+ expectedList := strings.Split(expected, "\n")
+ startLine := 0
+ for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {
+ startLine++
+ }
+ t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
+ out, expected, startLine+1, outList[startLine], expectedList[startLine])
+ }
+ })
+ }
+}
+
+type testContext struct {
+ nextNode int
+ nodes map[string]string
+}
+
+type matcher interface {
+ matchString(*testContext) string
+ typeString() string
+}
+
+type targetMatcher struct {
+ target string
+ conditions []string
+}
+
+func (tm *targetMatcher) matchString(ctx *testContext) string {
+ m := tm.target
+ if len(tm.conditions) > 0 {
+ m += "\\n" + strings.Join(tm.conditions, "\\n")
+ }
+ m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];"
+ return m
+}
+
+func (tm *targetMatcher) typeString() string {
+ return "target"
+}
+
+type edgeMatcher struct {
+ target string
+ dep string
+ annotations []string
+}
+
+func (em *edgeMatcher) matchString(ctx *testContext) string {
+ return ctx.nodes[em.dep] + " -> " + ctx.nodes[em.target] + " [label=\"" + strings.Join(em.annotations, "\\n") + "\"];"
+}
+
+func (tm *edgeMatcher) typeString() string {
+ return "edge"
+}
+
+type getMatcher func(*testContext) matcher
+
+func matchTarget(target string, conditions ...string) getMatcher {
+ return func(ctx *testContext) matcher {
+ ctx.nodes[target] = fmt.Sprintf("n%d", ctx.nextNode)
+ ctx.nextNode++
+ return &targetMatcher{target, append([]string{}, conditions...)}
+ }
+}
+
+func matchEdge(target, dep string, annotations ...string) getMatcher {
+ return func(ctx *testContext) matcher {
+ if _, ok := ctx.nodes[target]; !ok {
+ panic(fmt.Errorf("no node for target %v in %v -> %v [label=\"%s\"];", target, dep, target, strings.Join(annotations, "\\n")))
+ }
+ if _, ok := ctx.nodes[dep]; !ok {
+ panic(fmt.Errorf("no node for dep %v in %v -> %v [label=\"%s\"];", target, dep, target, strings.Join(annotations, "\\n")))
+ }
+ return &edgeMatcher{target, dep, append([]string{}, annotations...)}
+ }
+}
+
+func Test_graphviz(t *testing.T) {
+ tests := []struct {
+ condition string
+ name string
+ roots []string
+ ctx context
+ expectedOut []getMatcher
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/bin/bin1.meta_lic"),
+ matchTarget("testdata/firstparty/bin/bin2.meta_lic"),
+ matchTarget("testdata/firstparty/highest.apex.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libc.a.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
+ matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "notice"),
+ matchTarget("lib/libb.so.meta_lic", "notice"),
+ matchTarget("lib/libc.a.meta_lic", "notice"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/bin/bin1.meta_lic"),
+ matchTarget("testdata/firstparty/bin/bin2.meta_lic"),
+ matchTarget("testdata/firstparty/container.zip.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libc.a.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
+ matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/application.meta_lic"),
+ matchTarget("testdata/firstparty/bin/bin3.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
+ matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/bin/bin3.meta_lic", "toolchain"),
+ matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/bin/bin1.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libc.a.meta_lic"),
+ matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{matchTarget("testdata/firstparty/lib/libd.so.meta_lic")},
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/bin/bin1.meta_lic"),
+ matchTarget("testdata/notice/bin/bin2.meta_lic"),
+ matchTarget("testdata/notice/highest.apex.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libb.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libc.a.meta_lic"),
+ matchTarget("testdata/notice/lib/libd.so.meta_lic"),
+ matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "notice"),
+ matchTarget("lib/libb.so.meta_lic", "notice"),
+ matchTarget("lib/libc.a.meta_lic", "notice"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/bin/bin1.meta_lic"),
+ matchTarget("testdata/notice/bin/bin2.meta_lic"),
+ matchTarget("testdata/notice/container.zip.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libb.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libc.a.meta_lic"),
+ matchTarget("testdata/notice/lib/libd.so.meta_lic"),
+ matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/application.meta_lic"),
+ matchTarget("testdata/notice/bin/bin3.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libb.so.meta_lic"),
+ matchEdge("testdata/notice/application.meta_lic", "testdata/notice/bin/bin3.meta_lic", "toolchain"),
+ matchEdge("testdata/notice/application.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/notice/application.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/bin/bin1.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libc.a.meta_lic"),
+ matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{matchTarget("testdata/notice/lib/libd.so.meta_lic")},
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"),
+ matchTarget("testdata/reciprocal/bin/bin2.meta_lic"),
+ matchTarget("testdata/reciprocal/highest.apex.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
+ matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "reciprocal"),
+ matchTarget("lib/libb.so.meta_lic", "notice"),
+ matchTarget("lib/libc.a.meta_lic", "reciprocal"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"),
+ matchTarget("testdata/reciprocal/bin/bin2.meta_lic"),
+ matchTarget("testdata/reciprocal/container.zip.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
+ matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/application.meta_lic"),
+ matchTarget("testdata/reciprocal/bin/bin3.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
+ matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/bin/bin3.meta_lic", "toolchain"),
+ matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"),
+ matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{matchTarget("testdata/reciprocal/lib/libd.so.meta_lic")},
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/bin/bin1.meta_lic"),
+ matchTarget("testdata/restricted/bin/bin2.meta_lic"),
+ matchTarget("testdata/restricted/highest.apex.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
+ matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
+ matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "restricted"),
+ matchTarget("lib/libb.so.meta_lic", "restricted"),
+ matchTarget("lib/libc.a.meta_lic", "reciprocal"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/bin/bin1.meta_lic"),
+ matchTarget("testdata/restricted/bin/bin2.meta_lic"),
+ matchTarget("testdata/restricted/container.zip.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
+ matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
+ matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/application.meta_lic"),
+ matchTarget("testdata/restricted/bin/bin3.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
+ matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/bin/bin3.meta_lic", "toolchain"),
+ matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/bin/bin1.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
+ matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{matchTarget("testdata/restricted/lib/libd.so.meta_lic")},
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/bin/bin1.meta_lic"),
+ matchTarget("testdata/proprietary/bin/bin2.meta_lic"),
+ matchTarget("testdata/proprietary/highest.apex.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
+ matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "by_exception_only", "proprietary"),
+ matchTarget("lib/libb.so.meta_lic", "restricted"),
+ matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"),
+ matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"),
+ matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/bin/bin1.meta_lic"),
+ matchTarget("testdata/proprietary/bin/bin2.meta_lic"),
+ matchTarget("testdata/proprietary/container.zip.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
+ matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"),
+ matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"),
+ matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "dynamic"),
+ matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "static"),
+ matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "static"),
+ matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/application.meta_lic"),
+ matchTarget("testdata/proprietary/bin/bin3.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
+ matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/bin/bin3.meta_lic", "toolchain"),
+ matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/bin/bin1.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
+ matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"),
+ matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{matchTarget("testdata/proprietary/lib/libd.so.meta_lic")},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ ctx := &testContext{0, make(map[string]string)}
+
+ expectedOut := &bytes.Buffer{}
+ for _, eo := range tt.expectedOut {
+ m := eo(ctx)
+ expectedOut.WriteString(m.matchString(ctx))
+ expectedOut.WriteString("\n")
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+ tt.ctx.graphViz = true
+ err := dumpGraph(&tt.ctx, stdout, stderr, rootFiles...)
+ if err != nil {
+ t.Fatalf("dumpgraph: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ if stderr.Len() > 0 {
+ t.Errorf("dumpgraph: gotStderr = %v, want none", stderr)
+ }
+ outList := strings.Split(stdout.String(), "\n")
+ outLine := 0
+ if outList[outLine] != "strict digraph {" {
+ t.Errorf("dumpgraph: got 1st line %v, want strict digraph {")
+ }
+ outLine++
+ if strings.HasPrefix(strings.TrimLeft(outList[outLine], " \t"), "rankdir") {
+ outLine++
+ }
+ endOut := len(outList)
+ for endOut > 0 && strings.TrimLeft(outList[endOut-1], " \t") == "" {
+ endOut--
+ }
+ if outList[endOut-1] != "}" {
+ t.Errorf("dumpgraph: got last line %v, want }", outList[endOut-1])
+ }
+ endOut--
+ if strings.HasPrefix(strings.TrimLeft(outList[endOut-1], " \t"), "{rank=same") {
+ endOut--
+ }
+ expectedList := strings.Split(expectedOut.String(), "\n")
+ for len(expectedList) > 0 && expectedList[len(expectedList)-1] == "" {
+ expectedList = expectedList[0 : len(expectedList)-1]
+ }
+ matchLine := 0
+
+ for outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], " \t") == expectedList[matchLine] {
+ outLine++
+ matchLine++
+ }
+ if outLine < endOut || matchLine < len(expectedList) {
+ if outLine >= endOut {
+ t.Errorf("dumpgraph: missing lines at end of graph, want %d lines %v", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], "\n"))
+ } else if matchLine >= len(expectedList) {
+ t.Errorf("dumpgraph: unexpected lines at end of graph starting line %d, got %v, want nothing", outLine+1, strings.Join(outList[outLine:], "\n"))
+ } else {
+ t.Errorf("dumpgraph: at line %d, got %v, want %v", outLine+1, strings.Join(outList[outLine:], "\n"), strings.Join(expectedList[matchLine:], "\n"))
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/cmd/dumpresolutions.go b/tools/compliance/cmd/dumpresolutions.go
new file mode 100644
index 0000000..36fbb7d
--- /dev/null
+++ b/tools/compliance/cmd/dumpresolutions.go
@@ -0,0 +1,284 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "compliance"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+var (
+ conditions = newMultiString("c", "License condition to resolve. (may be given multiple times)")
+ graphViz = flag.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.")
+ labelConditions = flag.Bool("label_conditions", false, "Whether to label target nodes with conditions.")
+ stripPrefix = flag.String("strip_prefix", "", "Prefix to remove from paths. i.e. path to root")
+
+ failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
+ failNoLicenses = fmt.Errorf("No licenses found")
+)
+
+type context struct {
+ conditions []string
+ graphViz bool
+ labelConditions bool
+ stripPrefix string
+}
+
+func init() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
+
+Outputs a space-separated Target ActsOn Origin Condition tuple for each
+resolution in the graph. When -dot flag given, outputs nodes and edges
+in graphviz directed graph format.
+
+If one or more '-c condition' conditions are given, outputs the joined
+set of resolutions for all of the conditions. Otherwise, outputs the
+result of the bottom-up and top-down resolve only.
+
+In plain text mode, when '-label_conditions' is requested, the Target
+and Origin have colon-separated license conditions appended:
+i.e. target:condition1:condition2 etc.
+
+Options:
+`, filepath.Base(os.Args[0]))
+ flag.PrintDefaults()
+ }
+}
+
+// newMultiString creates a flag that allows multiple values in an array.
+func newMultiString(name, usage string) *multiString {
+ var f multiString
+ flag.Var(&f, name, usage)
+ return &f
+}
+
+// multiString implements the flag `Value` interface for multiple strings.
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ flag.Parse()
+
+ // Must specify at least one root target.
+ if flag.NArg() == 0 {
+ flag.Usage()
+ os.Exit(2)
+ }
+
+ ctx := &context{
+ conditions: append([]string{}, *conditions...),
+ graphViz: *graphViz,
+ labelConditions: *labelConditions,
+ stripPrefix: *stripPrefix,
+ }
+ err := dumpResolutions(ctx, os.Stdout, os.Stderr, flag.Args()...)
+ if err != nil {
+ if err == failNoneRequested {
+ flag.Usage()
+ }
+ fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
+
+// dumpResolutions implements the dumpresolutions utility.
+func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) error {
+ if len(files) < 1 {
+ return failNoneRequested
+ }
+
+ // Read the license graph from the license metadata files (*.meta_lic).
+ licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
+ if err != nil {
+ return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
+ }
+ if licenseGraph == nil {
+ return failNoLicenses
+ }
+
+ // resolutions will contain the requested set of resolutions.
+ var resolutions *compliance.ResolutionSet
+
+ resolutions = compliance.ResolveTopDownConditions(licenseGraph)
+ if len(ctx.conditions) > 0 {
+ rlist := make([]*compliance.ResolutionSet, 0, len(ctx.conditions))
+ for _, c := range ctx.conditions {
+ rlist = append(rlist, compliance.WalkResolutionsForCondition(licenseGraph, resolutions, compliance.ConditionNames{c}))
+ }
+ if len(rlist) == 1 {
+ resolutions = rlist[0]
+ } else {
+ resolutions = compliance.JoinResolutionSets(rlist...)
+ }
+ }
+
+ // nodes maps license metadata file names to graphViz node names when graphViz requested.
+ nodes := make(map[string]string)
+ n := 0
+
+ // targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed.
+ targetOut := func(target *compliance.TargetNode, sep string) string {
+ tOut := strings.TrimPrefix(target.Name(), ctx.stripPrefix)
+ if ctx.labelConditions {
+ conditions := make([]string, 0, target.LicenseConditions().Count())
+ for _, lc := range target.LicenseConditions().AsList() {
+ conditions = append(conditions, lc.Name())
+ }
+ sort.Strings(conditions)
+ if len(conditions) > 0 {
+ tOut += sep + strings.Join(conditions, sep)
+ }
+ }
+ return tOut
+ }
+
+ // makeNode maps `target` to a graphViz node name.
+ makeNode := func(target *compliance.TargetNode) {
+ tName := target.Name()
+ if _, ok := nodes[tName]; !ok {
+ nodeName := fmt.Sprintf("n%d", n)
+ nodes[tName] = nodeName
+ fmt.Fprintf(stdout, "\t%s [label=\"%s\"];\n", nodeName, targetOut(target, "\\n"))
+ n++
+ }
+ }
+
+ // outputResolution prints a resolution in the requested format to `stdout`, where one can read
+ // a resolution as `tname` resolves `oname`'s conditions named in `cnames`.
+ // `tname` is the name of the target the resolution applies to.
+ // `oname` is the name of the target where the conditions originate.
+ // `cnames` is the list of conditions to resolve.
+ outputResolution := func(tname, aname, oname string, cnames []string) {
+ if ctx.graphViz {
+ // ... one edge per line labelled with \\n-separated annotations.
+ tNode := nodes[tname]
+ aNode := nodes[aname]
+ oNode := nodes[oname]
+ fmt.Fprintf(stdout, "\t%s -> %s; %s -> %s [label=\"%s\"];\n", tNode, aNode, aNode, oNode, strings.Join(cnames, "\\n"))
+ } else {
+ // ... one edge per line with names in a colon-separated tuple.
+ fmt.Fprintf(stdout, "%s %s %s %s\n", tname, aname, oname, strings.Join(cnames, ":"))
+ }
+ }
+
+ // outputSingleton prints `tname` to plain text in the unexpected event that `tname` is the name of
+ // a target in `resolutions.AppliesTo()` but has no conditions to resolve.
+ outputSingleton := func(tname, aname string) {
+ if !ctx.graphViz {
+ fmt.Fprintf(stdout, "%s %s\n", tname, aname)
+ }
+ }
+
+ // Sort the resolutions by targetname for repeatability/stability.
+ targets := resolutions.AttachesTo()
+ sort.Sort(targets)
+
+ // If graphviz output, start the directed graph.
+ if ctx.graphViz {
+ fmt.Fprintf(stdout, "strict digraph {\n\trankdir=LR;\n")
+ for _, target := range targets {
+ makeNode(target)
+ rl := compliance.ResolutionList(resolutions.Resolutions(target))
+ sort.Sort(rl)
+ for _, r := range rl {
+ makeNode(r.ActsOn())
+ }
+ conditions := rl.AllConditions().AsList()
+ sort.Sort(conditions)
+ for _, lc := range conditions {
+ makeNode(lc.Origin())
+ }
+ }
+ }
+
+ // Output the sorted targets.
+ for _, target := range targets {
+ var tname string
+ if ctx.graphViz {
+ tname = target.Name()
+ } else {
+ tname = targetOut(target, ":")
+ }
+
+ rl := compliance.ResolutionList(resolutions.Resolutions(target))
+ sort.Sort(rl)
+ for _, r := range rl {
+ var aname string
+ if ctx.graphViz {
+ aname = r.ActsOn().Name()
+ } else {
+ aname = targetOut(r.ActsOn(), ":")
+ }
+
+ conditions := r.Resolves().AsList()
+ sort.Sort(conditions)
+
+ // poname is the previous origin name or "" if no previous
+ poname := ""
+
+ // cnames accumulates the list of condition names originating at a single origin that apply to `target`.
+ cnames := make([]string, 0, len(conditions))
+
+ // Output 1 line for each attachesTo+actsOn+origin combination.
+ for _, condition := range conditions {
+ var oname string
+ if ctx.graphViz {
+ oname = condition.Origin().Name()
+ } else {
+ oname = targetOut(condition.Origin(), ":")
+ }
+
+ // Detect when origin changes and output prior origin's conditions.
+ if poname != oname && poname != "" {
+ outputResolution(tname, aname, poname, cnames)
+ cnames = cnames[:0]
+ }
+ poname = oname
+ cnames = append(cnames, condition.Name())
+ }
+ // Output last origin's conditions or a singleton if no origins.
+ if poname == "" {
+ outputSingleton(tname, aname)
+ } else {
+ outputResolution(tname, aname, poname, cnames)
+ }
+ }
+ }
+ // If graphViz output, rank the root nodes together, and complete the directed graph.
+ if ctx.graphViz {
+ fmt.Fprintf(stdout, "\t{rank=same;")
+ for _, f := range files {
+ fName := f
+ if !strings.HasSuffix(fName, ".meta_lic") {
+ fName += ".meta_lic"
+ }
+ if fNode, ok := nodes[fName]; ok {
+ fmt.Fprintf(stdout, " %s", fNode)
+ }
+ }
+ fmt.Fprintf(stdout, "}\n}\n")
+ }
+ return nil
+}
diff --git a/tools/compliance/cmd/dumpresolutions_test.go b/tools/compliance/cmd/dumpresolutions_test.go
new file mode 100644
index 0000000..cab1cc8
--- /dev/null
+++ b/tools/compliance/cmd/dumpresolutions_test.go
@@ -0,0 +1,4634 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func Test_plaintext(t *testing.T) {
+ tests := []struct {
+ condition string
+ name string
+ roots []string
+ ctx context
+ expectedOut []string
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic notice",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+ "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+ "testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+ "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+ "lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+ "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+ "lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic notice",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+ "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+ "testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic notice",
+ "testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/bin/bin3.meta_lic testdata/firstparty/bin/bin3.meta_lic testdata/firstparty/bin/bin3.meta_lic notice",
+ "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+ "testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{
+ "testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic notice",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+ "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+ "testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+ "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+ "lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+ "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+ "lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+ "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+ "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+ "testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic notice",
+ "testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+ "testdata/notice/container.zip.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+ "testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/application.meta_lic testdata/notice/application.meta_lic testdata/notice/application.meta_lic notice",
+ "testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/bin/bin3.meta_lic testdata/notice/bin/bin3.meta_lic testdata/notice/bin/bin3.meta_lic notice",
+ "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+ "testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+ },
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{
+ "testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic notice",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+ "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+ "testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
+ "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
+ "lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
+ "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+ "lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic notice",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+ "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+ "testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic notice",
+ "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/bin/bin3.meta_lic testdata/reciprocal/bin/bin3.meta_lic testdata/reciprocal/bin/bin3.meta_lic notice",
+ "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+ "testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{
+ "testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic notice",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "highest.apex.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/libc.a.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []string{},
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted restricted",
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted restricted",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/liba.so.meta_lic:restricted restricted",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
+ "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
+ "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/liba.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
+ "highest.apex.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted restricted",
+ "lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
+ "lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal lib/liba.so.meta_lic:restricted restricted",
+ "lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic notice",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic notice",
+ "testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/bin/bin3.meta_lic testdata/restricted/bin/bin3.meta_lic testdata/restricted/bin/bin3.meta_lic restricted",
+ "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
+ "testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{
+ "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic notice",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/"},
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic by_exception_only:proprietary",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic by_exception_only:proprietary",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic by_exception_only:proprietary",
+ "bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic by_exception_only:proprietary",
+ "highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic by_exception_only:proprietary",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic by_exception_only:proprietary",
+ "highest.apex.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic by_exception_only:proprietary",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic by_exception_only:proprietary",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
+ "highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []string{
+ "bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []string{
+ "bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+ "bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
+ "bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
+ "bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
+ "highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+ "highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ "highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
+ "lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+ "lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/", labelConditions: true},
+ expectedOut: []string{
+ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted restricted",
+ "bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
+ "bin/bin2.meta_lic:by_exception_only:proprietary lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+ "highest.apex.meta_lic:notice highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
+ "highest.apex.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "highest.apex.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
+ "lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+ "lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic notice",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic notice",
+ "testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/bin/bin3.meta_lic testdata/proprietary/bin/bin3.meta_lic testdata/proprietary/bin/bin3.meta_lic restricted",
+ "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
+ "testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []string{
+ "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ expectedOut := &bytes.Buffer{}
+ for _, eo := range tt.expectedOut {
+ expectedOut.WriteString(eo)
+ expectedOut.WriteString("\n")
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+ err := dumpResolutions(&tt.ctx, stdout, stderr, rootFiles...)
+ if err != nil {
+ t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ if stderr.Len() > 0 {
+ t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr)
+ }
+ out := stdout.String()
+ expected := expectedOut.String()
+ if out != expected {
+ outList := strings.Split(out, "\n")
+ expectedList := strings.Split(expected, "\n")
+ startLine := 0
+ for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {
+ startLine++
+ }
+ t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
+ out, expected, startLine+1, outList[startLine], expectedList[startLine])
+ }
+ })
+ }
+}
+
+type testContext struct {
+ nextNode int
+ nodes map[string]string
+}
+
+type matcher interface {
+ matchString(*testContext) string
+ typeString() string
+}
+
+type targetMatcher struct {
+ target string
+ conditions []string
+}
+
+func (tm *targetMatcher) matchString(ctx *testContext) string {
+ m := tm.target
+ if len(tm.conditions) > 0 {
+ m += "\\n" + strings.Join(tm.conditions, "\\n")
+ }
+ m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];"
+ return m
+}
+
+func (tm *targetMatcher) typeString() string {
+ return "target"
+}
+
+type resolutionMatcher struct {
+ appliesTo string
+ actsOn string
+ origin string
+ conditions []string
+}
+
+func (rm *resolutionMatcher) matchString(ctx *testContext) string {
+ return ctx.nodes[rm.appliesTo] + " -> " + ctx.nodes[rm.actsOn] + "; " +
+ ctx.nodes[rm.actsOn] + " -> " + ctx.nodes[rm.origin] +
+ " [label=\"" + strings.Join(rm.conditions, "\\n") + "\"];"
+}
+
+func (rm *resolutionMatcher) typeString() string {
+ return "resolution"
+}
+
+type getMatcher func(*testContext) matcher
+
+func matchTarget(target string, conditions ...string) getMatcher {
+ return func(ctx *testContext) matcher {
+ ctx.nodes[target] = fmt.Sprintf("n%d", ctx.nextNode)
+ ctx.nextNode++
+ return &targetMatcher{target, append([]string{}, conditions...)}
+ }
+}
+
+func matchResolution(appliesTo, actsOn, origin string, conditions ...string) getMatcher {
+ return func(ctx *testContext) matcher {
+ if _, ok := ctx.nodes[appliesTo]; !ok {
+ ctx.nodes[appliesTo] = fmt.Sprintf("unknown%d", ctx.nextNode)
+ ctx.nextNode++
+ }
+ if _, ok := ctx.nodes[actsOn]; !ok {
+ ctx.nodes[actsOn] = fmt.Sprintf("unknown%d", ctx.nextNode)
+ ctx.nextNode++
+ }
+ if _, ok := ctx.nodes[origin]; !ok {
+ ctx.nodes[origin] = fmt.Sprintf("unknown%d", ctx.nextNode)
+ ctx.nextNode++
+ }
+ return &resolutionMatcher{appliesTo, actsOn, origin, append([]string{}, conditions...)}
+ }
+}
+
+func Test_graphviz(t *testing.T) {
+ tests := []struct {
+ condition string
+ name string
+ roots []string
+ ctx context
+ expectedOut []getMatcher
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/bin/bin1.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libc.a.meta_lic"),
+ matchTarget("testdata/firstparty/bin/bin2.meta_lic"),
+ matchTarget("testdata/firstparty/highest.apex.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/firstparty/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "firstparty",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/firstparty/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "notice"),
+ matchTarget("lib/libc.a.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/libb.so.meta_lic", "notice"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/bin/bin1.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libc.a.meta_lic"),
+ matchTarget("testdata/firstparty/bin/bin2.meta_lic"),
+ matchTarget("testdata/firstparty/container.zip.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/container.zip.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/application.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/bin/bin3.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
+ matchResolution(
+ "testdata/firstparty/application.meta_lic",
+ "testdata/firstparty/application.meta_lic",
+ "testdata/firstparty/application.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/application.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin3.meta_lic",
+ "testdata/firstparty/bin/bin3.meta_lic",
+ "testdata/firstparty/bin/bin3.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/bin/bin1.meta_lic"),
+ matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
+ matchTarget("testdata/firstparty/lib/libc.a.meta_lic"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/bin/bin1.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libc.a.meta_lic"),
+ matchTarget("testdata/notice/bin/bin2.meta_lic"),
+ matchTarget("testdata/notice/highest.apex.meta_lic"),
+ matchTarget("testdata/notice/lib/libb.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libd.so.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/notice/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "notice",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/notice/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "notice"),
+ matchTarget("lib/libc.a.meta_lic", "notice"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/libb.so.meta_lic", "notice"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/bin/bin1.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libc.a.meta_lic"),
+ matchTarget("testdata/notice/bin/bin2.meta_lic"),
+ matchTarget("testdata/notice/container.zip.meta_lic"),
+ matchTarget("testdata/notice/lib/libb.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/container.zip.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libd.so.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/application.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/bin/bin3.meta_lic"),
+ matchTarget("testdata/notice/lib/libb.so.meta_lic"),
+ matchResolution(
+ "testdata/notice/application.meta_lic",
+ "testdata/notice/application.meta_lic",
+ "testdata/notice/application.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/application.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin3.meta_lic",
+ "testdata/notice/bin/bin3.meta_lic",
+ "testdata/notice/bin/bin3.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/bin/bin1.meta_lic"),
+ matchTarget("testdata/notice/lib/liba.so.meta_lic"),
+ matchTarget("testdata/notice/lib/libc.a.meta_lic"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/notice/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/notice/lib/libd.so.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"),
+ matchTarget("testdata/reciprocal/bin/bin2.meta_lic"),
+ matchTarget("testdata/reciprocal/highest.apex.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/reciprocal/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/reciprocal/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "reciprocal"),
+ matchTarget("lib/libc.a.meta_lic", "reciprocal"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchTarget("lib/libb.so.meta_lic", "notice"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"),
+ matchTarget("testdata/reciprocal/bin/bin2.meta_lic"),
+ matchTarget("testdata/reciprocal/container.zip.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/container.zip.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/application.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/bin/bin3.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
+ matchResolution(
+ "testdata/reciprocal/application.meta_lic",
+ "testdata/reciprocal/application.meta_lic",
+ "testdata/reciprocal/application.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/application.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin3.meta_lic",
+ "testdata/reciprocal/bin/bin3.meta_lic",
+ "testdata/reciprocal/bin/bin3.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
+ matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "reciprocal"),
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/bin/bin1.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
+ matchTarget("testdata/restricted/bin/bin2.meta_lic"),
+ matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
+ matchTarget("testdata/restricted/highest.apex.meta_lic"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []getMatcher{},
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/restricted/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/restricted/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "restricted"),
+ matchTarget("lib/libc.a.meta_lic", "reciprocal"),
+ matchTarget("bin/bin2.meta_lic", "notice"),
+ matchTarget("lib/libb.so.meta_lic", "restricted"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/bin/bin1.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
+ matchTarget("testdata/restricted/bin/bin2.meta_lic"),
+ matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
+ matchTarget("testdata/restricted/container.zip.meta_lic"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/application.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
+ matchTarget("testdata/restricted/bin/bin3.meta_lic"),
+ matchResolution(
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/application.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin3.meta_lic",
+ "testdata/restricted/bin/bin3.meta_lic",
+ "testdata/restricted/bin/bin3.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "restricted"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/bin/bin1.meta_lic"),
+ matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
+ matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ matchResolution(
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "reciprocal"),
+ },
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/bin/bin1.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
+ matchTarget("testdata/proprietary/bin/bin2.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
+ matchTarget("testdata/proprietary/highest.apex.meta_lic"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/"},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("lib/libd.so.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_notice",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"notice"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_share",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"proprietary"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "proprietary"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_share_private",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{
+ conditions: []string{"reciprocal", "restricted", "proprietary"},
+ stripPrefix: "testdata/proprietary/",
+ },
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic"),
+ matchTarget("lib/liba.so.meta_lic"),
+ matchTarget("lib/libc.a.meta_lic"),
+ matchTarget("bin/bin2.meta_lic"),
+ matchTarget("lib/libb.so.meta_lic"),
+ matchTarget("highest.apex.meta_lic"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "proprietary"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "apex_trimmed_labelled",
+ roots: []string{"highest.apex.meta_lic"},
+ ctx: context{stripPrefix: "testdata/proprietary/", labelConditions: true},
+ expectedOut: []getMatcher{
+ matchTarget("bin/bin1.meta_lic", "notice"),
+ matchTarget("lib/liba.so.meta_lic", "by_exception_only", "proprietary"),
+ matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"),
+ matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"),
+ matchTarget("lib/libb.so.meta_lic", "restricted"),
+ matchTarget("lib/libd.so.meta_lic", "notice"),
+ matchTarget("highest.apex.meta_lic", "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "bin/bin1.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "bin/bin2.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin1.meta_lic",
+ "bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "bin/bin2.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "notice"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "highest.apex.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/bin/bin1.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
+ matchTarget("testdata/proprietary/bin/bin2.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
+ matchTarget("testdata/proprietary/container.zip.meta_lic"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/container.zip.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/application.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
+ matchTarget("testdata/proprietary/bin/bin3.meta_lic"),
+ matchResolution(
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/application.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/bin/bin3.meta_lic",
+ "testdata/proprietary/bin/bin3.meta_lic",
+ "testdata/proprietary/bin/bin3.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ matchResolution(
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "restricted"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/bin/bin1.meta_lic"),
+ matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
+ matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "notice"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ matchResolution(
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "by_exception_only",
+ "proprietary"),
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []getMatcher{
+ matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
+ matchResolution(
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ "notice"),
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ ctx := &testContext{0, make(map[string]string)}
+
+ expectedOut := &bytes.Buffer{}
+ for _, eo := range tt.expectedOut {
+ m := eo(ctx)
+ expectedOut.WriteString(m.matchString(ctx))
+ expectedOut.WriteString("\n")
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+ tt.ctx.graphViz = true
+ err := dumpResolutions(&tt.ctx, stdout, stderr, rootFiles...)
+
+ if err != nil {
+ t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ if stderr.Len() > 0 {
+ t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr)
+ }
+ outList := strings.Split(stdout.String(), "\n")
+ outLine := 0
+ if outList[outLine] != "strict digraph {" {
+ t.Errorf("dumpresolutions: got 1st line %v, want strict digraph {")
+ }
+ outLine++
+ if strings.HasPrefix(strings.TrimLeft(outList[outLine], " \t"), "rankdir") {
+ outLine++
+ }
+ endOut := len(outList)
+ for endOut > 0 && strings.TrimLeft(outList[endOut-1], " \t") == "" {
+ endOut--
+ }
+ if outList[endOut-1] != "}" {
+ t.Errorf("dumpresolutions: got last line %v, want }", outList[endOut-1])
+ }
+ endOut--
+ if strings.HasPrefix(strings.TrimLeft(outList[endOut-1], " \t"), "{rank=same") {
+ endOut--
+ }
+ expectedList := strings.Split(expectedOut.String(), "\n")
+ for len(expectedList) > 0 && expectedList[len(expectedList)-1] == "" {
+ expectedList = expectedList[0 : len(expectedList)-1]
+ }
+ matchLine := 0
+
+ for outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], " \t") == expectedList[matchLine] {
+ outLine++
+ matchLine++
+ }
+ if outLine < endOut || matchLine < len(expectedList) {
+ if outLine >= endOut {
+ t.Errorf("dumpresolutions: missing lines at end of graph, want %d lines %v", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], "\n"))
+ } else if matchLine >= len(expectedList) {
+ t.Errorf("dumpresolutions: unexpected lines at end of graph starting line %d, got %v, want nothing", outLine+1, strings.Join(outList[outLine:], "\n"))
+ } else {
+ t.Errorf("dumpresolutions: at line %d, got %v, want %v", outLine+1, strings.Join(outList[outLine:], "\n"), strings.Join(expectedList[matchLine:], "\n"))
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/cmd/listshare.go b/tools/compliance/cmd/listshare.go
new file mode 100644
index 0000000..bba2308
--- /dev/null
+++ b/tools/compliance/cmd/listshare.go
@@ -0,0 +1,124 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "compliance"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+)
+
+func init() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
+
+Outputs a csv file with 1 project per line in the first field followed
+by target:condition pairs describing why the project must be shared.
+
+Each target is the path to a generated license metadata file for a
+Soong module or Make target, and the license condition is either
+restricted (e.g. GPL) or reciprocal (e.g. MPL).
+`, filepath.Base(os.Args[0]))
+ }
+}
+
+var (
+ failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
+ failNoLicenses = fmt.Errorf("No licenses found")
+)
+
+func main() {
+ flag.Parse()
+
+ // Must specify at least one root target.
+ if flag.NArg() == 0 {
+ flag.Usage()
+ os.Exit(2)
+ }
+
+ err := listShare(os.Stdout, os.Stderr, flag.Args()...)
+ if err != nil {
+ if err == failNoneRequested {
+ flag.Usage()
+ }
+ fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
+
+// listShare implements the listshare utility.
+func listShare(stdout, stderr io.Writer, files ...string) error {
+ // Must be at least one root file.
+ if len(files) < 1 {
+ return failNoneRequested
+ }
+
+ // Read the license graph from the license metadata files (*.meta_lic).
+ licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
+ if err != nil {
+ return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
+ }
+ if licenseGraph == nil {
+ return failNoLicenses
+ }
+
+ // shareSource contains all source-sharing resolutions.
+ shareSource := compliance.ResolveSourceSharing(licenseGraph)
+
+ // Group the resolutions by project.
+ presolution := make(map[string]*compliance.LicenseConditionSet)
+ for _, target := range shareSource.AttachesTo() {
+ rl := shareSource.Resolutions(target)
+ sort.Sort(rl)
+ for _, r := range rl {
+ for _, p := range r.ActsOn().Projects() {
+ if _, ok := presolution[p]; !ok {
+ presolution[p] = r.Resolves().Copy()
+ continue
+ }
+ presolution[p].AddSet(r.Resolves())
+ }
+ }
+ }
+
+ // Sort the projects for repeatability/stability.
+ projects := make([]string, 0, len(presolution))
+ for p := range presolution {
+ projects = append(projects, p)
+ }
+ sort.Strings(projects)
+
+ // Output the sorted projects and the source-sharing license conditions that each project resolves.
+ for _, p := range projects {
+ fmt.Fprintf(stdout, "%s", p)
+
+ // Sort the conditions for repeatability/stability.
+ conditions := presolution[p].AsList()
+ sort.Sort(conditions)
+
+ // Output the sorted origin:condition pairs.
+ for _, lc := range conditions {
+ fmt.Fprintf(stdout, ",%s:%s", lc.Origin().Name(), lc.Name())
+ }
+ fmt.Fprintf(stdout, "\n")
+ }
+
+ return nil
+}
diff --git a/tools/compliance/cmd/listshare_test.go b/tools/compliance/cmd/listshare_test.go
new file mode 100644
index 0000000..b4847e3
--- /dev/null
+++ b/tools/compliance/cmd/listshare_test.go
@@ -0,0 +1,405 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+func Test(t *testing.T) {
+ type projectShare struct {
+ project string
+ conditions []string
+ }
+ tests := []struct {
+ condition string
+ name string
+ roots []string
+ expectedOut []projectShare
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+ },
+ {
+ project: "static/library",
+ conditions: []string{
+ "lib/libc.a.meta_lic:reciprocal",
+ },
+ },
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+ },
+ {
+ project: "static/library",
+ conditions: []string{
+ "lib/libc.a.meta_lic:reciprocal",
+ },
+ },
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+ },
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{
+ "lib/liba.so.meta_lic:reciprocal",
+ },
+ },
+ {
+ project: "static/library",
+ conditions: []string{
+ "lib/libc.a.meta_lic:reciprocal",
+ },
+ },
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "base/library",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "device/library",
+ conditions: []string{"lib/liba.so.meta_lic:restricted"},
+ },
+ {
+ project: "dynamic/binary",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "highest/apex",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libb.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "static/binary",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "static/library",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libc.a.meta_lic:reciprocal",
+ },
+ },
+ },
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "base/library",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "container/zip",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libb.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "device/library",
+ conditions: []string{"lib/liba.so.meta_lic:restricted"},
+ },
+ {
+ project: "dynamic/binary",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "static/binary",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "static/library",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libc.a.meta_lic:reciprocal",
+ },
+ },
+ },
+ },
+ {
+ condition: "restricted",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libb.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "distributable/application",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libb.so.meta_lic:restricted",
+ },
+ },
+ },
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "static/binary",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ },
+ },
+ {
+ project: "static/library",
+ conditions: []string{
+ "lib/liba.so.meta_lic:restricted",
+ "lib/libc.a.meta_lic:reciprocal",
+ },
+ },
+ },
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "base/library",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "dynamic/binary",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "highest/apex",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "base/library",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "container/zip",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "dynamic/binary",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []projectShare{
+ {
+ project: "device/library",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ {
+ project: "distributable/application",
+ conditions: []string{"lib/libb.so.meta_lic:restricted"},
+ },
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []projectShare{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ expectedOut := &bytes.Buffer{}
+ for _, p := range tt.expectedOut {
+ expectedOut.WriteString(p.project)
+ for _, lc := range p.conditions {
+ expectedOut.WriteString(",")
+ expectedOut.WriteString("testdata/")
+ expectedOut.WriteString(tt.condition)
+ expectedOut.WriteString("/")
+ expectedOut.WriteString(lc)
+ }
+ expectedOut.WriteString("\n")
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+ err := listShare(stdout, stderr, rootFiles...)
+ if err != nil {
+ t.Fatalf("listshare: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ if stderr.Len() > 0 {
+ t.Errorf("listshare: gotStderr = %v, want none", stderr)
+ }
+ out := stdout.String()
+ expected := expectedOut.String()
+ if out != expected {
+ outList := strings.Split(out, "\n")
+ expectedList := strings.Split(expected, "\n")
+ startLine := 0
+ for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {
+ startLine++
+ }
+ t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
+ out, expected, startLine+1, outList[startLine], expectedList[startLine])
+ }
+ })
+ }
+}
diff --git a/tools/compliance/cmd/testdata/README.md b/tools/compliance/cmd/testdata/README.md
new file mode 100644
index 0000000..9872c04
--- /dev/null
+++ b/tools/compliance/cmd/testdata/README.md
@@ -0,0 +1,321 @@
+## Test data
+
+Each directory under testdata/ defines a similar build graph.
+All have the same structure, but different versions of the graph have different
+license metadata.
+
+### Testdata build graph structure:
+
+The structure is meant to simulate some common scenarios:
+
+* a `lib/` directory with some libraries
+* a `bin/` directory with some executables
+* one of the binaries, `bin3`, is a toolchain executable like a compiler
+* an `application` built with the `bin3` compiler and linking a couple libraries
+* a pure aggregation `continer.zip` that merely bundles files together, and
+* an apex file (more like an apk file) with some binaries and libraries.
+
+The testdata starts with a `firstparty/` version containng only first-party
+licenses, and each subsequent directory introduces more restrictive conditions:
+
+* `notice/` starts with `firstparty/` adds third-party notice conditions
+* `reciprocal/` starts with `notice/` and adds some reciprocal conditions
+* `restricted/` starts with `reciprocal/` and adds some restricted conditions
+* `proprietary/` starts with `restricted/` and add some privacy conditions
+
+#### a `lib/` directory with some libraries
+
+```dot
+strict digraph {
+ liba [label="lib/liba.so.meta_lic"];
+ libb [label="lib/libb.so.meta_lic"];
+ libc [label="lib/libc.a.meta_lic"];
+ libd [label="lib/libd.so.meta_lic"];
+}
+```
+
+#### a `bin/` directory with some executables
+
+strict digraph {
+ rankdir=LR;
+ bin1 [label="bin/bin1.meta_lic"];
+ bin2 [label="bin/bin2.meta_lic"];
+ bin3 [label="bin/bin3.meta_lic\ntoolchain"];
+ liba [label="lib/liba.so.meta_lic"];
+ libb [label="lib/libb.so.meta_lic"];
+ libc [label="lib/libc.a.meta_lic"];
+ libd [label="lib/libd.so.meta_lic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ {rank=same; bin1 bin2 bin3}
+}
+
+#### an `application` built with the `bin3` compiler and linking a couple libraries
+
+```dot
+strict digraph {
+ rankdir=LR;
+ app [label="application.meta_lic"];
+ bin3 [label="bin/bin3.meta_lic"];
+ liba [label="lib/liba.so.meta_lic"];
+ libb [label="lib/libb.so.meta_lic"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ {rank=same; app}
+}
+```
+
+#### a pure aggregation `container.zip` that merely bundles files together
+
+strict digraph {
+ rankdir=LR;
+ bin1 [label="bin/bin1.meta_lic"];
+ bin2 [label="bin/bin2.meta_lic"];
+ container [label="container.zip.meta_lic"];
+ liba [label="lib/liba.so.meta_lic"];
+ libb [label="lib/libb.so.meta_lic"];
+ libc [label="lib/libc.a.meta_lic"];
+ libd [label="lib/libd.so.meta_lic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ {rank=same; container}
+}
+
+#### an apex file (more like an apk file) with some binaries and libraries
+
+```dot
+strict digraph {
+ rankdir=LR;
+ apex [label="highest.apex.meta_lic"];
+ bin1 [label="bin/bin1.meta_lic"];
+ bin2 [label="bin/bin2.meta_lic"];
+ bin3 [label="bin/bin3.meta_lic"];
+ liba [label="lib/liba.so.meta_lic"];
+ libb [label="lib/libb.so.meta_lic"];
+ libc [label="lib/libc.a.meta_lic"];
+ libd [label="lib/libd.so.meta_lic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; apex}
+}
+```
+
+#### the whole build graph
+
+```dot
+strict digraph {
+ rankdir=LR;
+ apex [label="highest.apex.meta_lic"];
+ app [label="application.meta_lic"];
+ bin1 [label="bin/bin1.meta_lic"];
+ bin2 [label="bin/bin2.meta_lic"];
+ bin3 [label="bin/bin3.meta_lic"];
+ container [label="container.zip.meta_lic"];
+ liba [label="lib/liba.so.meta_lic"];
+ libb [label="lib/libb.so.meta_lic"];
+ libc [label="lib/libc.a.meta_lic"];
+ libd [label="lib/libd.so.meta_lic"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; app container apex}
+}
+```
+
+
+### firstparty/ testdata starts with all first-party licensing
+
+```dot
+strict digraph {
+ rankdir=LR;
+ app [label="firstparty/application.meta_lic"];
+ bin1 [label="firstparty/bin/bin1.meta_lic"];
+ bin2 [label="firstparty/bin/bin2.meta_lic"];
+ bin3 [label="firstparty/bin/bin3.meta_lic"];
+ container [label="firstparty/container.zip.meta_lic"];
+ apex [label="firstparty/highest.apex.meta_lic"];
+ liba [label="firstparty/lib/liba.so.meta_lic"];
+ libb [label="firstparty/lib/libb.so.meta_lic"];
+ libc [label="firstparty/lib/libc.a.meta_lic"];
+ lib [label="firstparty/lib/libd.so.meta_lic"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; app container apex}
+}
+```
+
+### notice/ testdata introduces third-party notice conditions
+
+```dot
+strict digraph {
+ rankdir=LR;
+ app [label="notice/application.meta_lic"];
+ bin1 [label="notice/bin/bin1.meta_lic"];
+ bin2 [label="notice/bin/bin2.meta_lic"];
+ bin3 [label="notice/bin/bin3.meta_lic\nnotice"];
+ container [label="notice/container.zip.meta_lic"];
+ apex [label="notice/highest.apex.meta_lic"];
+ liba [label="notice/lib/liba.so.meta_lic\nnotice"];
+ libb [label="notice/lib/libb.so.meta_lic"];
+ libc [label="notice/lib/libc.a.meta_lic\nnotice"];
+ libd [label="notice/lib/libd.so.meta_lic\nnotice"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; app container apex}
+}
+```
+
+### reciprocal/ testdata introduces third-party reciprocal sharing conditions
+
+```dot
+strict digraph {
+ rankdir=LR;
+ app [label="reciprocal/application.meta_lic"];
+ bin1 [label="reciprocal/bin/bin1.meta_lic"];
+ bin2 [label="reciprocal/bin/bin2.meta_lic"];
+ bin3 [label="reciprocal/bin/bin3.meta_lic\nnotice"];
+ container [label="reciprocal/container.zip.meta_lic"];
+ apex [label="reciprocal/highest.apex.meta_lic"];
+ liba [label="reciprocal/lib/liba.so.meta_lic\nreciprocal"];
+ libb [label="reciprocal/lib/libb.so.meta_lic"];
+ libc [label="reciprocal/lib/libc.a.meta_lic\nreciprocal"];
+ libd [label="reciprocal/lib/libd.so.meta_lic\nnotice"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; app container apex}
+}
+```
+
+### restricted/ testdata introduces restricted source sharing conditions
+
+```dot
+strict digraph {
+ rankdir=LR;
+ app [label="restricted/application.meta_lic"];
+ bin1 [label="restricted/bin/bin1.meta_lic"];
+ bin2 [label="restricted/bin/bin2.meta_lic"];
+ bin3 [label="restricted/bin/bin3.meta_lic\nrestricted"];
+ container [label="restricted/container.zip.meta_lic"];
+ apex [label="restricted/highest.apex.meta_lic"];
+ liba [label="restricted/lib/liba.so.meta_lic\nrestricted"];
+ libb [label="restricted/lib/libb.so.meta_lic\nrestricted"];
+ libc [label="restricted/lib/libc.a.meta_lic\nreciprocal"];
+ libd [label="restricted/lib/libd.so.meta_lic\nnotice"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; app container apex}
+}
+```
+
+### proprietary/ testdata introduces privacy conditions
+
+```dot
+strict digraph {
+ rankdir=LR;
+ app [label="proprietary/application.meta_lic"];
+ bin1 [label="proprietary/bin/bin1.meta_lic"];
+ bin2 [label="proprietary/bin/bin2.meta_lic\nby_exception_only\nproprietary"];
+ bin3 [label="proprietary/bin/bin3.meta_lic\nrestricted"];
+ container [label="proprietary/container.zip.meta_lic"];
+ apex [label="proprietary/highest.apex.meta_lic"];
+ liba [label="proprietary/lib/liba.so.meta_lic\nby_exception_only\nproprietary"];
+ libb [label="proprietary/lib/libb.so.meta_lic\nrestricted"];
+ libc [label="proprietary/lib/libc.a.meta_lic\nby_exception_only\nproprietary"];
+ libd [label="proprietary/lib/libd.so.meta_lic\nnotice"];
+ app -> bin3 [label="toolchain"];
+ app -> liba [label="static"];
+ app -> libb [label="dynamic"];
+ bin1 -> liba [label="static"];
+ bin1 -> libc [label="static"];
+ bin2 -> libb [label="dynamic"];
+ bin2 -> libd [label="dynamic"];
+ container -> bin1 [label="static"];
+ container -> bin2 [label="static"];
+ container -> liba [label="static"];
+ container -> libb [label="static"];
+ apex -> bin1 [label="static"];
+ apex -> bin2 [label="static"];
+ apex -> liba [label="static"];
+ apex -> libb [label="static"];
+ {rank=same; app container apex}
+}
+```
diff --git a/tools/compliance/cmd/testdata/firstparty/application.meta_lic b/tools/compliance/cmd/testdata/firstparty/application.meta_lic
new file mode 100644
index 0000000..58a1566
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/application.meta_lic
@@ -0,0 +1,24 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "distributable/application"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
+installed: "out/target/product/fictional/bin/application"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin3"
+deps: {
+ file: "testdata/firstparty/bin/bin3.meta_lic"
+ annotations: "toolchain"
+}
+deps: {
+ file: "testdata/firstparty/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic
new file mode 100644
index 0000000..34d81d9
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "static/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libc.a"
+deps: {
+ file: "testdata/firstparty/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/lib/libc.a.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic
new file mode 100644
index 0000000..6154421
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "dynamic/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/lib/libd.so"
+deps: {
+ file: "testdata/firstparty/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
+deps: {
+ file: "testdata/firstparty/lib/libd.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic
new file mode 100644
index 0000000..9b7908e
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic
@@ -0,0 +1,9 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "standalone/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed: "out/target/product/fictional/system/bin/bin3"
diff --git a/tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic b/tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic
new file mode 100644
index 0000000..350b123
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic
@@ -0,0 +1,36 @@
+package_name: "Android"
+projects: "container/zip"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed: "out/target/product/fictional/data/container.zip"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/"
+ container_path: ""
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/"
+ container_path: ""
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/firstparty/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic b/tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic
new file mode 100644
index 0000000..53f81a2
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic
@@ -0,0 +1,44 @@
+package_name: "Android"
+projects: "highest/apex"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
+installed: "out/target/product/fictional/system/apex/highest.apex"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/liba.so"
+ container_path: "lib/liba.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/lib/libb.so"
+ container_path: "lib/libb.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin1"
+ container_path: "bin/bin1"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin2"
+ container_path: "bin/bin2"
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/firstparty/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/firstparty/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic b/tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic
new file mode 100644
index 0000000..7913af0
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic
@@ -0,0 +1,9 @@
+package_name: "Android"
+projects: "device/library"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
+installed: "out/target/product/fictional/system/lib/liba.so"
diff --git a/tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic b/tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic
new file mode 100644
index 0000000..a4935d4
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic
@@ -0,0 +1,9 @@
+package_name: "Android"
+projects: "base/library"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
+installed: "out/target/product/fictional/system/lib/libb.so"
diff --git a/tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic b/tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic
new file mode 100644
index 0000000..fa7459a
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic
@@ -0,0 +1,7 @@
+package_name: "Android"
+projects: "static/library"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
diff --git a/tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic b/tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic
new file mode 100644
index 0000000..a2db94a
--- /dev/null
+++ b/tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Android"
+projects: "dynamic/library"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
+installed: "out/target/product/fictional/system/lib/libd.so"
diff --git a/tools/compliance/cmd/testdata/notice/application.meta_lic b/tools/compliance/cmd/testdata/notice/application.meta_lic
new file mode 100644
index 0000000..56c60ef
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/application.meta_lic
@@ -0,0 +1,24 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "distributable/application"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
+installed: "out/target/product/fictional/bin/application"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin3"
+deps: {
+ file: "testdata/notice/bin/bin3.meta_lic"
+ annotations: "toolchain"
+}
+deps: {
+ file: "testdata/notice/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic
new file mode 100644
index 0000000..9bede1b
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "static/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libc.a"
+deps: {
+ file: "testdata/notice/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/lib/libc.a.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic
new file mode 100644
index 0000000..86e06c6
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "dynamic/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/lib/libd.so"
+deps: {
+ file: "testdata/notice/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
+deps: {
+ file: "testdata/notice/lib/libd.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic
new file mode 100644
index 0000000..285d899
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Compiler"
+module_classes: "EXECUTABLES"
+projects: "standalone/binary"
+license_kinds: "SPDX-license-identifier-NCSA"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed: "out/target/product/fictional/system/bin/bin3"
diff --git a/tools/compliance/cmd/testdata/notice/container.zip.meta_lic b/tools/compliance/cmd/testdata/notice/container.zip.meta_lic
new file mode 100644
index 0000000..e8af61c
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/container.zip.meta_lic
@@ -0,0 +1,36 @@
+package_name: "Android"
+projects: "container/zip"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed: "out/target/product/fictional/data/container.zip"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/"
+ container_path: ""
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/"
+ container_path: ""
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/notice/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/notice/highest.apex.meta_lic b/tools/compliance/cmd/testdata/notice/highest.apex.meta_lic
new file mode 100644
index 0000000..9b90aa5
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/highest.apex.meta_lic
@@ -0,0 +1,44 @@
+package_name: "Android"
+projects: "highest/apex"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
+installed: "out/target/product/fictional/system/apex/highest.apex"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/liba.so"
+ container_path: "lib/liba.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/lib/libb.so"
+ container_path: "lib/libb.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin1"
+ container_path: "bin/bin1"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin2"
+ container_path: "bin/bin2"
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/notice/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/notice/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic b/tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic
new file mode 100644
index 0000000..a69839f
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Device"
+projects: "device/library"
+license_kinds: "SPDX-license-identifier-BSD"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
+installed: "out/target/product/fictional/system/lib/liba.so"
diff --git a/tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic b/tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic
new file mode 100644
index 0000000..a4935d4
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic
@@ -0,0 +1,9 @@
+package_name: "Android"
+projects: "base/library"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
+installed: "out/target/product/fictional/system/lib/libb.so"
diff --git a/tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic b/tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic
new file mode 100644
index 0000000..eb0f81f
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic
@@ -0,0 +1,6 @@
+package_name: "External"
+projects: "static/library"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
diff --git a/tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic b/tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic
new file mode 100644
index 0000000..942d298
--- /dev/null
+++ b/tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic
@@ -0,0 +1,7 @@
+package_name: "External"
+projects: "dynamic/library"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
+installed: "out/target/product/fictional/system/lib/libd.so"
diff --git a/tools/compliance/cmd/testdata/proprietary/application.meta_lic b/tools/compliance/cmd/testdata/proprietary/application.meta_lic
new file mode 100644
index 0000000..51b97c5
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/application.meta_lic
@@ -0,0 +1,24 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "distributable/application"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
+installed: "out/target/product/fictional/bin/application"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin3"
+deps: {
+ file: "testdata/proprietary/bin/bin3.meta_lic"
+ annotations: "toolchain"
+}
+deps: {
+ file: "testdata/proprietary/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic
new file mode 100644
index 0000000..c815858
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "static/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libc.a"
+deps: {
+ file: "testdata/proprietary/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/lib/libc.a.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic
new file mode 100644
index 0000000..6b89ba4
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "dynamic/binary"
+license_kinds: "legacy_proprietary"
+license_conditions: "proprietary"
+license_conditions: "by_exception_only"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/lib/libd.so"
+deps: {
+ file: "testdata/proprietary/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
+deps: {
+ file: "testdata/proprietary/lib/libd.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
new file mode 100644
index 0000000..f93553d
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Compiler"
+module_classes: "EXECUTABLES"
+projects: "standalone/binary"
+license_kinds: "SPDX-license-identifier-LGPL-2.0"
+license_conditions: "restricted"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed: "out/target/product/fictional/system/bin/bin3"
diff --git a/tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic b/tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic
new file mode 100644
index 0000000..889e17e
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic
@@ -0,0 +1,36 @@
+package_name: "Android"
+projects: "container/zip"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed: "out/target/product/fictional/data/container.zip"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/"
+ container_path: ""
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/"
+ container_path: ""
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/proprietary/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic b/tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic
new file mode 100644
index 0000000..d615404
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic
@@ -0,0 +1,44 @@
+package_name: "Android"
+projects: "highest/apex"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
+installed: "out/target/product/fictional/system/apex/highest.apex"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/liba.so"
+ container_path: "lib/liba.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/lib/libb.so"
+ container_path: "lib/libb.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin1"
+ container_path: "bin/bin1"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin2"
+ container_path: "bin/bin2"
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/proprietary/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/proprietary/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic b/tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic
new file mode 100644
index 0000000..51141c8
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic
@@ -0,0 +1,9 @@
+package_name: "Device"
+projects: "device/library"
+license_kinds: "legacy_proprietary"
+license_conditions: "proprietary"
+license_conditions: "by_exception_only"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
+installed: "out/target/product/fictional/system/lib/liba.so"
diff --git a/tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic b/tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic
new file mode 100644
index 0000000..c1b86d7
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Android"
+projects: "base/library"
+license_kinds: "SPDX-license-identifier-GPL-2.0"
+license_conditions: "restricted"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
+installed: "out/target/product/fictional/system/lib/libb.so"
diff --git a/tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic b/tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic
new file mode 100644
index 0000000..1ade7da
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic
@@ -0,0 +1,7 @@
+package_name: "External"
+projects: "static/library"
+license_kinds: "legacy_proprietary"
+license_conditions: "proprietary"
+license_conditions: "by_exception_only"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
diff --git a/tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic b/tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic
new file mode 100644
index 0000000..942d298
--- /dev/null
+++ b/tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic
@@ -0,0 +1,7 @@
+package_name: "External"
+projects: "dynamic/library"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
+installed: "out/target/product/fictional/system/lib/libd.so"
diff --git a/tools/compliance/cmd/testdata/reciprocal/application.meta_lic b/tools/compliance/cmd/testdata/reciprocal/application.meta_lic
new file mode 100644
index 0000000..015c2d9
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/application.meta_lic
@@ -0,0 +1,24 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "distributable/application"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
+installed: "out/target/product/fictional/bin/application"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin3"
+deps: {
+ file: "testdata/reciprocal/bin/bin3.meta_lic"
+ annotations: "toolchain"
+}
+deps: {
+ file: "testdata/reciprocal/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic
new file mode 100644
index 0000000..4ebf653
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "static/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libc.a"
+deps: {
+ file: "testdata/reciprocal/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/lib/libc.a.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic
new file mode 100644
index 0000000..4d28608
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "dynamic/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/lib/libd.so"
+deps: {
+ file: "testdata/reciprocal/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
+deps: {
+ file: "testdata/reciprocal/lib/libd.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic
new file mode 100644
index 0000000..285d899
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Compiler"
+module_classes: "EXECUTABLES"
+projects: "standalone/binary"
+license_kinds: "SPDX-license-identifier-NCSA"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed: "out/target/product/fictional/system/bin/bin3"
diff --git a/tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic b/tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic
new file mode 100644
index 0000000..ea3598f
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic
@@ -0,0 +1,36 @@
+package_name: "Android"
+projects: "container/zip"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed: "out/target/product/fictional/data/container.zip"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/"
+ container_path: ""
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/"
+ container_path: ""
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/reciprocal/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic b/tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic
new file mode 100644
index 0000000..1fec741
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic
@@ -0,0 +1,44 @@
+package_name: "Android"
+projects: "highest/apex"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
+installed: "out/target/product/fictional/system/apex/highest.apex"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/liba.so"
+ container_path: "lib/liba.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/lib/libb.so"
+ container_path: "lib/libb.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin1"
+ container_path: "bin/bin1"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin2"
+ container_path: "bin/bin2"
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/reciprocal/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/reciprocal/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic b/tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic
new file mode 100644
index 0000000..79d7a9e
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Device"
+projects: "device/library"
+license_kinds: "SPDX-license-identifier-MPL"
+license_conditions: "reciprocal"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
+installed: "out/target/product/fictional/system/lib/liba.so"
diff --git a/tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic b/tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic
new file mode 100644
index 0000000..a4935d4
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic
@@ -0,0 +1,9 @@
+package_name: "Android"
+projects: "base/library"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
+installed: "out/target/product/fictional/system/lib/libb.so"
diff --git a/tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic b/tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic
new file mode 100644
index 0000000..8f6d356
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic
@@ -0,0 +1,6 @@
+package_name: "External"
+projects: "static/library"
+license_kinds: "SPDX-license-identifier-MPL"
+license_conditions: "reciprocal"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
diff --git a/tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic b/tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic
new file mode 100644
index 0000000..942d298
--- /dev/null
+++ b/tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic
@@ -0,0 +1,7 @@
+package_name: "External"
+projects: "dynamic/library"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
+installed: "out/target/product/fictional/system/lib/libd.so"
diff --git a/tools/compliance/cmd/testdata/restricted/application.meta_lic b/tools/compliance/cmd/testdata/restricted/application.meta_lic
new file mode 100644
index 0000000..a06a2c8
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/application.meta_lic
@@ -0,0 +1,24 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "distributable/application"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
+installed: "out/target/product/fictional/bin/application"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin3"
+deps: {
+ file: "testdata/restricted/bin/bin3.meta_lic"
+ annotations: "toolchain"
+}
+deps: {
+ file: "testdata/restricted/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic
new file mode 100644
index 0000000..dd8a2e0
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "static/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/liba.a"
+sources: "out/target/product/fictional/system/lib/libc.a"
+deps: {
+ file: "testdata/restricted/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/lib/libc.a.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic
new file mode 100644
index 0000000..714b537
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name: "Android"
+module_classes: "EXECUTABLES"
+projects: "dynamic/binary"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/lib/libd.so"
+deps: {
+ file: "testdata/restricted/lib/libb.so.meta_lic"
+ annotations: "dynamic"
+}
+deps: {
+ file: "testdata/restricted/lib/libd.so.meta_lic"
+ annotations: "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
new file mode 100644
index 0000000..f93553d
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Compiler"
+module_classes: "EXECUTABLES"
+projects: "standalone/binary"
+license_kinds: "SPDX-license-identifier-LGPL-2.0"
+license_conditions: "restricted"
+is_container: false
+built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed: "out/target/product/fictional/system/bin/bin3"
diff --git a/tools/compliance/cmd/testdata/restricted/container.zip.meta_lic b/tools/compliance/cmd/testdata/restricted/container.zip.meta_lic
new file mode 100644
index 0000000..a63263b
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/container.zip.meta_lic
@@ -0,0 +1,36 @@
+package_name: "Android"
+projects: "container/zip"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed: "out/target/product/fictional/data/container.zip"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/"
+ container_path: ""
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/"
+ container_path: ""
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/restricted/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic b/tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic
new file mode 100644
index 0000000..dba419a
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic
@@ -0,0 +1,44 @@
+package_name: "Android"
+projects: "highest/apex"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+license_texts: "build/soong/licenses/LICENSE"
+is_container: true
+built: "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
+installed: "out/target/product/fictional/system/apex/highest.apex"
+install_map {
+ from_path: "out/target/product/fictional/system/lib/liba.so"
+ container_path: "lib/liba.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/lib/libb.so"
+ container_path: "lib/libb.so"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin1"
+ container_path: "bin/bin1"
+}
+install_map {
+ from_path: "out/target/product/fictional/system/bin/bin2"
+ container_path: "bin/bin2"
+}
+sources: "out/target/product/fictional/system/lib/liba.so"
+sources: "out/target/product/fictional/system/lib/libb.so"
+sources: "out/target/product/fictional/system/bin/bin1"
+sources: "out/target/product/fictional/system/bin/bin2"
+deps: {
+ file: "testdata/restricted/bin/bin1.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/bin/bin2.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/lib/liba.so.meta_lic"
+ annotations: "static"
+}
+deps: {
+ file: "testdata/restricted/lib/libb.so.meta_lic"
+ annotations: "static"
+}
diff --git a/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic b/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
new file mode 100644
index 0000000..b1d4560
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Device"
+projects: "device/library"
+license_kinds: "SPDX-license-identifier-LGPL-2.0"
+license_conditions: "restricted"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
+installed: "out/target/product/fictional/system/lib/liba.so"
diff --git a/tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic b/tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic
new file mode 100644
index 0000000..c1b86d7
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic
@@ -0,0 +1,8 @@
+package_name: "Android"
+projects: "base/library"
+license_kinds: "SPDX-license-identifier-GPL-2.0"
+license_conditions: "restricted"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
+installed: "out/target/product/fictional/system/lib/libb.so"
diff --git a/tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic b/tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic
new file mode 100644
index 0000000..8f6d356
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic
@@ -0,0 +1,6 @@
+package_name: "External"
+projects: "static/library"
+license_kinds: "SPDX-license-identifier-MPL"
+license_conditions: "reciprocal"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
diff --git a/tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic b/tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic
new file mode 100644
index 0000000..942d298
--- /dev/null
+++ b/tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic
@@ -0,0 +1,7 @@
+package_name: "External"
+projects: "dynamic/library"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+is_container: false
+built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
+installed: "out/target/product/fictional/system/lib/libd.so"
diff --git a/tools/compliance/condition.go b/tools/compliance/condition.go
new file mode 100644
index 0000000..b5c8cec
--- /dev/null
+++ b/tools/compliance/condition.go
@@ -0,0 +1,165 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "strings"
+)
+
+// LicenseCondition describes an individual license condition or requirement
+// originating at a specific target node. (immutable)
+//
+// e.g. A module licensed under GPL terms would originate a `restricted` condition.
+type LicenseCondition struct {
+ name string
+ origin *TargetNode
+}
+
+// Name returns the name of the condition. e.g. "restricted" or "notice"
+func (lc LicenseCondition) Name() string {
+ return lc.name
+}
+
+// Origin identifies the TargetNode where the condition originates.
+func (lc LicenseCondition) Origin() *TargetNode {
+ return lc.origin
+}
+
+// asString returns a string representation of a license condition:
+// origin+separator+condition.
+func (lc LicenseCondition) asString(separator string) string {
+ return lc.origin.name + separator + lc.name
+}
+
+// ConditionList implements introspection methods to arrays of LicenseCondition.
+type ConditionList []LicenseCondition
+
+
+// ConditionList orders arrays of LicenseCondition by Origin and Name.
+
+// Len returns the length of the list.
+func (l ConditionList) Len() int { return len(l) }
+
+// Swap rearranges 2 elements in the list so each occupies the other's former position.
+func (l ConditionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than tht `j`th element.
+func (l ConditionList) Less(i, j int) bool {
+ if l[i].origin.name == l[j].origin.name {
+ return l[i].name < l[j].name
+ }
+ return l[i].origin.name < l[j].origin.name
+}
+
+// String returns a string representation of the set.
+func (cl ConditionList) String() string {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "[")
+ sep := ""
+ for _, lc := range cl {
+ fmt.Fprintf(&sb, "%s%s:%s", sep, lc.origin.name, lc.name)
+ sep = ", "
+ }
+ fmt.Fprintf(&sb, "]")
+ return sb.String()
+}
+
+// Names returns the list of the conditions' names.
+func (cl ConditionList) Names() []string {
+ result := make([]string, 0, len(cl))
+ for _, lc := range cl {
+ result = append(result, lc.name)
+ }
+ return result
+}
+
+// HasByName returns true if the list contains any condition matching `name`.
+func (cl ConditionList) HasByName(name ConditionNames) bool {
+ for _, lc := range cl {
+ if name.Contains(lc.name) {
+ return true
+ }
+ }
+ return false
+}
+
+// ByName returns the sublist of conditions that match `name`.
+func (cl ConditionList) ByName(name ConditionNames) ConditionList {
+ result := make(ConditionList, 0, cl.CountByName(name))
+ for _, lc := range cl {
+ if name.Contains(lc.name) {
+ result = append(result, lc)
+ }
+ }
+ return result
+}
+
+// CountByName returns the size of the sublist of conditions that match `name`.
+func (cl ConditionList) CountByName(name ConditionNames) int {
+ size := 0
+ for _, lc := range cl {
+ if name.Contains(lc.name) {
+ size++
+ }
+ }
+ return size
+}
+
+// HasByOrigin returns true if the list contains any condition originating at `origin`.
+func (cl ConditionList) HasByOrigin(origin *TargetNode) bool {
+ for _, lc := range cl {
+ if lc.origin.name == origin.name {
+ return true
+ }
+ }
+ return false
+}
+
+// ByOrigin returns the sublist of conditions that originate at `origin`.
+func (cl ConditionList) ByOrigin(origin *TargetNode) ConditionList {
+ result := make(ConditionList, 0, cl.CountByOrigin(origin))
+ for _, lc := range cl {
+ if lc.origin.name == origin.name {
+ result = append(result, lc)
+ }
+ }
+ return result
+}
+
+// CountByOrigin returns the size of the sublist of conditions that originate at `origin`.
+func (cl ConditionList) CountByOrigin(origin *TargetNode) int {
+ size := 0
+ for _, lc := range cl {
+ if lc.origin.name == origin.name {
+ size++
+ }
+ }
+ return size
+}
+
+// ConditionNames implements the Contains predicate for slices of condition
+// name strings.
+type ConditionNames []string
+
+// Contains returns true if the name matches one of the ConditionNames.
+func (cn ConditionNames) Contains(name string) bool {
+ for _, cname := range cn {
+ if cname == name {
+ return true
+ }
+ }
+ return false
+}
diff --git a/tools/compliance/condition_test.go b/tools/compliance/condition_test.go
new file mode 100644
index 0000000..0507469
--- /dev/null
+++ b/tools/compliance/condition_test.go
@@ -0,0 +1,218 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestConditionNames(t *testing.T) {
+ impliesShare := ConditionNames([]string{"restricted", "reciprocal"})
+
+ if impliesShare.Contains("notice") {
+ t.Errorf("impliesShare.Contains(\"notice\") got true, want false")
+ }
+
+ if !impliesShare.Contains("restricted") {
+ t.Errorf("impliesShare.Contains(\"restricted\") got false, want true")
+ }
+
+ if !impliesShare.Contains("reciprocal") {
+ t.Errorf("impliesShare.Contains(\"reciprocal\") got false, want true")
+ }
+
+ if impliesShare.Contains("") {
+ t.Errorf("impliesShare.Contains(\"\") got true, want false")
+ }
+}
+
+func TestConditionList(t *testing.T) {
+ tests := []struct {
+ name string
+ conditions map[string][]string
+ byName map[string][]string
+ byOrigin map[string][]string
+ }{
+ {
+ name: "noticeonly",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "lib1"},
+ },
+ byName: map[string][]string{
+ "notice": []string{"bin1", "lib1"},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice"},
+ "lib1": []string{"notice"},
+ "bin2": []string{},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "empty",
+ conditions: map[string][]string{},
+ byName: map[string][]string{
+ "notice": []string{},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{},
+ "lib1": []string{},
+ "bin2": []string{},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "everything",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "allbutoneeach",
+ conditions: map[string][]string{
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "oneeach",
+ conditions: map[string][]string{
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice"},
+ "bin2": []string{"reciprocal"},
+ "lib1": []string{"restricted"},
+ "lib2": []string{"by_exception_only"},
+ "other": []string{},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ lg := newLicenseGraph()
+ cl := toConditionList(lg, tt.conditions)
+ for names, expected := range tt.byName {
+ name := ConditionNames(strings.Split(names, ":"))
+ if cl.HasByName(name) {
+ if len(expected) == 0 {
+ t.Errorf("unexpected ConditionList.HasByName(%q): got true, want false", name)
+ }
+ } else {
+ if len(expected) != 0 {
+ t.Errorf("unexpected ConditionList.HasByName(%q): got false, want true", name)
+ }
+ }
+ if len(expected) != cl.CountByName(name) {
+ t.Errorf("unexpected ConditionList.CountByName(%q): got %d, want %d", name, cl.CountByName(name), len(expected))
+ }
+ byName := cl.ByName(name)
+ if len(expected) != len(byName) {
+ t.Errorf("unexpected ConditionList.ByName(%q): got %v, want %v", name, byName, expected)
+ } else {
+ sort.Strings(expected)
+ actual := make([]string, 0, len(byName))
+ for _, lc := range byName {
+ actual = append(actual, lc.Origin().Name())
+ }
+ sort.Strings(actual)
+ for i := 0; i < len(expected); i++ {
+ if expected[i] != actual[i] {
+ t.Errorf("unexpected ConditionList.ByName(%q) index %d in %v: got %s, want %s", name, i, actual, actual[i], expected[i])
+ }
+ }
+ }
+ }
+ for origin, expected := range tt.byOrigin {
+ onode := newTestNode(lg, origin)
+ if cl.HasByOrigin(onode) {
+ if len(expected) == 0 {
+ t.Errorf("unexpected ConditionList.HasByOrigin(%q): got true, want false", origin)
+ }
+ } else {
+ if len(expected) != 0 {
+ t.Errorf("unexpected ConditionList.HasByOrigin(%q): got false, want true", origin)
+ }
+ }
+ if len(expected) != cl.CountByOrigin(onode) {
+ t.Errorf("unexpected ConditionList.CountByOrigin(%q): got %d, want %d", origin, cl.CountByOrigin(onode), len(expected))
+ }
+ byOrigin := cl.ByOrigin(onode)
+ if len(expected) != len(byOrigin) {
+ t.Errorf("unexpected ConditionList.ByOrigin(%q): got %v, want %v", origin, byOrigin, expected)
+ } else {
+ sort.Strings(expected)
+ actual := make([]string, 0, len(byOrigin))
+ for _, lc := range byOrigin {
+ actual = append(actual, lc.Name())
+ }
+ sort.Strings(actual)
+ for i := 0; i < len(expected); i++ {
+ if expected[i] != actual[i] {
+ t.Errorf("unexpected ConditionList.ByOrigin(%q) index %d in %v: got %s, want %s", origin, i, actual, actual[i], expected[i])
+ }
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/conditionset.go b/tools/compliance/conditionset.go
new file mode 100644
index 0000000..1ad15ca
--- /dev/null
+++ b/tools/compliance/conditionset.go
@@ -0,0 +1,278 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+)
+
+// NewLicenseConditionSet creates a new instance or variable of *LicenseConditionSet.
+func NewLicenseConditionSet(conditions ...LicenseCondition) *LicenseConditionSet {
+ cs := newLicenseConditionSet()
+ cs.Add(conditions...)
+ return cs
+}
+
+// LicenseConditionSet describes a mutable set of immutable license conditions.
+type LicenseConditionSet struct {
+ // conditions describes the set of license conditions i.e. (condition name, origin target) pairs
+ // by mapping condition name -> origin target -> true.
+ conditions map[string]map[*TargetNode]bool
+}
+
+// Add makes all `conditions` members of the set if they were not previously.
+func (cs *LicenseConditionSet) Add(conditions ...LicenseCondition) {
+ if len(conditions) == 0 {
+ return
+ }
+ for _, lc := range conditions {
+ if _, ok := cs.conditions[lc.name]; !ok {
+ cs.conditions[lc.name] = make(map[*TargetNode]bool)
+ }
+ cs.conditions[lc.name][lc.origin] = true
+ }
+}
+
+// AddSet makes all elements of `conditions` members of the set if they were not previously.
+func (cs *LicenseConditionSet) AddSet(other *LicenseConditionSet) {
+ if len(other.conditions) == 0 {
+ return
+ }
+ for name, origins := range other.conditions {
+ if len(origins) == 0 {
+ continue
+ }
+ if _, ok := cs.conditions[name]; !ok {
+ cs.conditions[name] = make(map[*TargetNode]bool)
+ }
+ for origin := range origins {
+ cs.conditions[name][origin] = other.conditions[name][origin]
+ }
+ }
+}
+
+// ByName returns a list of the conditions in the set matching `names`.
+func (cs *LicenseConditionSet) ByName(names ...ConditionNames) *LicenseConditionSet {
+ other := newLicenseConditionSet()
+ for _, cn := range names {
+ for _, name := range cn {
+ if origins, ok := cs.conditions[name]; ok {
+ other.conditions[name] = make(map[*TargetNode]bool)
+ for origin := range origins {
+ other.conditions[name][origin] = true
+ }
+ }
+ }
+ }
+ return other
+}
+
+// HasAnyByName returns true if the set contains any conditions matching `names` originating at any target.
+func (cs *LicenseConditionSet) HasAnyByName(names ...ConditionNames) bool {
+ for _, cn := range names {
+ for _, name := range cn {
+ if origins, ok := cs.conditions[name]; ok {
+ if len(origins) > 0 {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+// CountByName returns the number of conditions matching `names` originating at any target.
+func (cs *LicenseConditionSet) CountByName(names ...ConditionNames) int {
+ size := 0
+ for _, cn := range names {
+ for _, name := range cn {
+ if origins, ok := cs.conditions[name]; ok {
+ size += len(origins)
+ }
+ }
+ }
+ return size
+}
+
+// ByOrigin returns all of the conditions that originate at `origin` regardless of name.
+func (cs *LicenseConditionSet) ByOrigin(origin *TargetNode) *LicenseConditionSet {
+ other := newLicenseConditionSet()
+ for name, origins := range cs.conditions {
+ if _, ok := origins[origin]; ok {
+ other.conditions[name] = make(map[*TargetNode]bool)
+ other.conditions[name][origin] = true
+ }
+ }
+ return other
+}
+
+// HasAnyByOrigin returns true if the set contains any conditions originating at `origin` regardless of condition name.
+func (cs *LicenseConditionSet) HasAnyByOrigin(origin *TargetNode) bool {
+ for _, origins := range cs.conditions {
+ if _, ok := origins[origin]; ok {
+ return true
+ }
+ }
+ return false
+}
+
+// CountByOrigin returns the number of conditions originating at `origin` regardless of condition name.
+func (cs *LicenseConditionSet) CountByOrigin(origin *TargetNode) int {
+ size := 0
+ for _, origins := range cs.conditions {
+ if _, ok := origins[origin]; ok {
+ size++
+ }
+ }
+ return size
+}
+
+// AsList returns a list of all the conditions in the set.
+func (cs *LicenseConditionSet) AsList() ConditionList {
+ result := make(ConditionList, 0, cs.Count())
+ for name, origins := range cs.conditions {
+ for origin := range origins {
+ result = append(result, LicenseCondition{name, origin})
+ }
+ }
+ return result
+}
+
+// Names returns a list of the names of the conditions in the set.
+func (cs *LicenseConditionSet) Names() []string {
+ result := make([]string, 0, len(cs.conditions))
+ for name := range cs.conditions {
+ result = append(result, name)
+ }
+ return result
+}
+
+// Count returns the number of conditions in the set.
+func (cs *LicenseConditionSet) Count() int {
+ size := 0
+ for _, origins := range cs.conditions {
+ size += len(origins)
+ }
+ return size
+}
+
+// Copy creates a new LicenseCondition variable with the same value.
+func (cs *LicenseConditionSet) Copy() *LicenseConditionSet {
+ other := newLicenseConditionSet()
+ for name := range cs.conditions {
+ other.conditions[name] = make(map[*TargetNode]bool)
+ for origin := range cs.conditions[name] {
+ other.conditions[name][origin] = cs.conditions[name][origin]
+ }
+ }
+ return other
+}
+
+// HasCondition returns true if the set contains any condition matching both `names` and `origin`.
+func (cs *LicenseConditionSet) HasCondition(names ConditionNames, origin *TargetNode) bool {
+ for _, name := range names {
+ if origins, ok := cs.conditions[name]; ok {
+ _, isPresent := origins[origin]
+ if isPresent {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// IsEmpty returns true when the set of conditions contains zero elements.
+func (cs *LicenseConditionSet) IsEmpty() bool {
+ for _, origins := range cs.conditions {
+ if 0 < len(origins) {
+ return false
+ }
+ }
+ return true
+}
+
+// RemoveAllByName changes the set to delete all conditions matching `names`.
+func (cs *LicenseConditionSet) RemoveAllByName(names ...ConditionNames) {
+ for _, cn := range names {
+ for _, name := range cn {
+ delete(cs.conditions, name)
+ }
+ }
+}
+
+// Remove changes the set to delete `conditions`.
+func (cs *LicenseConditionSet) Remove(conditions ...LicenseCondition) {
+ for _, lc := range conditions {
+ if _, isPresent := cs.conditions[lc.name]; !isPresent {
+ panic(fmt.Errorf("attempt to remove non-existent condition: %q", lc.asString(":")))
+ }
+ if _, isPresent := cs.conditions[lc.name][lc.origin]; !isPresent {
+ panic(fmt.Errorf("attempt to remove non-existent origin: %q", lc.asString(":")))
+ }
+ delete(cs.conditions[lc.name], lc.origin)
+ }
+}
+
+// removeSet changes the set to delete all conditions also present in `other`.
+func (cs *LicenseConditionSet) RemoveSet(other *LicenseConditionSet) {
+ for name, origins := range other.conditions {
+ if _, isPresent := cs.conditions[name]; !isPresent {
+ continue
+ }
+ for origin := range origins {
+ delete(cs.conditions[name], origin)
+ }
+ }
+}
+
+// compliance-only LicenseConditionSet methods
+
+// newLicenseConditionSet constructs a set of `conditions`.
+func newLicenseConditionSet() *LicenseConditionSet {
+ return &LicenseConditionSet{make(map[string]map[*TargetNode]bool)}
+}
+
+// add changes the set to include each element of `conditions` originating at `origin`.
+func (cs *LicenseConditionSet) add(origin *TargetNode, conditions ...string) {
+ for _, name := range conditions {
+ if _, ok := cs.conditions[name]; !ok {
+ cs.conditions[name] = make(map[*TargetNode]bool)
+ }
+ cs.conditions[name][origin] = true
+ }
+}
+
+// asStringList returns the conditions in the set as `separator`-separated (origin, condition-name) pair strings.
+func (cs *LicenseConditionSet) asStringList(separator string) []string {
+ result := make([]string, 0, cs.Count())
+ for name, origins := range cs.conditions {
+ for origin := range origins {
+ result = append(result, origin.name+separator+name)
+ }
+ }
+ return result
+}
+
+// conditionNamesArray implements a `contains` predicate for arrays of ConditionNames
+type conditionNamesArray []ConditionNames
+
+func (cn conditionNamesArray) contains(name string) bool {
+ for _, names := range cn {
+ if names.Contains(name) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/tools/compliance/conditionset_test.go b/tools/compliance/conditionset_test.go
new file mode 100644
index 0000000..eac0680
--- /dev/null
+++ b/tools/compliance/conditionset_test.go
@@ -0,0 +1,590 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "sort"
+ "strings"
+ "testing"
+)
+
+type byName map[string][]string
+
+func (bn byName) checkPublic(ls *LicenseConditionSet, t *testing.T) {
+ for names, expected := range bn {
+ name := ConditionNames(strings.Split(names, ":"))
+ if ls.HasAnyByName(name) {
+ if len(expected) == 0 {
+ t.Errorf("unexpected LicenseConditionSet.HasAnyByName(%q): got true, want false", name)
+ }
+ } else {
+ if len(expected) != 0 {
+ t.Errorf("unexpected LicenseConditionSet.HasAnyByName(%q): got false, want true", name)
+ }
+ }
+ if len(expected) != ls.CountByName(name) {
+ t.Errorf("unexpected LicenseConditionSet.CountByName(%q): got %d, want %d", name, ls.CountByName(name), len(expected))
+ }
+ byName := ls.ByName(name).AsList()
+ if len(expected) != len(byName) {
+ t.Errorf("unexpected LicenseConditionSet.ByName(%q): got %v, want %v", name, byName, expected)
+ } else {
+ sort.Strings(expected)
+ actual := make([]string, 0, len(byName))
+ for _, lc := range byName {
+ actual = append(actual, lc.Origin().Name())
+ }
+ sort.Strings(actual)
+ for i := 0; i < len(expected); i++ {
+ if expected[i] != actual[i] {
+ t.Errorf("unexpected LicenseConditionSet.ByName(%q) index %d in %v: got %s, want %s", name, i, actual, actual[i], expected[i])
+ }
+ }
+ }
+ }
+}
+
+type byOrigin map[string][]string
+
+func (bo byOrigin) checkPublic(lg *LicenseGraph, ls *LicenseConditionSet, t *testing.T) {
+ expectedCount := 0
+ for origin, expected := range bo {
+ expectedCount += len(expected)
+ onode := newTestNode(lg, origin)
+ if ls.HasAnyByOrigin(onode) {
+ if len(expected) == 0 {
+ t.Errorf("unexpected LicenseConditionSet.HasAnyByOrigin(%q): got true, want false", origin)
+ }
+ } else {
+ if len(expected) != 0 {
+ t.Errorf("unexpected LicenseConditionSet.HasAnyByOrigin(%q): got false, want true", origin)
+ }
+ }
+ if len(expected) != ls.CountByOrigin(onode) {
+ t.Errorf("unexpected LicenseConditionSet.CountByOrigin(%q): got %d, want %d", origin, ls.CountByOrigin(onode), len(expected))
+ }
+ byOrigin := ls.ByOrigin(onode).AsList()
+ if len(expected) != len(byOrigin) {
+ t.Errorf("unexpected LicenseConditionSet.ByOrigin(%q): got %v, want %v", origin, byOrigin, expected)
+ } else {
+ sort.Strings(expected)
+ actual := make([]string, 0, len(byOrigin))
+ for _, lc := range byOrigin {
+ actual = append(actual, lc.Name())
+ }
+ sort.Strings(actual)
+ for i := 0; i < len(expected); i++ {
+ if expected[i] != actual[i] {
+ t.Errorf("unexpected LicenseConditionSet.ByOrigin(%q) index %d in %v: got %s, want %s", origin, i, actual, actual[i], expected[i])
+ }
+ }
+ }
+ }
+ if expectedCount != ls.Count() {
+ t.Errorf("unexpected LicenseConditionSet.Count(): got %d, want %d", ls.Count(), expectedCount)
+ }
+ if ls.IsEmpty() {
+ if expectedCount != 0 {
+ t.Errorf("unexpected LicenseConditionSet.IsEmpty(): got true, want false")
+ }
+ } else {
+ if expectedCount == 0 {
+ t.Errorf("unexpected LicenseConditionSet.IsEmpty(): got false, want true")
+ }
+ }
+}
+
+func TestConditionSet(t *testing.T) {
+ tests := []struct {
+ name string
+ conditions map[string][]string
+ add map[string][]string
+ byName map[string][]string
+ byOrigin map[string][]string
+ }{
+ {
+ name: "empty",
+ conditions: map[string][]string{},
+ add: map[string][]string{},
+ byName: map[string][]string{
+ "notice": []string{},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{},
+ "lib1": []string{},
+ "bin2": []string{},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "noticeonly",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "lib1"},
+ },
+ byName: map[string][]string{
+ "notice": []string{"bin1", "lib1"},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice"},
+ "lib1": []string{"notice"},
+ "bin2": []string{},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "noticeonlyadded",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "lib1"},
+ },
+ add: map[string][]string{
+ "notice": []string{"bin1", "bin2"},
+ },
+ byName: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1"},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice"},
+ "lib1": []string{"notice"},
+ "bin2": []string{"notice"},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "everything",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ add: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "allbutoneeach",
+ conditions: map[string][]string{
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "allbutoneeachadded",
+ conditions: map[string][]string{
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ add: map[string][]string{
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "allbutoneeachfilled",
+ conditions: map[string][]string{
+ "notice": []string{"bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1"},
+ },
+ add: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1"},
+ "reciprocal": []string{"bin1", "bin2", "lib2"},
+ "restricted": []string{"bin1", "lib1", "lib2"},
+ "by_exception_only": []string{"bin2", "lib1", "lib2"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "oneeach",
+ conditions: map[string][]string{
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice"},
+ "bin2": []string{"reciprocal"},
+ "lib1": []string{"restricted"},
+ "lib2": []string{"by_exception_only"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "oneeachoverlap",
+ conditions: map[string][]string{
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ add: map[string][]string{
+ "notice": []string{"lib2"},
+ "reciprocal": []string{"lib1"},
+ "restricted": []string{"bin2"},
+ "by_exception_only": []string{"bin1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1", "lib2"},
+ "reciprocal": []string{"bin2", "lib1"},
+ "restricted": []string{"bin2", "lib1"},
+ "by_exception_only": []string{"bin1", "lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"by_exception_only", "notice"},
+ "bin2": []string{"reciprocal", "restricted"},
+ "lib1": []string{"reciprocal", "restricted"},
+ "lib2": []string{"by_exception_only", "notice"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "oneeachadded",
+ conditions: map[string][]string{
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ add: map[string][]string{
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1"},
+ "reciprocal": []string{"bin2"},
+ "restricted": []string{"lib1"},
+ "by_exception_only": []string{"lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice"},
+ "bin2": []string{"reciprocal"},
+ "lib1": []string{"restricted"},
+ "lib2": []string{"by_exception_only"},
+ "other": []string{},
+ },
+ },
+ }
+ for _, tt := range tests {
+ testPublicInterface := func(lg *LicenseGraph, cs *LicenseConditionSet, t *testing.T) {
+ byName(tt.byName).checkPublic(cs, t)
+ byOrigin(tt.byOrigin).checkPublic(lg, cs, t)
+ }
+ t.Run(tt.name+"_public_interface", func(t *testing.T) {
+ lg := newLicenseGraph()
+ cs := NewLicenseConditionSet(toConditionList(lg, tt.conditions)...)
+ if tt.add != nil {
+ cs.Add(toConditionList(lg, tt.add)...)
+ }
+ testPublicInterface(lg, cs, t)
+ })
+
+ t.Run("Copy() of "+tt.name+"_public_interface", func(t *testing.T) {
+ lg := newLicenseGraph()
+ cs := NewLicenseConditionSet(toConditionList(lg, tt.conditions)...)
+ if tt.add != nil {
+ cs.Add(toConditionList(lg, tt.add)...)
+ }
+ testPublicInterface(lg, cs.Copy(), t)
+ })
+
+ testPrivateInterface := func(lg *LicenseGraph, cs *LicenseConditionSet, t *testing.T) {
+ slist := make([]string, 0, cs.Count())
+ for origin, expected := range tt.byOrigin {
+ for _, name := range expected {
+ slist = append(slist, origin+";"+name)
+ }
+ }
+ actualSlist := cs.asStringList(";")
+ if len(slist) != len(actualSlist) {
+ t.Errorf("unexpected LicenseConditionSet.asStringList(\";\"): got %v, want %v", actualSlist, slist)
+ } else {
+ sort.Strings(slist)
+ sort.Strings(actualSlist)
+ for i := 0; i < len(slist); i++ {
+ if slist[i] != actualSlist[i] {
+ t.Errorf("unexpected LicenseConditionSet.asStringList(\";\") index %d in %v: got %s, want %s", i, actualSlist, actualSlist[i], slist[i])
+ }
+ }
+ }
+ }
+
+ t.Run(tt.name+"_private_list_interface", func(t *testing.T) {
+ lg := newLicenseGraph()
+ cs := newLicenseConditionSet()
+ for name, origins := range tt.conditions {
+ for _, origin := range origins {
+ cs.add(newTestNode(lg, origin), name)
+ }
+ }
+ if tt.add != nil {
+ cs.Add(toConditionList(lg, tt.add)...)
+ }
+ testPrivateInterface(lg, cs, t)
+ })
+
+ t.Run(tt.name+"_private_set_interface", func(t *testing.T) {
+ lg := newLicenseGraph()
+ cs := newLicenseConditionSet()
+ for name, origins := range tt.conditions {
+ for _, origin := range origins {
+ cs.add(newTestNode(lg, origin), name)
+ }
+ }
+ if tt.add != nil {
+ other := newLicenseConditionSet()
+ for name, origins := range tt.add {
+ for _, origin := range origins {
+ other.add(newTestNode(lg, origin), name)
+ }
+ }
+ cs.AddSet(other)
+ }
+ testPrivateInterface(lg, cs, t)
+ })
+ }
+}
+
+func TestConditionSet_Removals(t *testing.T) {
+ tests := []struct {
+ name string
+ conditions map[string][]string
+ removeByName []ConditionNames
+ removeSet map[string][]string
+ byName map[string][]string
+ byOrigin map[string][]string
+ }{
+ {
+ name: "emptybyname",
+ conditions: map[string][]string{},
+ removeByName: []ConditionNames{{"reciprocal", "restricted"}},
+ byName: map[string][]string{
+ "notice": []string{},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{},
+ "lib1": []string{},
+ "bin2": []string{},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "emptybyset",
+ conditions: map[string][]string{},
+ removeSet: map[string][]string{
+ "notice": []string{"bin1", "bin2"},
+ "restricted": []string{"lib1", "lib2"},
+ },
+ byName: map[string][]string{
+ "notice": []string{},
+ "restricted": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{},
+ "lib1": []string{},
+ "bin2": []string{},
+ "lib2": []string{},
+ },
+ },
+ {
+ name: "everythingremovenone",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ removeByName: []ConditionNames{{"permissive", "unencumbered"}},
+ removeSet: map[string][]string{
+ "notice": []string{"apk1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "bin2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "lib2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "everythingremovesome",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ removeByName: []ConditionNames{{"restricted", "by_exception_only"}},
+ removeSet: map[string][]string{
+ "notice": []string{"lib1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{"bin1", "bin2", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{},
+ "by_exception_only": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{"notice", "reciprocal"},
+ "bin2": []string{"notice", "reciprocal"},
+ "lib1": []string{"reciprocal"},
+ "lib2": []string{"notice", "reciprocal"},
+ "other": []string{},
+ },
+ },
+ {
+ name: "everythingremoveall",
+ conditions: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1", "bin2", "lib1", "lib2"},
+ "by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+ },
+ removeByName: []ConditionNames{{"restricted", "by_exception_only"}},
+ removeSet: map[string][]string{
+ "notice": []string{"bin1", "bin2", "lib1", "lib2"},
+ "reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
+ "restricted": []string{"bin1"},
+ },
+ byName: map[string][]string{
+ "permissive": []string{},
+ "notice": []string{},
+ "reciprocal": []string{},
+ "restricted": []string{},
+ "by_exception_only": []string{},
+ },
+ byOrigin: map[string][]string{
+ "bin1": []string{},
+ "bin2": []string{},
+ "lib1": []string{},
+ "lib2": []string{},
+ "other": []string{},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ lg := newLicenseGraph()
+ cs := newLicenseConditionSet()
+ for name, origins := range tt.conditions {
+ for _, origin := range origins {
+ cs.add(newTestNode(lg, origin), name)
+ }
+ }
+ if tt.removeByName != nil {
+ cs.RemoveAllByName(tt.removeByName...)
+ }
+ if tt.removeSet != nil {
+ other := newLicenseConditionSet()
+ for name, origins := range tt.removeSet {
+ for _, origin := range origins {
+ other.add(newTestNode(lg, origin), name)
+ }
+ }
+ cs.RemoveSet(other)
+ }
+ byName(tt.byName).checkPublic(cs, t)
+ byOrigin(tt.byOrigin).checkPublic(lg, cs, t)
+ })
+ }
+}
diff --git a/tools/compliance/doc.go b/tools/compliance/doc.go
new file mode 100644
index 0000000..a47c1cf
--- /dev/null
+++ b/tools/compliance/doc.go
@@ -0,0 +1,77 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/*
+
+Package compliance provides an approved means for reading, consuming, and
+analyzing license metadata graphs.
+
+Assuming the license metadata and dependencies are fully and accurately
+recorded in the build system, any discrepancy between the official policy for
+open source license compliance and this code is a bug in this code.
+
+A few principal types to understand are LicenseGraph, LicenseCondition, and
+ResolutionSet.
+
+LicenseGraph
+------------
+
+A LicenseGraph is an immutable graph of the targets and dependencies reachable
+from a specific set of root targets. In general, the root targets will be the
+artifacts in a release or distribution. While conceptually immutable, parts of
+the graph may be loaded or evaluated lazily.
+
+LicenseCondition
+----------------
+
+A LicenseCondition is an immutable tuple pairing a condition name with an
+originating target. e.g. Per current policy, a static library licensed under an
+MIT license would pair a "notice" condition with the static library target, and
+a dynamic license licensed under GPL would pair a "restricted" condition with
+the dynamic library target.
+
+ResolutionSet
+-------------
+
+A ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`
+tuples describing how license conditions apply to targets.
+
+`AttachesTo` is the trigger for acting. Distribution of the target invokes
+the policy.
+
+`ActsOn` is the target to share, give notice for, hide etc.
+
+`Resolves` is the license condition that the action resolves.
+
+Remember: Each license condition pairs a condition name with an originating
+target so each resolution in a ResolutionSet has two targets it applies to and
+one target from which it originates, all of which may be the same target.
+
+For most condition types, `ActsOn` and `Resolves.Origin` will be the same
+target. For example, a notice condition policy means attribution or notice must
+be given for the target where the condition originates. Likewise, a proprietary
+condition policy means the privacy of the target where the condition originates
+must be respected. i.e. The thing acted on is the origin.
+
+Restricted conditions are different. The infectious nature of restricted often
+means sharing code that is not the target where the restricted condition
+originates. Linking an MIT library to a GPL library implies a policy to share
+the MIT library despite the MIT license having no source sharing requirement.
+
+In this case, one or more resolution tuples will have the MIT license module in
+`ActsOn` and the restricted condition originating at the GPL library module in
+`Resolves`. These tuples will `AttachTo` every target that depends on the GPL
+library because shipping any of those targets trigger the policy to share the
+code.
+*/
+package compliance
diff --git a/tools/compliance/graph.go b/tools/compliance/graph.go
new file mode 100644
index 0000000..9dcfa66
--- /dev/null
+++ b/tools/compliance/graph.go
@@ -0,0 +1,503 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+ "sync"
+)
+
+// LicenseGraph describes the immutable license metadata for a set of root
+// targets and the transitive closure of their dependencies.
+//
+// Alternatively, a graph is a set of edges. In this case directed, annotated
+// edges from targets to dependencies.
+//
+// A LicenseGraph provides the frame of reference for all of the other types
+// defined here. It is possible to have multiple graphs, and to have targets,
+// edges, and resolutions from multiple graphs. But it is an error to try to
+// mix items from different graphs in the same operation.
+// May panic if attempted.
+//
+// The compliance package assumes specific private implementations of each of
+// these interfaces. May panic if attempts are made to combine different
+// implementations of some interfaces with expected implementations of other
+// interfaces here.
+type LicenseGraph struct {
+ // rootFiles identifies the original set of files to read. (immutable)
+ //
+ // Defines the starting "top" for top-down walks.
+ //
+ // Alternatively, an instance of licenseGraphImp conceptually defines a scope within
+ // the universe of build graphs as a sub-graph rooted at rootFiles where all edges
+ // and targets for the instance are defined relative to and within that scope. For
+ // most analyses, the correct scope is to root the graph at all of the distributed
+ // artifacts.
+ rootFiles []string
+
+ // edges lists the directed edges in the graph from target to dependency. (guarded by mu)
+ //
+ // Alternatively, the graph is the set of `edges`.
+ edges []*dependencyEdge
+
+ // targets identifies, indexes by name, and describes the entire set of target node files.
+ /// (guarded by mu)
+ targets map[string]*TargetNode
+
+ // index facilitates looking up edges from targets. (creation guarded by my)
+ //
+ // This is a forward index from target to dependencies. i.e. "top-down"
+ index map[string][]*dependencyEdge
+
+ // rsBU caches the results of a full bottom-up resolve. (creation guarded by mu)
+ //
+ // A bottom-up resolve is a prerequisite for all of the top-down resolves so caching
+ // the result is a performance win.
+ rsBU *ResolutionSet
+
+ // rsTD caches the results of a full top-down resolve. (creation guarded by mu)
+ //
+ // A top-down resolve is a prerequisite for final resolutions.
+ // e.g. a shipped node inheriting a `restricted` condition from a parent through a
+ // dynamic dependency implies a notice dependency on the parent; even though, the
+ // distribution does not happen as a result of the dynamic dependency itself.
+ rsTD *ResolutionSet
+
+ // shippedNodes caches the results of a full walk of nodes identifying targets
+ // distributed either directly or as derivative works. (creation guarded by mu)
+ shippedNodes *TargetNodeSet
+
+ // mu guards against concurrent update.
+ mu sync.Mutex
+}
+
+// TargetNode returns the target node identified by `name`.
+func (lg *LicenseGraph) TargetNode(name string) *TargetNode {
+ if _, ok := lg.targets[name]; !ok {
+ panic(fmt.Errorf("target node %q missing from graph", name))
+ }
+ return lg.targets[name]
+}
+
+// HasTargetNode returns true if a target node identified by `name` appears in
+// the graph.
+func (lg *LicenseGraph) HasTargetNode(name string) bool {
+ _, isPresent := lg.targets[name]
+ return isPresent
+}
+
+// Edges returns the list of edges in the graph. (unordered)
+func (lg *LicenseGraph) Edges() TargetEdgeList {
+ edges := make(TargetEdgeList, 0, len(lg.edges))
+ for _, e := range lg.edges {
+ edges = append(edges, TargetEdge{lg, e})
+ }
+ return edges
+}
+
+// Targets returns the list of target nodes in the graph. (unordered)
+func (lg *LicenseGraph) Targets() TargetNodeList {
+ targets := make(TargetNodeList, 0, len(lg.targets))
+ for target := range lg.targets {
+ targets = append(targets, lg.targets[target])
+ }
+ return targets
+}
+
+// compliance-only LicenseGraph methods
+
+// newLicenseGraph constructs a new, empty instance of LicenseGraph.
+func newLicenseGraph() *LicenseGraph {
+ return &LicenseGraph{
+ rootFiles: []string{},
+ edges: make([]*dependencyEdge, 0, 1000),
+ targets: make(map[string]*TargetNode),
+ }
+}
+
+// indexForward guarantees the `index` map is populated to look up edges by
+// `target`.
+func (lg *LicenseGraph) indexForward() {
+ lg.mu.Lock()
+ defer func() {
+ lg.mu.Unlock()
+ }()
+
+ if lg.index != nil {
+ return
+ }
+
+ lg.index = make(map[string][]*dependencyEdge)
+ for _, e := range lg.edges {
+ if _, ok := lg.index[e.target]; ok {
+ lg.index[e.target] = append(lg.index[e.target], e)
+ } else {
+ lg.index[e.target] = []*dependencyEdge{e}
+ }
+ }
+}
+
+// TargetEdge describes a directed, annotated edge from a target to a
+// dependency. (immutable)
+//
+// A LicenseGraph, above, is a set of TargetEdges.
+//
+// i.e. `Target` depends on `Dependency` in the manner described by
+// `Annotations`.
+type TargetEdge struct {
+ // lg identifies the scope, i.e. license graph, in which the edge appears.
+ lg *LicenseGraph
+
+ // e identifies describes the target, dependency, and annotations of the edge.
+ e *dependencyEdge
+}
+
+// Target identifies the target that depends on the dependency.
+//
+// Target needs Dependency to build.
+func (e TargetEdge) Target() *TargetNode {
+ return e.lg.targets[e.e.target]
+}
+
+// Dependency identifies the target depended on by the target.
+//
+// Dependency builds without Target, but Target needs Dependency to build.
+func (e TargetEdge) Dependency() *TargetNode {
+ return e.lg.targets[e.e.dependency]
+}
+
+// Annotations describes the type of edge by the set of annotations attached to
+// it.
+//
+// Only annotations prescribed by policy have any meaning for licensing, and
+// the meaning for licensing is likewise prescribed by policy. Other annotations
+// are preserved and ignored by policy.
+func (e TargetEdge) Annotations() TargetEdgeAnnotations {
+ return e.e.annotations
+}
+
+// TargetEdgeList orders lists of edges by target then dependency then annotations.
+type TargetEdgeList []TargetEdge
+
+// Len returns the count of the elmements in the list.
+func (l TargetEdgeList) Len() int { return len(l) }
+
+// Swap rearranges 2 elements so that each occupies the other's former position.
+func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than the `j`th.
+func (l TargetEdgeList) Less(i, j int) bool {
+ if l[i].e.target == l[j].e.target {
+ if l[i].e.dependency == l[j].e.dependency {
+ return l[i].e.annotations.Compare(l[j].e.annotations) < 0
+ }
+ return l[i].e.dependency < l[j].e.dependency
+ }
+ return l[i].e.target < l[j].e.target
+}
+
+// TargetEdgePath describes a sequence of edges starting at a root and ending
+// at some final dependency.
+type TargetEdgePath []TargetEdge
+
+// NewTargetEdgePath creates a new, empty path with capacity `cap`.
+func NewTargetEdgePath(cap int) *TargetEdgePath {
+ p := make(TargetEdgePath, 0, cap)
+ return &p
+}
+
+// Push appends a new edge to the list verifying that the target of the new
+// edge is the dependency of the prior.
+func (p *TargetEdgePath) Push(edge TargetEdge) {
+ if len(*p) == 0 {
+ *p = append(*p, edge)
+ return
+ }
+ if (*p)[len(*p)-1].e.dependency != edge.e.target {
+ panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.e.target))
+ }
+ *p = append(*p, edge)
+}
+
+// Pop shortens the path by 1 edge.
+func (p *TargetEdgePath) Pop() {
+ if len(*p) == 0 {
+ panic(fmt.Errorf("attempt to remove edge from empty path"))
+ }
+ *p = (*p)[:len(*p)-1]
+}
+
+// Clear makes the path length 0.
+func (p *TargetEdgePath) Clear() {
+ *p = (*p)[:0]
+}
+
+// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
+func (p *TargetEdgePath) String() string {
+ if p == nil {
+ return "nil"
+ }
+ if len(*p) == 0 {
+ return "[]"
+ }
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "[")
+ for _, e := range *p {
+ fmt.Fprintf(&sb, "%s -> ", e.e.target)
+ }
+ fmt.Fprintf(&sb, "%s]", (*p)[len(*p)-1].e.dependency)
+ return sb.String()
+}
+
+// TargetNode describes a module or target identified by the name of a specific
+// metadata file. (immutable)
+//
+// Each metadata file corresponds to a Soong module or to a Make target.
+//
+// A target node can appear as the target or as the dependency in edges.
+// Most target nodes appear as both target in one edge and as dependency in
+// other edges.
+type TargetNode targetNode
+
+// Name returns the string that identifies the target node.
+// i.e. path to license metadata file
+func (tn *TargetNode) Name() string {
+ return tn.name
+}
+
+// PackageName returns the string that identifes the package for the target.
+func (tn *TargetNode) PackageName() string {
+ return tn.proto.GetPackageName()
+}
+
+// ModuleTypes returns the list of module types implementing the target.
+// (unordered)
+//
+// In an ideal world, only 1 module type would implement each target, but the
+// interactions between Soong and Make for host versus product and for a
+// variety of architectures sometimes causes multiple module types per target
+// (often a regular build target and a prebuilt.)
+func (tn *TargetNode) ModuleTypes() []string {
+ return append([]string{}, tn.proto.ModuleTypes...)
+}
+
+// ModuleClasses returns the list of module classes implementing the target.
+// (unordered)
+func (tn *TargetNode) ModuleClasses() []string {
+ return append([]string{}, tn.proto.ModuleClasses...)
+}
+
+// Projects returns the projects defining the target node. (unordered)
+//
+// In an ideal world, only 1 project defines a target, but the interaction
+// between Soong and Make for a variety of architectures and for host versus
+// product means a module is sometimes defined more than once.
+func (tn *TargetNode) Projects() []string {
+ return append([]string{}, tn.proto.Projects...)
+}
+
+// LicenseKinds returns the list of license kind names for the module or
+// target. (unordered)
+//
+// e.g. SPDX-license-identifier-MIT or legacy_proprietary
+func (tn *TargetNode) LicenseKinds() []string {
+ return append([]string{}, tn.proto.LicenseKinds...)
+}
+
+// LicenseConditions returns a copy of the set of license conditions
+// originating at the target. The values that appear and how each is resolved
+// is a matter of policy. (unordered)
+//
+// e.g. notice or proprietary
+func (tn *TargetNode) LicenseConditions() *LicenseConditionSet {
+ result := newLicenseConditionSet()
+ result.add(tn, tn.proto.LicenseConditions...)
+ return result
+}
+
+// LicenseTexts returns the paths to the files containing the license texts for
+// the target. (unordered)
+func (tn *TargetNode) LicenseTexts() []string {
+ return append([]string{}, tn.proto.LicenseTexts...)
+}
+
+// IsContainer returns true if the target represents a container that merely
+// aggregates other targets.
+func (tn *TargetNode) IsContainer() bool {
+ return tn.proto.GetIsContainer()
+}
+
+// Built returns the list of files built by the module or target. (unordered)
+func (tn *TargetNode) Built() []string {
+ return append([]string{}, tn.proto.Built...)
+}
+
+// Installed returns the list of files installed by the module or target.
+// (unordered)
+func (tn *TargetNode) Installed() []string {
+ return append([]string{}, tn.proto.Installed...)
+}
+
+// InstallMap returns the list of path name transformations to make to move
+// files from their original location in the file system to their destination
+// inside a container. (unordered)
+func (tn *TargetNode) InstallMap() []InstallMap {
+ result := make([]InstallMap, 0, len(tn.proto.InstallMap))
+ for _, im := range tn.proto.InstallMap {
+ result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
+ }
+ return result
+}
+
+// Sources returns the list of file names depended on by the target, which may
+// be a proper subset of those made available by dependency modules.
+// (unordered)
+func (tn *TargetNode) Sources() []string {
+ return append([]string{}, tn.proto.Sources...)
+}
+
+// InstallMap describes the mapping from an input filesystem file to file in a
+// container.
+type InstallMap struct {
+ // FromPath is the input path on the filesystem.
+ FromPath string
+
+ // ContainerPath is the path to the same file inside the container or
+ // installed location.
+ ContainerPath string
+}
+
+// TargetEdgeAnnotations describes an immutable set of annotations attached to
+// an edge from a target to a dependency.
+//
+// Annotations typically distinguish between static linkage versus dynamic
+// versus tools that are used at build time but are not linked in any way.
+type TargetEdgeAnnotations struct {
+ annotations map[string]bool
+}
+
+// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
+func newEdgeAnnotations() TargetEdgeAnnotations {
+ return TargetEdgeAnnotations{make(map[string]bool)}
+}
+
+// HasAnnotation returns true if an annotation `ann` is in the set.
+func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
+ _, ok := ea.annotations[ann]
+ return ok
+}
+
+// Compare orders TargetAnnotations returning:
+// -1 when ea < other,
+// +1 when ea > other, and
+// 0 when ea == other.
+func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
+ a1 := ea.AsList()
+ a2 := other.AsList()
+ sort.Strings(a1)
+ sort.Strings(a2)
+ for k := 0; k < len(a1) && k < len(a2); k++ {
+ if a1[k] < a2[k] {
+ return -1
+ }
+ if a1[k] > a2[k] {
+ return 1
+ }
+ }
+ if len(a1) < len(a2) {
+ return -1
+ }
+ if len(a1) > len(a2) {
+ return 1
+ }
+ return 0
+}
+
+// AsList returns the list of annotation names attached to the edge.
+// (unordered)
+func (ea TargetEdgeAnnotations) AsList() []string {
+ l := make([]string, 0, len(ea.annotations))
+ for ann := range ea.annotations {
+ l = append(l, ann)
+ }
+ return l
+}
+
+// TargetNodeSet describes a set of distinct nodes in a license graph.
+type TargetNodeSet struct {
+ nodes map[*TargetNode]bool
+}
+
+// Contains returns true when `target` is an element of the set.
+func (ts *TargetNodeSet) Contains(target *TargetNode) bool {
+ _, isPresent := ts.nodes[target]
+ return isPresent
+}
+
+// AsList returns the list of target nodes in the set. (unordered)
+func (ts *TargetNodeSet) AsList() TargetNodeList {
+ result := make(TargetNodeList, 0, len(ts.nodes))
+ for tn := range ts.nodes {
+ result = append(result, tn)
+ }
+ return result
+}
+
+// Names returns the array of target node namess in the set. (unordered)
+func (ts *TargetNodeSet) Names() []string {
+ result := make([]string, 0, len(ts.nodes))
+ for tn := range ts.nodes {
+ result = append(result, tn.name)
+ }
+ return result
+}
+
+// TargetNodeList orders a list of targets by name.
+type TargetNodeList []*TargetNode
+
+// Len returns the count of elements in the list.
+func (l TargetNodeList) Len() int { return len(l) }
+
+// Swap rearranges 2 elements so that each occupies the other's former position.
+func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographicallt less than the `j`th.
+func (l TargetNodeList) Less(i, j int) bool {
+ return l[i].name < l[j].name
+}
+
+// String returns a string representation of the list.
+func (l TargetNodeList) String() string {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "[")
+ sep := ""
+ for _, tn := range l {
+ fmt.Fprintf(&sb, "%s%s", sep, tn.name)
+ sep = " "
+ }
+ fmt.Fprintf(&sb, "]")
+ return sb.String()
+}
+
+// Names returns an array the names of the nodes in the same order as the nodes in the list.
+func (l TargetNodeList) Names() []string {
+ result := make([]string, 0, len(l))
+ for _, tn := range l {
+ result = append(result, tn.name)
+ }
+ return result
+}
diff --git a/tools/compliance/policy/policy.go b/tools/compliance/policy/policy.go
new file mode 100644
index 0000000..9dab05b
--- /dev/null
+++ b/tools/compliance/policy/policy.go
@@ -0,0 +1,238 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "regexp"
+ "strings"
+)
+
+var (
+ // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
+ ImpliesUnencumbered = ConditionNames{"unencumbered"}
+
+ // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
+ ImpliesPermissive = ConditionNames{"permissive"}
+
+ // ImpliesNotice lists the condition names implying a notice or attribution policy.
+ ImpliesNotice = ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary", "by_exception_only"}
+
+ // ImpliesReciprocal lists the condition names implying a local source-sharing policy.
+ ImpliesReciprocal = ConditionNames{"reciprocal"}
+
+ // Restricted lists the condition names implying an infectious source-sharing policy.
+ ImpliesRestricted = ConditionNames{"restricted"}
+
+ // ImpliesProprietary lists the condition names implying a confidentiality policy.
+ ImpliesProprietary = ConditionNames{"proprietary"}
+
+ // ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
+ ImpliesByExceptionOnly = ConditionNames{"proprietary", "by_exception_only"}
+
+ // ImpliesPrivate lists the condition names implying a source-code privacy policy.
+ ImpliesPrivate = ConditionNames{"proprietary"}
+
+ // ImpliesShared lists the condition names implying a source-code sharing policy.
+ ImpliesShared = ConditionNames{"reciprocal", "restricted"}
+)
+
+var (
+ anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
+ versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
+ genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
+ ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
+)
+
+// Resolution happens in two passes:
+//
+// 1. A bottom-up traversal propagates license conditions up to targets from
+// dendencies as needed.
+//
+// 2. For each condition of interest, a top-down traversal adjusts the attached
+// conditions pushing restricted down from targets into linked dependencies.
+//
+// The behavior of the 2 passes gets controlled by the 2 functions below.
+//
+// The first function controls what happens during the bottom-up traversal. In
+// general conditions flow up through static links but not other dependencies;
+// except, restricted sometimes flows up through dynamic links.
+//
+// In general, too, the originating target gets acted on to resolve the
+// condition (e.g. providing notice), but again restricted is special in that
+// it requires acting on (i.e. sharing source of) both the originating module
+// and the target using the module.
+//
+// The latter function controls what happens during the top-down traversal. In
+// general, only restricted conditions flow down at all, and only through
+// static links.
+//
+// Not all restricted licenses are create equal. Some have special rules or
+// exceptions. e.g. LGPL or "with classpath excption".
+
+// depActionsApplicableToTarget returns the actions which propagate up an
+// edge from dependency to target.
+//
+// This function sets the policy for the bottom-up traversal and how conditions
+// flow up the graph from dependencies to targets.
+//
+// If a pure aggregation is built into a derivative work that is not a pure
+// aggregation, per policy it ceases to be a pure aggregation in the context of
+// that derivative work. The `treatAsAggregate` parameter will be false for
+// non-aggregates and for aggregates in non-aggregate contexts.
+func depActionsApplicableToTarget(e TargetEdge, depActions actionSet, treatAsAggregate bool) actionSet {
+ result := make(actionSet)
+ if edgeIsDerivation(e) {
+ result.addSet(depActions)
+ for _, cs := range depActions.byName(ImpliesRestricted) {
+ result.add(e.Target(), cs)
+ }
+ return result
+ }
+ if !edgeIsDynamicLink(e) {
+ return result
+ }
+
+ restricted := depActions.byName(ImpliesRestricted)
+ for actsOn, cs := range restricted {
+ for _, lc := range cs.AsList() {
+ hasGpl := false
+ hasLgpl := false
+ hasClasspath := false
+ hasGeneric := false
+ hasOther := false
+ for _, kind := range lc.origin.LicenseKinds() {
+ if strings.HasSuffix(kind, "-with-classpath-exception") {
+ hasClasspath = true
+ } else if anyLgpl.MatchString(kind) {
+ hasLgpl = true
+ } else if versionedGpl.MatchString(kind) {
+ hasGpl = true
+ } else if genericGpl.MatchString(kind) {
+ hasGeneric = true
+ } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
+ hasOther = true
+ }
+ }
+ if hasOther || hasGpl {
+ result.addCondition(actsOn, lc)
+ result.addCondition(e.Target(), lc)
+ continue
+ }
+ if hasClasspath && !edgeNodesAreIndependentModules(e) {
+ result.addCondition(actsOn, lc)
+ result.addCondition(e.Target(), lc)
+ continue
+ }
+ if hasLgpl || hasClasspath {
+ continue
+ }
+ if !hasGeneric {
+ continue
+ }
+ result.addCondition(actsOn, lc)
+ result.addCondition(e.Target(), lc)
+ }
+ }
+ return result
+}
+
+// targetConditionsApplicableToDep returns the conditions which propagate down
+// an edge from target to dependency.
+//
+// This function sets the policy for the top-down traversal and how conditions
+// flow down the graph from targets to dependencies.
+//
+// If a pure aggregation is built into a derivative work that is not a pure
+// aggregation, per policy it ceases to be a pure aggregation in the context of
+// that derivative work. The `treatAsAggregate` parameter will be false for
+// non-aggregates and for aggregates in non-aggregate contexts.
+func targetConditionsApplicableToDep(e TargetEdge, targetConditions *LicenseConditionSet, treatAsAggregate bool) *LicenseConditionSet {
+ result := targetConditions.Copy()
+
+ // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
+ result.RemoveAllByName(ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "proprietary", "by_exception_only"})
+
+ if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
+ // target is not a derivative work of dependency and is not linked to dependency
+ result.RemoveAllByName(ImpliesRestricted)
+ return result
+ }
+ if treatAsAggregate {
+ // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
+ // Otherwise, restricted does not propagate back down to dependencies.
+ restricted := result.ByName(ImpliesRestricted).AsList()
+ for _, lc := range restricted {
+ if lc.origin.name != e.e.target {
+ result.Remove(lc)
+ }
+ }
+ return result
+ }
+ if edgeIsDerivation(e) {
+ return result
+ }
+ restricted := result.ByName(ImpliesRestricted).AsList()
+ for _, lc := range restricted {
+ hasGpl := false
+ hasLgpl := false
+ hasClasspath := false
+ hasGeneric := false
+ hasOther := false
+ for _, kind := range lc.origin.LicenseKinds() {
+ if strings.HasSuffix(kind, "-with-classpath-exception") {
+ hasClasspath = true
+ } else if anyLgpl.MatchString(kind) {
+ hasLgpl = true
+ } else if versionedGpl.MatchString(kind) {
+ hasGpl = true
+ } else if genericGpl.MatchString(kind) {
+ hasGeneric = true
+ } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
+ hasOther = true
+ }
+ }
+ if hasOther || hasGpl {
+ continue
+ }
+ if hasClasspath && !edgeNodesAreIndependentModules(e) {
+ continue
+ }
+ if hasGeneric && !hasLgpl && !hasClasspath {
+ continue
+ }
+ result.Remove(lc)
+ }
+ return result
+}
+
+// edgeIsDynamicLink returns true for edges representing shared libraries
+// linked dynamically at runtime.
+func edgeIsDynamicLink(e TargetEdge) bool {
+ return e.e.annotations.HasAnnotation("dynamic")
+}
+
+// edgeIsDerivation returns true for edges where the target is a derivative
+// work of dependency.
+func edgeIsDerivation(e TargetEdge) bool {
+ isDynamic := e.e.annotations.HasAnnotation("dynamic")
+ isToolchain := e.e.annotations.HasAnnotation("toolchain")
+ return !isDynamic && !isToolchain
+}
+
+// edgeNodesAreIndependentModules returns true for edges where the target and
+// dependency are independent modules.
+func edgeNodesAreIndependentModules(e TargetEdge) bool {
+ return e.Target().PackageName() != e.Dependency().PackageName()
+}
diff --git a/tools/compliance/policy/policy_test.go b/tools/compliance/policy/policy_test.go
new file mode 100644
index 0000000..aea307f
--- /dev/null
+++ b/tools/compliance/policy/policy_test.go
@@ -0,0 +1,300 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestPolicy_edgeConditions(t *testing.T) {
+ tests := []struct {
+ name string
+ edge annotated
+ treatAsAggregate bool
+ otherCondition string
+ expectedDepActions []string
+ expectedTargetConditions []string
+ }{
+ {
+ name: "firstparty",
+ edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "notice",
+ edge: annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fponlgpl",
+ edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:lgplLib.meta_lic:restricted",
+ "lgplLib.meta_lic:lgplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fponlgpldynamic",
+ edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fpongpl",
+ edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fpongpldynamic",
+ edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "independentmodule",
+ edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "independentmodulestatic",
+ edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ "gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "dependentmodule",
+ edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{
+ "dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ "gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+
+ {
+ name: "lgplonfp",
+ edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{"lgplBin.meta_lic:restricted"},
+ },
+ {
+ name: "lgplonfpdynamic",
+ edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "gplonfp",
+ edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
+ },
+ {
+ name: "gplcontainer",
+ edge: annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ treatAsAggregate: true,
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
+ },
+ {
+ name: "gploncontainer",
+ edge: annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ treatAsAggregate: true,
+ otherCondition: "gplLib.meta_lic:restricted",
+ expectedDepActions: []string{
+ "apacheContainer.meta_lic:gplLib.meta_lic:restricted",
+ "apacheLib.meta_lic:apacheLib.meta_lic:notice",
+ "apacheLib.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "gplonbin",
+ edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ treatAsAggregate: false,
+ otherCondition: "gplLib.meta_lic:restricted",
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplLib.meta_lic:restricted",
+ "apacheLib.meta_lic:apacheLib.meta_lic:notice",
+ "apacheLib.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{"gplLib.meta_lic:restricted"},
+ },
+ {
+ name: "gplonfpdynamic",
+ edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
+ },
+ {
+ name: "independentmodulereverse",
+ edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "independentmodulereversestatic",
+ edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheBin.meta_lic:apacheBin.meta_lic:notice"},
+ expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
+ },
+ {
+ name: "dependentmodulereverse",
+ edge: annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
+ },
+ {
+ name: "ponr",
+ edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "proprietary.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "ronp",
+ edge: annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"proprietary.meta_lic:proprietary.meta_lic:proprietary"},
+ expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
+ },
+ {
+ name: "noticeonb_e_o",
+ edge: annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"by_exception.meta_lic:by_exception.meta_lic:by_exception_only"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "b_e_oonnotice",
+ edge: annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "noticeonrecip",
+ edge: annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mplLib.meta_lic:mplLib.meta_lic:reciprocal"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "reciponnotice",
+ edge: annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fs := make(testFS)
+ stderr := &bytes.Buffer{}
+ target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n file: \"%s\"\n", tt.edge.dep)
+ for _, ann := range tt.edge.annotations {
+ target += fmt.Sprintf(" annotations: \"%s\"\n", ann)
+ }
+ fs[tt.edge.target] = []byte(target + "}\n")
+ fs[tt.edge.dep] = []byte(meta[tt.edge.dep])
+ lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})
+ if err != nil {
+ t.Errorf("unexpected error reading graph: %w", err)
+ return
+ }
+ // simulate a condition inherited from another edge/dependency.
+ otherTarget := ""
+ otherCondition := ""
+ if len(tt.otherCondition) > 0 {
+ fields := strings.Split(tt.otherCondition, ":")
+ otherTarget = fields[0]
+ otherCondition = fields[1]
+ // other target must exist in graph
+ lg.targets[otherTarget] = &TargetNode{name: otherTarget}
+ lg.targets[otherTarget].proto.LicenseConditions = append(lg.targets[otherTarget].proto.LicenseConditions, otherCondition)
+ }
+ if tt.expectedDepActions != nil {
+ depActions := make(actionSet)
+ depActions[lg.targets[tt.edge.dep]] = lg.targets[tt.edge.dep].LicenseConditions()
+ if otherTarget != "" {
+ // simulate a sub-dependency's condition having already propagated up to dep and about to go to target
+ otherCs := lg.targets[otherTarget].LicenseConditions()
+ depActions[lg.targets[tt.edge.dep]].AddSet(otherCs)
+ depActions[lg.targets[otherTarget]] = otherCs
+ }
+ asActual := depActionsApplicableToTarget(lg.Edges()[0], depActions, tt.treatAsAggregate)
+ asExpected := make(actionSet)
+ for _, triple := range tt.expectedDepActions {
+ fields := strings.Split(triple, ":")
+ actsOn := lg.targets[fields[0]]
+ origin := lg.targets[fields[1]]
+ expectedConditions := newLicenseConditionSet()
+ expectedConditions.add(origin, fields[2:]...)
+ if _, ok := asExpected[actsOn]; ok {
+ asExpected[actsOn].AddSet(expectedConditions)
+ } else {
+ asExpected[actsOn] = expectedConditions
+ }
+ }
+
+ checkSameActions(lg, asActual, asExpected, t)
+ }
+ if tt.expectedTargetConditions != nil {
+ targetConditions := lg.TargetNode(tt.edge.target).LicenseConditions()
+ if otherTarget != "" {
+ targetConditions.add(lg.targets[otherTarget], otherCondition)
+ }
+ cs := targetConditionsApplicableToDep(
+ lg.Edges()[0],
+ targetConditions,
+ tt.treatAsAggregate)
+ actual := make([]string, 0, cs.Count())
+ for _, lc := range cs.AsList() {
+ actual = append(actual, lc.asString(":"))
+ }
+ sort.Strings(actual)
+ sort.Strings(tt.expectedTargetConditions)
+ if len(actual) != len(tt.expectedTargetConditions) {
+ t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
+ actual, len(actual), tt.expectedTargetConditions, len(tt.expectedTargetConditions))
+ } else {
+ for i := 0; i < len(actual); i++ {
+ if actual[i] != tt.expectedTargetConditions[i] {
+ t.Errorf("unexpected target condition at element %d: got %q, want %q",
+ i, actual[i], tt.expectedTargetConditions[i])
+ }
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolve.go b/tools/compliance/policy/resolve.go
new file mode 100644
index 0000000..58547f8
--- /dev/null
+++ b/tools/compliance/policy/resolve.go
@@ -0,0 +1,241 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
+// propagating conditions up the graph as necessary according to the properties
+// of each edge and according to each license condition in question.
+//
+// Subsequent top-down walks of the graph will filter some resolutions and may
+// introduce new resolutions.
+//
+// e.g. if a "restricted" condition applies to a binary, it also applies to all
+// of the statically-linked libraries and the transitive closure of their static
+// dependencies; even if neither they nor the transitive closure of their
+// dependencies originate any "restricted" conditions. The bottom-up walk will
+// not resolve the library and its transitive closure, but the later top-down
+// walk will.
+func ResolveBottomUpConditions(lg *LicenseGraph) *ResolutionSet {
+
+ // short-cut if already walked and cached
+ lg.mu.Lock()
+ rs := lg.rsBU
+ lg.mu.Unlock()
+
+ if rs != nil {
+ return rs
+ }
+
+ // must be indexed for fast lookup
+ lg.indexForward()
+
+ rs = resolveBottomUp(lg, make(map[*TargetNode]actionSet) /* empty map; no prior resolves */)
+
+ // if not yet cached, save the result
+ lg.mu.Lock()
+ if lg.rsBU == nil {
+ lg.rsBU = rs
+ } else {
+ // if we end up with 2, release the later for garbage collection
+ rs = lg.rsBU
+ }
+ lg.mu.Unlock()
+
+ return rs
+}
+
+// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
+// resolving all reachable nodes for `condition`. Policy establishes the rules
+// for transforming and propagating resolutions down the graph.
+//
+// e.g. For current policy, none of the conditions propagate from target to
+// dependency except restricted. For restricted, the policy is to share the
+// source of any libraries linked to restricted code and to provide notice.
+func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
+
+ // short-cut if already walked and cached
+ lg.mu.Lock()
+ rs := lg.rsTD
+ lg.mu.Unlock()
+
+ if rs != nil {
+ return rs
+ }
+
+ // start with the conditions propagated up the graph
+ rs = ResolveBottomUpConditions(lg)
+
+ // rmap maps 'appliesTo' targets to their applicable conditions
+ //
+ // rmap is the resulting ResolutionSet
+ rmap := make(map[*TargetNode]actionSet)
+
+ // cmap contains the set of targets walked as pure aggregates. i.e. containers
+ cmap := make(map[*TargetNode]bool)
+
+ var walk func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool)
+
+ walk = func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool) {
+ if _, ok := rmap[fnode]; !ok {
+ rmap[fnode] = make(actionSet)
+ }
+ rmap[fnode].add(fnode, cs)
+ if treatAsAggregate {
+ cmap[fnode] = true
+ }
+ // add conditions attached to `fnode`
+ cs = cs.Copy()
+ for _, fcs := range rs.resolutions[fnode] {
+ cs.AddSet(fcs)
+ }
+ // for each dependency
+ for _, edge := range lg.index[fnode.name] {
+ e := TargetEdge{lg, edge}
+ // dcs holds the dpendency conditions inherited from the target
+ dcs := targetConditionsApplicableToDep(e, cs, treatAsAggregate)
+ if dcs.IsEmpty() && !treatAsAggregate {
+ continue
+ }
+ dnode := lg.targets[edge.dependency]
+ if as, alreadyWalked := rmap[dnode]; alreadyWalked {
+ diff := dcs.Copy()
+ diff.RemoveSet(as.conditions())
+ if diff.IsEmpty() {
+ // no new conditions
+
+ // pure aggregates never need walking a 2nd time with same conditions
+ if treatAsAggregate {
+ continue
+ }
+ // non-aggregates don't need walking as non-aggregate a 2nd time
+ if _, asAggregate := cmap[dnode]; !asAggregate {
+ continue
+ }
+ // previously walked as pure aggregate; need to re-walk as non-aggregate
+ delete(cmap, dnode)
+ }
+ }
+ // add the conditions to the dependency
+ walk(dnode, dcs, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
+ }
+ }
+
+ // walk each of the roots
+ for _, r := range lg.rootFiles {
+ rnode := lg.targets[r]
+ as, ok := rs.resolutions[rnode]
+ if !ok {
+ // no conditions in root or transitive closure of dependencies
+ continue
+ }
+ if as.isEmpty() {
+ continue
+ }
+
+ // add the conditions to the root and its transitive closure
+ walk(rnode, newLicenseConditionSet(), lg.targets[r].IsContainer())
+ }
+
+ // back-fill any bottom-up conditions on targets missed by top-down walk
+ for attachesTo, as := range rs.resolutions {
+ if _, ok := rmap[attachesTo]; !ok {
+ rmap[attachesTo] = as.copy()
+ } else {
+ rmap[attachesTo].addSet(as)
+ }
+ }
+
+ // propagate any new conditions back up the graph
+ rs = resolveBottomUp(lg, rmap)
+
+ // if not yet cached, save the result
+ lg.mu.Lock()
+ if lg.rsTD == nil {
+ lg.rsTD = rs
+ } else {
+ // if we end up with 2, release the later for garbage collection
+ rs = lg.rsTD
+ }
+ lg.mu.Unlock()
+
+ return rs
+}
+
+// resolveBottomUp implements a bottom-up resolve propagating conditions both
+// from the graph, and from a `priors` map of resolutions.
+func resolveBottomUp(lg *LicenseGraph, priors map[*TargetNode]actionSet) *ResolutionSet {
+ rs := newResolutionSet()
+
+ // cmap contains an entry for every target that was previously walked as a pure aggregate only.
+ cmap := make(map[string]bool)
+
+ var walk func(f string, treatAsAggregate bool) actionSet
+
+ walk = func(f string, treatAsAggregate bool) actionSet {
+ target := lg.targets[f]
+ result := make(actionSet)
+ result[target] = newLicenseConditionSet()
+ result[target].add(target, target.proto.LicenseConditions...)
+ if pas, ok := priors[target]; ok {
+ result.addSet(pas)
+ }
+ if preresolved, ok := rs.resolutions[target]; ok {
+ if treatAsAggregate {
+ result.addSet(preresolved)
+ return result
+ }
+ if _, asAggregate := cmap[f]; !asAggregate {
+ result.addSet(preresolved)
+ return result
+ }
+ // previously walked in a pure aggregate context,
+ // needs to walk again in non-aggregate context
+ delete(cmap, f)
+ }
+ if treatAsAggregate {
+ cmap[f] = true
+ }
+
+ // add all the conditions from all the dependencies
+ for _, edge := range lg.index[f] {
+ // walk dependency to get its conditions
+ as := walk(edge.dependency, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
+
+ // turn those into the conditions that apply to the target
+ as = depActionsApplicableToTarget(TargetEdge{lg, edge}, as, treatAsAggregate)
+
+ // add them to the result
+ result.addSet(as)
+ }
+
+ // record these conditions as applicable to the target
+ rs.addConditions(target, result)
+ if len(priors) == 0 {
+ // on the first bottom-up resolve, parents have their own sharing and notice needs
+ // on the later resolve, if priors is empty, there will be nothing new to add
+ rs.addSelf(target, result.byName(ImpliesRestricted))
+ }
+
+ // return this up the tree
+ return result
+ }
+
+ // walk each of the roots
+ for _, r := range lg.rootFiles {
+ _ = walk(r, lg.targets[r].IsContainer())
+ }
+
+ return rs
+}
diff --git a/tools/compliance/policy/resolve_test.go b/tools/compliance/policy/resolve_test.go
new file mode 100644
index 0000000..4c99d35
--- /dev/null
+++ b/tools/compliance/policy/resolve_test.go
@@ -0,0 +1,764 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveBottomUpConditions(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartytool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartywide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpath",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependent",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependentdynamic",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveBottomUpConditions(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
+
+func TestResolveTopDownConditions(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"mplLib.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "restrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"mplLib.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "restricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpath",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdependent",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdependentdynamic",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveTopDownConditions(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolvenotices.go b/tools/compliance/policy/resolvenotices.go
new file mode 100644
index 0000000..80b5e02
--- /dev/null
+++ b/tools/compliance/policy/resolvenotices.go
@@ -0,0 +1,21 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveNotices implements the policy for notices.
+func ResolveNotices(lg *LicenseGraph) *ResolutionSet {
+ rs := ResolveTopDownConditions(lg)
+ return WalkResolutionsForCondition(lg, rs, ImpliesNotice)
+}
diff --git a/tools/compliance/policy/resolvenotices_test.go b/tools/compliance/policy/resolvenotices_test.go
new file mode 100644
index 0000000..275c0a5
--- /dev/null
+++ b/tools/compliance/policy/resolvenotices_test.go
@@ -0,0 +1,468 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveNotices(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "mitLib.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicwideshipped",
+ roots: []string{"apacheContainer.meta_lic", "gplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestrictedtoolshipped",
+ roots: []string{"apacheBin.meta_lic", "lgplBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "lgplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeepshipped",
+ roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwideshipped",
+ roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpath",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependent",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependentdynamic",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependentdynamicshipped",
+ roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveNotices(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolveprivacy.go b/tools/compliance/policy/resolveprivacy.go
new file mode 100644
index 0000000..dabbc62
--- /dev/null
+++ b/tools/compliance/policy/resolveprivacy.go
@@ -0,0 +1,21 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveSourcePrivacy implements the policy for source privacy.
+func ResolveSourcePrivacy(lg *LicenseGraph) *ResolutionSet {
+ rs := ResolveTopDownConditions(lg)
+ return WalkResolutionsForCondition(lg, rs, ImpliesPrivate)
+}
diff --git a/tools/compliance/policy/resolveprivacy_test.go b/tools/compliance/policy/resolveprivacy_test.go
new file mode 100644
index 0000000..25772bb
--- /dev/null
+++ b/tools/compliance/policy/resolveprivacy_test.go
@@ -0,0 +1,87 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveSourcePrivacy(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "notice",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "lgpl",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "proprietaryonresricted",
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ {
+ name: "restrictedonproprietary",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveSourcePrivacy(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolveshare.go b/tools/compliance/policy/resolveshare.go
new file mode 100644
index 0000000..24efd28
--- /dev/null
+++ b/tools/compliance/policy/resolveshare.go
@@ -0,0 +1,21 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveSourceSharing implements the policy for source-sharing.
+func ResolveSourceSharing(lg *LicenseGraph) *ResolutionSet {
+ rs := ResolveTopDownConditions(lg)
+ return WalkResolutionsForCondition(lg, rs, ImpliesShared)
+}
diff --git a/tools/compliance/policy/resolveshare_test.go b/tools/compliance/policy/resolveshare_test.go
new file mode 100644
index 0000000..ad3630d
--- /dev/null
+++ b/tools/compliance/policy/resolveshare_test.go
@@ -0,0 +1,297 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveSourceSharing(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "independentmodulerestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "independentmodulerestrictedshipped",
+ roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulestaticrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulerestricted",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulerestrictedshipclasspath",
+ roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfprestricted",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicrestricted",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicrestrictedshiplib",
+ roots: []string{"lgplBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfprestricted",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplcontainerrestricted",
+ roots: []string{"gplContainer.meta_lic"},
+ edges: []annotated{
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gploncontainerrestricted",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonbinrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestricted",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestrictedshiplib",
+ roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereverserestricted",
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversestaticrestricted",
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestricted",
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestrictedshipdependent",
+ roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrrestricted",
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ronprestricted",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "noticeonb_e_orestricted",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "b_e_oonnoticerestricted",
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "noticeonreciprecip",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "reciponnoticerecip",
+ roots: []string{"mplBin.meta_lic"},
+ edges: []annotated{
+ {"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveSourceSharing(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/shareprivacyconflicts.go b/tools/compliance/policy/shareprivacyconflicts.go
new file mode 100644
index 0000000..dabdff5
--- /dev/null
+++ b/tools/compliance/policy/shareprivacyconflicts.go
@@ -0,0 +1,91 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+)
+
+// SourceSharePrivacyConflict describes an individual conflict between a source-sharing
+// condition and a source privacy condition
+type SourceSharePrivacyConflict struct {
+ SourceNode *TargetNode
+ ShareCondition LicenseCondition
+ PrivacyCondition LicenseCondition
+}
+
+// Error returns a string describing the conflict.
+func (conflict SourceSharePrivacyConflict) Error() string {
+ return fmt.Sprintf("%s %s from %s and must share from %s %s\n",
+ conflict.SourceNode.name,
+ conflict.PrivacyCondition.name, conflict.PrivacyCondition.origin.name,
+ conflict.ShareCondition.name, conflict.ShareCondition.origin.name)
+}
+
+// IsEqualTo returns true when `conflict` and `other` describe the same conflict.
+func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
+ return conflict.SourceNode.name == other.SourceNode.name &&
+ conflict.ShareCondition.name == other.ShareCondition.name &&
+ conflict.ShareCondition.origin.name == other.ShareCondition.origin.name &&
+ conflict.PrivacyCondition.name == other.PrivacyCondition.name &&
+ conflict.PrivacyCondition.origin.name == other.PrivacyCondition.origin.name
+}
+
+// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
+// share the source and to keep the source private apply to the target.
+func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
+ // shareSource is the set of all source-sharing resolutions.
+ shareSource := ResolveSourceSharing(lg)
+ if shareSource.IsEmpty() {
+ return []SourceSharePrivacyConflict{}
+ }
+
+ // privateSource is the set of all source privacy resolutions.
+ privateSource := ResolveSourcePrivacy(lg)
+ if privateSource.IsEmpty() {
+ return []SourceSharePrivacyConflict{}
+ }
+
+ // combined is the combination of source-sharing and source privacy.
+ combined := JoinResolutionSets(shareSource, privateSource)
+
+ // size is the size of the result
+ size := 0
+ for _, actsOn := range combined.ActsOn() {
+ rl := combined.ResolutionsByActsOn(actsOn)
+ size += rl.CountConditionsByName(ImpliesShared) * rl.CountConditionsByName(ImpliesPrivate)
+ }
+ if size == 0 {
+ return []SourceSharePrivacyConflict{}
+ }
+ result := make([]SourceSharePrivacyConflict, 0, size)
+ for _, actsOn := range combined.ActsOn() {
+ rl := combined.ResolutionsByActsOn(actsOn)
+ if len(rl) == 0 {
+ continue
+ }
+
+ pconditions := rl.ByName(ImpliesPrivate).AllConditions().AsList()
+ ssconditions := rl.ByName(ImpliesShared).AllConditions().AsList()
+
+ // report all conflicting condition combinations
+ for _, p := range pconditions {
+ for _, ss := range ssconditions {
+ result = append(result, SourceSharePrivacyConflict{actsOn, ss, p})
+ }
+ }
+ }
+ return result
+}
diff --git a/tools/compliance/policy/shareprivacyconflicts_test.go b/tools/compliance/policy/shareprivacyconflicts_test.go
new file mode 100644
index 0000000..162c1fe
--- /dev/null
+++ b/tools/compliance/policy/shareprivacyconflicts_test.go
@@ -0,0 +1,129 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "sort"
+ "testing"
+)
+
+// byConflict orders conflicts by target then share then privacy
+type byConflict []SourceSharePrivacyConflict
+
+// Len returns the count of elements in the slice.
+func (l byConflict) Len() int { return len(l) }
+
+// Swap rearranged 2 elements so that each occupies the other's former
+// position.
+func (l byConflict) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than
+// the `j`th element.
+func (l byConflict) Less(i, j int) bool {
+ if l[i].SourceNode.name == l[j].SourceNode.name {
+ if l[i].ShareCondition.origin.name == l[j].ShareCondition.origin.name {
+ if l[i].ShareCondition.name == l[j].ShareCondition.name {
+ if l[i].PrivacyCondition.origin.name == l[j].PrivacyCondition.origin.name {
+ return l[i].PrivacyCondition.name < l[j].PrivacyCondition.name
+ }
+ return l[i].PrivacyCondition.origin.name < l[j].PrivacyCondition.origin.name
+ }
+ return l[i].ShareCondition.name < l[j].ShareCondition.name
+ }
+ return l[i].ShareCondition.origin.name < l[j].ShareCondition.origin.name
+ }
+ return l[i].SourceNode.name < l[j].SourceNode.name
+}
+
+func TestConflictingSharedPrivateSource(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedConflicts []confl
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{},
+ },
+ {
+ name: "notice",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{},
+ },
+ {
+ name: "lgpl",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{},
+ },
+ {
+ name: "proprietaryonrestricted",
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{
+ {"proprietary.meta_lic", "gplLib.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
+ },
+ },
+ {
+ name: "restrictedonproprietary",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{
+ {"proprietary.meta_lic", "gplBin.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedConflicts := toConflictList(lg, tt.expectedConflicts)
+ actualConflicts := ConflictingSharedPrivateSource(lg)
+ sort.Sort(byConflict(expectedConflicts))
+ sort.Sort(byConflict(actualConflicts))
+ if len(expectedConflicts) != len(actualConflicts) {
+ t.Errorf("unexpected number of share/privacy conflicts: got %v with %d conflicts, want %v with %d conflicts",
+ actualConflicts, len(actualConflicts), expectedConflicts, len(expectedConflicts))
+ } else {
+ for i := 0; i < len(actualConflicts); i++ {
+ if !actualConflicts[i].IsEqualTo(expectedConflicts[i]) {
+ t.Errorf("unexpected share/privacy conflict at element %d: got %q, want %q",
+ i, actualConflicts[i].Error(), expectedConflicts[i].Error())
+ }
+ }
+ }
+
+ })
+ }
+}
diff --git a/tools/compliance/policy/shipped.go b/tools/compliance/policy/shipped.go
new file mode 100644
index 0000000..74eb343
--- /dev/null
+++ b/tools/compliance/policy/shipped.go
@@ -0,0 +1,54 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ShippedNodes returns the set of nodes in a license graph where the target or
+// a derivative work gets distributed. (caches result)
+func ShippedNodes(lg *LicenseGraph) *TargetNodeSet {
+ lg.mu.Lock()
+ shipped := lg.shippedNodes
+ lg.mu.Unlock()
+ if shipped != nil {
+ return shipped
+ }
+
+ tset := make(map[*TargetNode]bool)
+
+ WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
+ if _, alreadyWalked := tset[tn]; alreadyWalked {
+ return false
+ }
+ if len(path) > 0 {
+ if !edgeIsDerivation(path[len(path)-1]) {
+ return false
+ }
+ }
+ tset[tn] = true
+ return true
+ })
+
+ shipped = &TargetNodeSet{tset}
+
+ lg.mu.Lock()
+ if lg.shippedNodes == nil {
+ lg.shippedNodes = shipped
+ } else {
+ // if we end up with 2, release the later for garbage collection.
+ shipped = lg.shippedNodes
+ }
+ lg.mu.Unlock()
+
+ return shipped
+}
diff --git a/tools/compliance/policy/shipped_test.go b/tools/compliance/policy/shipped_test.go
new file mode 100644
index 0000000..53a8469
--- /dev/null
+++ b/tools/compliance/policy/shipped_test.go
@@ -0,0 +1,130 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "sort"
+ "testing"
+)
+
+func TestShippedNodes(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedNodes []string
+ }{
+ {
+ name: "singleton",
+ roots: []string{"apacheLib.meta_lic"},
+ edges: []annotated{},
+ expectedNodes: []string{"apacheLib.meta_lic"},
+ },
+ {
+ name: "simplebinary",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedNodes: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
+ },
+ {
+ name: "simpledynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedNodes: []string{"apacheBin.meta_lic"},
+ },
+ {
+ name: "container",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedNodes: []string{
+ "apacheContainer.meta_lic",
+ "apacheLib.meta_lic",
+ "gplLib.meta_lic",
+ },
+ },
+ {
+ name: "binary",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedNodes: []string{
+ "apacheBin.meta_lic",
+ "apacheLib.meta_lic",
+ "gplLib.meta_lic",
+ },
+ },
+ {
+ name: "binarydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedNodes: []string{
+ "apacheBin.meta_lic",
+ "apacheLib.meta_lic",
+ },
+ },
+ {
+ name: "containerdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheLib.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedNodes: []string{
+ "apacheContainer.meta_lic",
+ "apacheBin.meta_lic",
+ "apacheLib.meta_lic",
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedNodes := append([]string{}, tt.expectedNodes...)
+ actualNodes := ShippedNodes(lg).Names()
+ sort.Strings(expectedNodes)
+ sort.Strings(actualNodes)
+ if len(expectedNodes) != len(actualNodes) {
+ t.Errorf("unexpected number of shipped nodes: got %v with %d nodes, want %v with %d nodes",
+ actualNodes, len(actualNodes), expectedNodes, len(expectedNodes))
+ return
+ }
+ for i := 0; i < len(actualNodes); i++ {
+ if expectedNodes[i] != actualNodes[i] {
+ t.Errorf("unexpected node at index %d: got %q in %v, want %q in %v",
+ i, actualNodes[i], actualNodes, expectedNodes[i], expectedNodes)
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/policy/walk.go b/tools/compliance/policy/walk.go
new file mode 100644
index 0000000..8b6737d
--- /dev/null
+++ b/tools/compliance/policy/walk.go
@@ -0,0 +1,76 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// VisitNode is called for each root and for each walked dependency node by
+// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
+// down the dependences of the node
+type VisitNode func(*LicenseGraph, *TargetNode, TargetEdgePath) bool
+
+// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
+// into depenencies when `visit` returns true.
+func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
+ path := NewTargetEdgePath(32)
+
+ // must be indexed for fast lookup
+ lg.indexForward()
+
+ var walk func(f string)
+ walk = func(f string) {
+ visitChildren := visit(lg, lg.targets[f], *path)
+ if !visitChildren {
+ return
+ }
+ for _, edge := range lg.index[f] {
+ path.Push(TargetEdge{lg, edge})
+ walk(edge.dependency)
+ path.Pop()
+ }
+ }
+
+ for _, r := range lg.rootFiles {
+ path.Clear()
+ walk(r)
+ }
+}
+
+// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
+// resolving all distributed works for condition `names`.
+func WalkResolutionsForCondition(lg *LicenseGraph, rs *ResolutionSet, names ConditionNames) *ResolutionSet {
+ shipped := ShippedNodes(lg)
+
+ // rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
+ //
+ // rmap is the resulting ResolutionSet
+ rmap := make(map[*TargetNode]actionSet)
+
+ WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, _ TargetEdgePath) bool {
+ if _, ok := rmap[tn]; ok {
+ return false
+ }
+ if !shipped.Contains(tn) {
+ return false
+ }
+ if as, ok := rs.resolutions[tn]; ok {
+ fas := as.byActsOn(shipped).byName(names)
+ if !fas.isEmpty() {
+ rmap[tn] = fas
+ }
+ }
+ return tn.IsContainer() // descend into containers
+ })
+
+ return &ResolutionSet{rmap}
+}
diff --git a/tools/compliance/policy/walk_test.go b/tools/compliance/policy/walk_test.go
new file mode 100644
index 0000000..07710aa
--- /dev/null
+++ b/tools/compliance/policy/walk_test.go
@@ -0,0 +1,631 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestWalkResolutionsForCondition(t *testing.T) {
+ tests := []struct {
+ name string
+ condition ConditionNames
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "notice",
+ condition: ImpliesNotice,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "fponlgplnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "fponlgpldynamicnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "independentmodulenotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "independentmodulerestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "independentmodulestaticnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulestaticrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulenotice",
+ condition: ImpliesNotice,
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulerestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpnotice",
+ condition: ImpliesNotice,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfprestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicnotice",
+ condition: ImpliesNotice,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfprestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplcontainernotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplContainer.meta_lic"},
+ edges: []annotated{
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplcontainerrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplContainer.meta_lic"},
+ edges: []annotated{
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gploncontainernotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gploncontainerrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonbinnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonbinrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestrictedshipped",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversenotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereverserestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereverserestrictedshipped",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversestaticnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversestaticrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereversenotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestrictedshipped",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrnotice",
+ condition: ImpliesNotice,
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ {"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrproprietary",
+ condition: ImpliesProprietary,
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ {
+ name: "ronpnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ronprestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ronpproprietary",
+ condition: ImpliesProprietary,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ {
+ name: "noticeonb_e_onotice",
+ condition: ImpliesNotice,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ },
+ },
+ {
+ name: "noticeonb_e_orestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "noticeonb_e_ob_e_o",
+ condition: ImpliesByExceptionOnly,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ },
+ },
+ {
+ name: "b_e_oonnoticenotice",
+ condition: ImpliesNotice,
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ {"by_exception.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "b_e_oonnoticerestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "b_e_oonnoticeb_e_o",
+ condition: ImpliesByExceptionOnly,
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ },
+ },
+ {
+ name: "noticeonrecipnotice",
+ condition: ImpliesNotice,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "noticeonreciprecip",
+ condition: ImpliesReciprocal,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "reciponnoticenotice",
+ condition: ImpliesNotice,
+ roots: []string{"mplBin.meta_lic"},
+ edges: []annotated{
+ {"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+ {"mplBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "reciponnoticerecip",
+ condition: ImpliesReciprocal,
+ roots: []string{"mplBin.meta_lic"},
+ edges: []annotated{
+ {"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := WalkResolutionsForCondition(lg, ResolveTopDownConditions(lg), tt.condition)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/readgraph.go b/tools/compliance/readgraph.go
new file mode 100644
index 0000000..0b5ebfe
--- /dev/null
+++ b/tools/compliance/readgraph.go
@@ -0,0 +1,259 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "io"
+ "io/fs"
+ "strings"
+ "sync"
+
+ "android/soong/compliance/license_metadata_proto"
+
+ "google.golang.org/protobuf/encoding/prototext"
+)
+
+var (
+ // ConcurrentReaders is the size of the task pool for limiting resource usage e.g. open files.
+ ConcurrentReaders = 5
+)
+
+// result describes the outcome of reading and parsing a single license metadata file.
+type result struct {
+ // file identifies the path to the license metadata file
+ file string
+
+ // target contains the parsed metadata or nil if an error
+ target *TargetNode
+
+ // edges contains the parsed dependencies
+ edges []*dependencyEdge
+
+ // err is nil unless an error occurs
+ err error
+}
+
+// receiver coordinates the tasks for reading and parsing license metadata files.
+type receiver struct {
+ // lg accumulates the read metadata and becomes the final resulting LicensGraph.
+ lg *LicenseGraph
+
+ // rootFS locates the root of the file system from which to read the files.
+ rootFS fs.FS
+
+ // stderr identifies the error output writer.
+ stderr io.Writer
+
+ // task provides a fixed-size task pool to limit concurrent open files etc.
+ task chan bool
+
+ // results returns one license metadata file result at a time.
+ results chan *result
+
+ // wg detects when done
+ wg sync.WaitGroup
+}
+
+// ReadLicenseGraph reads and parses `files` and their dependencies into a LicenseGraph.
+//
+// `files` become the root files of the graph for top-down walks of the graph.
+func ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseGraph, error) {
+ if len(files) == 0 {
+ return nil, fmt.Errorf("no license metadata to analyze")
+ }
+ if ConcurrentReaders < 1 {
+ return nil, fmt.Errorf("need at least one task in pool")
+ }
+
+ lg := newLicenseGraph()
+ for _, f := range files {
+ if strings.HasSuffix(f, ".meta_lic") {
+ lg.rootFiles = append(lg.rootFiles, f)
+ } else {
+ lg.rootFiles = append(lg.rootFiles, f+".meta_lic")
+ }
+ }
+
+ recv := &receiver{
+ lg: lg,
+ rootFS: rootFS,
+ stderr: stderr,
+ task: make(chan bool, ConcurrentReaders),
+ results: make(chan *result, ConcurrentReaders),
+ wg: sync.WaitGroup{},
+ }
+ for i := 0; i < ConcurrentReaders; i++ {
+ recv.task <- true
+ }
+
+ readFiles := func() {
+ lg.mu.Lock()
+ // identify the metadata files to schedule reading tasks for
+ for _, f := range lg.rootFiles {
+ lg.targets[f] = nil
+ }
+ lg.mu.Unlock()
+
+ // schedule tasks to read the files
+ for _, f := range lg.rootFiles {
+ readFile(recv, f)
+ }
+
+ // schedule a task to wait until finished and close the channel.
+ go func() {
+ recv.wg.Wait()
+ close(recv.task)
+ close(recv.results)
+ }()
+ }
+ go readFiles()
+
+ // tasks to read license metadata files are scheduled; read and process results from channel
+ var err error
+ for recv.results != nil {
+ select {
+ case r, ok := <-recv.results:
+ if ok {
+ // handle errors by nil'ing ls, setting err, and clobbering results channel
+ if r.err != nil {
+ err = r.err
+ fmt.Fprintf(recv.stderr, "%s\n", err.Error())
+ lg = nil
+ recv.results = nil
+ continue
+ }
+
+ // record the parsed metadata (guarded by mutex)
+ recv.lg.mu.Lock()
+ recv.lg.targets[r.file] = r.target
+ if len(r.edges) > 0 {
+ recv.lg.edges = append(recv.lg.edges, r.edges...)
+ }
+ recv.lg.mu.Unlock()
+ } else {
+ // finished -- nil the results channel
+ recv.results = nil
+ }
+ }
+ }
+
+ return lg, err
+
+}
+
+// targetNode contains the license metadata for a node in the license graph.
+type targetNode struct {
+ proto license_metadata_proto.LicenseMetadata
+
+ // name is the path to the metadata file
+ name string
+}
+
+// dependencyEdge describes a single edge in the license graph.
+type dependencyEdge struct {
+ // target identifies the target node being built and/or installed.
+ target string
+
+ // dependency identifies the target node being depended on.
+ //
+ // i.e. `dependency` is necessary to build `target`.
+ dependency string
+
+ // annotations are a set of text attributes attached to the edge.
+ //
+ // Policy prescribes meaning to a limited set of annotations; others
+ // are preserved and ignored.
+ annotations TargetEdgeAnnotations
+}
+
+// addDependencies converts the proto AnnotatedDependencies into `edges`
+func addDependencies(edges *[]*dependencyEdge, target string, dependencies []*license_metadata_proto.AnnotatedDependency) error {
+ for _, ad := range dependencies {
+ dependency := ad.GetFile()
+ if len(dependency) == 0 {
+ return fmt.Errorf("missing dependency name")
+ }
+ annotations := newEdgeAnnotations()
+ for _, a := range ad.Annotations {
+ if len(a) == 0 {
+ continue
+ }
+ annotations.annotations[a] = true
+ }
+ *edges = append(*edges, &dependencyEdge{target, dependency, annotations})
+ }
+ return nil
+}
+
+// readFile is a task to read and parse a single license metadata file, and to schedule
+// additional tasks for reading and parsing dependencies as necessary.
+func readFile(recv *receiver, file string) {
+ recv.wg.Add(1)
+ <-recv.task
+ go func() {
+ f, err := recv.rootFS.Open(file)
+ if err != nil {
+ recv.results <- &result{file, nil, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
+ return
+ }
+
+ // read the file
+ data, err := io.ReadAll(f)
+ if err != nil {
+ recv.results <- &result{file, nil, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
+ return
+ }
+
+ tn := &TargetNode{name: file}
+
+ err = prototext.Unmarshal(data, &tn.proto)
+ if err != nil {
+ recv.results <- &result{file, nil, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
+ return
+ }
+
+ edges := []*dependencyEdge{}
+ err = addDependencies(&edges, file, tn.proto.Deps)
+ if err != nil {
+ recv.results <- &result{file, nil, nil, fmt.Errorf("error license metadata dependency %q: %w", file, err)}
+ return
+ }
+ tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
+
+ // send result for this file and release task before scheduling dependencies,
+ // but do not signal done to WaitGroup until dependencies are scheduled.
+ recv.results <- &result{file, tn, edges, nil}
+ recv.task <- true
+
+ // schedule tasks as necessary to read dependencies
+ for _, e := range edges {
+ // decide, signal and record whether to schedule task in critical section
+ recv.lg.mu.Lock()
+ _, alreadyScheduled := recv.lg.targets[e.dependency]
+ if !alreadyScheduled {
+ recv.lg.targets[e.dependency] = nil
+ }
+ recv.lg.mu.Unlock()
+ // schedule task to read dependency file outside critical section
+ if !alreadyScheduled {
+ readFile(recv, e.dependency)
+ }
+ }
+
+ // signal task done after scheduling dependencies
+ recv.wg.Done()
+ }()
+}
diff --git a/tools/compliance/readgraph_test.go b/tools/compliance/readgraph_test.go
new file mode 100644
index 0000000..6248209
--- /dev/null
+++ b/tools/compliance/readgraph_test.go
@@ -0,0 +1,139 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestReadLicenseGraph(t *testing.T) {
+ tests := []struct {
+ name string
+ fs *testFS
+ roots []string
+ expectedError string
+ expectedEdges []edge
+ expectedTargets []string
+ }{
+ {
+ name: "trivial",
+ fs: &testFS{
+ "app.meta_lic": []byte("package_name: \"Android\"\n"),
+ },
+ roots: []string{"app.meta_lic"},
+ expectedEdges: []edge{},
+ expectedTargets: []string{"app.meta_lic"},
+ },
+ {
+ name: "unterminated",
+ fs: &testFS{
+ "app.meta_lic": []byte("package_name: \"Android\n"),
+ },
+ roots: []string{"app.meta_lic"},
+ expectedError: `invalid character '\n' in string`,
+ },
+ {
+ name: "danglingref",
+ fs: &testFS{
+ "app.meta_lic": []byte(AOSP + "deps: {\n file: \"lib.meta_lic\"\n}\n"),
+ },
+ roots: []string{"app.meta_lic"},
+ expectedError: `unknown file "lib.meta_lic"`,
+ },
+ {
+ name: "singleedge",
+ fs: &testFS{
+ "app.meta_lic": []byte(AOSP + "deps: {\n file: \"lib.meta_lic\"\n}\n"),
+ "lib.meta_lic": []byte(AOSP),
+ },
+ roots: []string{"app.meta_lic"},
+ expectedEdges: []edge{{"app.meta_lic", "lib.meta_lic"}},
+ expectedTargets: []string{"app.meta_lic", "lib.meta_lic"},
+ },
+ {
+ name: "fullgraph",
+ fs: &testFS{
+ "apex.meta_lic": []byte(AOSP + "deps: {\n file: \"app.meta_lic\"\n}\ndeps: {\n file: \"bin.meta_lic\"\n}\n"),
+ "app.meta_lic": []byte(AOSP),
+ "bin.meta_lic": []byte(AOSP + "deps: {\n file: \"lib.meta_lic\"\n}\n"),
+ "lib.meta_lic": []byte(AOSP),
+ },
+ roots: []string{"apex.meta_lic"},
+ expectedEdges: []edge{
+ {"apex.meta_lic", "app.meta_lic"},
+ {"apex.meta_lic", "bin.meta_lic"},
+ {"bin.meta_lic", "lib.meta_lic"},
+ },
+ expectedTargets: []string{"apex.meta_lic", "app.meta_lic", "bin.meta_lic", "lib.meta_lic"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := ReadLicenseGraph(tt.fs, stderr, tt.roots)
+ if err != nil {
+ if len(tt.expectedError) == 0 {
+ t.Errorf("unexpected error: got %w, want no error", err)
+ } else if !strings.Contains(err.Error(), tt.expectedError) {
+ t.Errorf("unexpected error: got %w, want %q", err, tt.expectedError)
+ }
+ return
+ }
+ if 0 < len(tt.expectedError) {
+ t.Errorf("unexpected success: got no error, want %q err", tt.expectedError)
+ return
+ }
+ if lg == nil {
+ t.Errorf("missing license graph: got nil, want license graph")
+ return
+ }
+ actualEdges := make([]edge, 0)
+ for _, e := range lg.Edges() {
+ actualEdges = append(actualEdges, edge{e.Target().Name(), e.Dependency().Name()})
+ }
+ sort.Sort(byEdge(tt.expectedEdges))
+ sort.Sort(byEdge(actualEdges))
+ if len(tt.expectedEdges) != len(actualEdges) {
+ t.Errorf("unexpected number of edges: got %v with %d elements, want %v with %d elements",
+ actualEdges, len(actualEdges), tt.expectedEdges, len(tt.expectedEdges))
+ } else {
+ for i := 0; i < len(actualEdges); i++ {
+ if tt.expectedEdges[i] != actualEdges[i] {
+ t.Errorf("unexpected edge at element %d: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
+ }
+ }
+ }
+ actualTargets := make([]string, 0)
+ for _, t := range lg.Targets() {
+ actualTargets = append(actualTargets, t.Name())
+ }
+ sort.Strings(tt.expectedTargets)
+ sort.Strings(actualTargets)
+ if len(tt.expectedTargets) != len(actualTargets) {
+ t.Errorf("unexpected number of targets: got %v with %d elements, want %v with %d elements",
+ actualTargets, len(actualTargets), tt.expectedTargets, len(tt.expectedTargets))
+ } else {
+ for i := 0; i < len(actualTargets); i++ {
+ if tt.expectedTargets[i] != actualTargets[i] {
+ t.Errorf("unexpected target at element %d: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/resolution.go b/tools/compliance/resolution.go
new file mode 100644
index 0000000..0865ecd
--- /dev/null
+++ b/tools/compliance/resolution.go
@@ -0,0 +1,208 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// Resolution describes an action to resolve one or more license conditions.
+//
+// `AttachesTo` identifies the target node that when distributed triggers the action.
+// `ActsOn` identifies the target node that is the object of the action.
+// `Resolves` identifies one or more license conditions that the action resolves.
+//
+// e.g. Suppose an MIT library is linked to a binary that also links to GPL code.
+//
+// A resolution would attach to the binary to share (act on) the MIT library to
+// resolve the restricted condition originating from the GPL code.
+type Resolution struct {
+ attachesTo, actsOn *TargetNode
+ cs *LicenseConditionSet
+}
+
+// AttachesTo returns the target node the resolution attaches to.
+func (r Resolution) AttachesTo() *TargetNode {
+ return r.attachesTo
+}
+
+// ActsOn returns the target node that must be acted on to resolve the condition.
+//
+// i.e. The node for which notice must be given or whose source must be shared etc.
+func (r Resolution) ActsOn() *TargetNode {
+ return r.actsOn
+}
+
+// Resolves returns the set of license condition the resolution satisfies.
+func (r Resolution) Resolves() *LicenseConditionSet {
+ return r.cs.Copy()
+}
+
+// asString returns a string representation of the resolution.
+func (r Resolution) asString() string {
+ var sb strings.Builder
+ cl := r.cs.AsList()
+ sort.Sort(cl)
+ fmt.Fprintf(&sb, "%s -> %s -> %s", r.attachesTo.name, r.actsOn.name, cl.String())
+ return sb.String()
+}
+
+// ResolutionList represents a partial order of Resolutions ordered by
+// AttachesTo() and ActsOn() leaving `Resolves()` unordered.
+type ResolutionList []Resolution
+
+// Len returns the count of elements in the list.
+func (l ResolutionList) Len() int { return len(l) }
+
+// Swap rearranges 2 elements so that each occupies the other's former position.
+func (l ResolutionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than tht `j`th.
+func (l ResolutionList) Less(i, j int) bool {
+ if l[i].attachesTo.name == l[j].attachesTo.name {
+ return l[i].actsOn.name < l[j].actsOn.name
+ }
+ return l[i].attachesTo.name < l[j].attachesTo.name
+}
+
+// String returns a string representation of the list.
+func (rl ResolutionList) String() string {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "[")
+ sep := ""
+ for _, r := range rl {
+ fmt.Fprintf(&sb, "%s%s", sep, r.asString())
+ sep = ", "
+ }
+ fmt.Fprintf(&sb, "]")
+ return sb.String()
+}
+
+// AllConditions returns the union of all license conditions resolved by any
+// element of the list.
+func (rl ResolutionList) AllConditions() *LicenseConditionSet {
+ result := newLicenseConditionSet()
+ for _, r := range rl {
+ result.AddSet(r.cs)
+ }
+ return result
+}
+
+// ByName returns the sub-list of resolutions resolving conditions matching
+// `names`.
+func (rl ResolutionList) ByName(names ConditionNames) ResolutionList {
+ result := make(ResolutionList, 0, rl.CountByName(names))
+ for _, r := range rl {
+ if r.Resolves().HasAnyByName(names) {
+ result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByName(names)})
+ }
+ }
+ return result
+}
+
+// CountByName returns the number of resolutions resolving conditions matching
+// `names`.
+func (rl ResolutionList) CountByName(names ConditionNames) int {
+ c := 0
+ for _, r := range rl {
+ if r.Resolves().HasAnyByName(names) {
+ c++
+ }
+ }
+ return c
+}
+
+// CountConditionsByName returns a count of distinct resolution/conditions
+// pairs matching `names`.
+//
+// A single resolution might resolve multiple conditions matching `names`.
+func (rl ResolutionList) CountConditionsByName(names ConditionNames) int {
+ c := 0
+ for _, r := range rl {
+ c += r.Resolves().CountByName(names)
+ }
+ return c
+}
+
+// ByAttachesTo returns the sub-list of resolutions attached to `attachesTo`.
+func (rl ResolutionList) ByAttachesTo(attachesTo *TargetNode) ResolutionList {
+ result := make(ResolutionList, 0, rl.CountByActsOn(attachesTo))
+ for _, r := range rl {
+ if r.attachesTo == attachesTo {
+ result = append(result, r)
+ }
+ }
+ return result
+}
+
+// CountByAttachesTo returns the number of resolutions attached to
+// `attachesTo`.
+func (rl ResolutionList) CountByAttachesTo(attachesTo *TargetNode) int {
+ c := 0
+ for _, r := range rl {
+ if r.attachesTo == attachesTo {
+ c++
+ }
+ }
+ return c
+}
+
+// ByActsOn returns the sub-list of resolutions matching `actsOn`.
+func (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList {
+ result := make(ResolutionList, 0, rl.CountByActsOn(actsOn))
+ for _, r := range rl {
+ if r.actsOn == actsOn {
+ result = append(result, r)
+ }
+ }
+ return result
+}
+
+// CountByActsOn returns the number of resolutions matching `actsOn`.
+func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int {
+ c := 0
+ for _, r := range rl {
+ if r.actsOn == actsOn {
+ c++
+ }
+ }
+ return c
+}
+
+// ByOrigin returns the sub-list of resolutions resolving license conditions
+// originating at `origin`.
+func (rl ResolutionList) ByOrigin(origin *TargetNode) ResolutionList {
+ result := make(ResolutionList, 0, rl.CountByOrigin(origin))
+ for _, r := range rl {
+ if r.Resolves().HasAnyByOrigin(origin) {
+ result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByOrigin(origin)})
+ }
+ }
+ return result
+}
+
+// CountByOrigin returns the number of resolutions resolving license conditions
+// originating at `origin`.
+func (rl ResolutionList) CountByOrigin(origin *TargetNode) int {
+ c := 0
+ for _, r := range rl {
+ if r.Resolves().HasAnyByOrigin(origin) {
+ c++
+ }
+ }
+ return c
+}
diff --git a/tools/compliance/resolutionset.go b/tools/compliance/resolutionset.go
new file mode 100644
index 0000000..ea49db9
--- /dev/null
+++ b/tools/compliance/resolutionset.go
@@ -0,0 +1,304 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "strings"
+)
+
+// JoinResolutionSets returns a new ResolutionSet combining the resolutions from
+// multiple resolution sets. All sets must be derived from the same license
+// graph.
+//
+// e.g. combine "restricted", "reciprocal", and "proprietary" resolutions.
+func JoinResolutionSets(resolutions ...*ResolutionSet) *ResolutionSet {
+ if len(resolutions) < 1 {
+ panic(fmt.Errorf("attempt to join 0 resolution sets"))
+ }
+ rmap := make(map[*TargetNode]actionSet)
+ for _, r := range resolutions {
+ if len(r.resolutions) < 1 {
+ continue
+ }
+ for attachesTo, as := range r.resolutions {
+ if as.isEmpty() {
+ continue
+ }
+ if _, ok := rmap[attachesTo]; !ok {
+ rmap[attachesTo] = as.copy()
+ continue
+ }
+ rmap[attachesTo].addSet(as)
+ }
+ }
+ return &ResolutionSet{rmap}
+}
+
+// ResolutionSet describes an immutable set of targets and the license
+// conditions each target must satisfy or "resolve" in a specific context.
+//
+// Ultimately, the purpose of recording the license metadata and building a
+// license graph is to identify, describe, and verify the necessary actions or
+// operations for compliance policy.
+//
+// i.e. What is the source-sharing policy? Has it been met? Meet it.
+//
+// i.e. Are there incompatible policy requirements? Such as a source-sharing
+// policy applied to code that policy also says may not be shared? If so, stop
+// and remove the dependencies that create the situation.
+//
+// The ResolutionSet is the base unit for mapping license conditions to the
+// targets triggering some necessary action per policy. Different ResolutionSet
+// values may be calculated for different contexts.
+//
+// e.g. Suppose an unencumbered binary links in a notice .a library.
+//
+// An "unencumbered" condition would originate from the binary, and a "notice"
+// condition would originate from the .a library. A ResolutionSet for the
+// context of the Notice policy might apply both conditions to the binary while
+// preserving the origin of each condition. By applying the notice condition to
+// the binary, the ResolutionSet stipulates the policy that the release of the
+// unencumbered binary must provide suitable notice for the .a library.
+//
+// The resulting ResolutionSet could be used for building a notice file, for
+// validating that a suitable notice has been built into the distribution, or
+// for reporting what notices need to be given.
+//
+// Resolutions for different contexts may be combined in a new ResolutionSet
+// using JoinResolutions(...).
+//
+// See: resolve.go for:
+// * ResolveBottomUpConditions(...)
+// * ResolveTopDownForCondition(...)
+// See also: policy.go for:
+// * ResolveSourceSharing(...)
+// * ResolveSourcePrivacy(...)
+type ResolutionSet struct {
+ // resolutions maps names of target with applicable conditions to the set of conditions that apply.
+ resolutions map[*TargetNode]actionSet
+}
+
+// String returns a string representation of the set.
+func (rs *ResolutionSet) String() string {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "{")
+ sep := ""
+ for attachesTo, as := range rs.resolutions {
+ fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.name, as.String())
+ sep = ", "
+ }
+ fmt.Fprintf(&sb, "}")
+ return sb.String()
+}
+
+// AttachesTo identifies the list of targets triggering action to resolve
+// conditions. (unordered)
+func (rs *ResolutionSet) AttachesTo() TargetNodeList {
+ targets := make(TargetNodeList, 0, len(rs.resolutions))
+ for attachesTo := range rs.resolutions {
+ targets = append(targets, attachesTo)
+ }
+ return targets
+}
+
+// ActsOn identifies the list of targets to act on (share, give notice etc.)
+// to resolve conditions. (unordered)
+func (rs *ResolutionSet) ActsOn() TargetNodeList {
+ tset := make(map[*TargetNode]bool)
+ for _, as := range rs.resolutions {
+ for actsOn := range as {
+ tset[actsOn] = true
+ }
+ }
+ targets := make(TargetNodeList, 0, len(tset))
+ for target := range tset {
+ targets = append(targets, target)
+ }
+ return targets
+}
+
+// Origins identifies the list of targets originating conditions to resolve.
+// (unordered)
+func (rs *ResolutionSet) Origins() TargetNodeList {
+ tset := make(map[*TargetNode]bool)
+ for _, as := range rs.resolutions {
+ for _, cs := range as {
+ for _, origins := range cs.conditions {
+ for origin := range origins {
+ tset[origin] = true
+ }
+ }
+ }
+ }
+ targets := make(TargetNodeList, 0, len(tset))
+ for target := range tset {
+ targets = append(targets, target)
+ }
+ return targets
+}
+
+// Resolutions returns the list of resolutions that `attachedTo`
+// target must resolve. Returns empty list if no conditions apply.
+//
+// Panics if `attachedTo` does not appear in the set.
+func (rs *ResolutionSet) Resolutions(attachedTo *TargetNode) ResolutionList {
+ as, ok := rs.resolutions[attachedTo]
+ if !ok {
+ return ResolutionList{}
+ }
+ result := make(ResolutionList, 0, len(as))
+ for actsOn, cs := range as {
+ result = append(result, Resolution{attachedTo, actsOn, cs.Copy()})
+ }
+ return result
+}
+
+// ResolutionsByActsOn returns the list of resolutions that must `actOn` to
+// resolvee. Returns empty list if no conditions apply.
+//
+// Panics if `actOn` does not appear in the set.
+func (rs *ResolutionSet) ResolutionsByActsOn(actOn *TargetNode) ResolutionList {
+ c := 0
+ for _, as := range rs.resolutions {
+ if _, ok := as[actOn]; ok {
+ c++
+ }
+ }
+ result := make(ResolutionList, 0, c)
+ for attachedTo, as := range rs.resolutions {
+ if cs, ok := as[actOn]; ok {
+ result = append(result, Resolution{attachedTo, actOn, cs.Copy()})
+ }
+ }
+ return result
+}
+
+// AttachesToByOrigin identifies the list of targets requiring action to
+// resolve conditions originating at `origin`. (unordered)
+func (rs *ResolutionSet) AttachesToByOrigin(origin *TargetNode) TargetNodeList {
+ tset := make(map[*TargetNode]bool)
+ for attachesTo, as := range rs.resolutions {
+ for _, cs := range as {
+ if cs.HasAnyByOrigin(origin) {
+ tset[attachesTo] = true
+ break
+ }
+ }
+ }
+ targets := make(TargetNodeList, 0, len(tset))
+ for target := range tset {
+ targets = append(targets, target)
+ }
+ return targets
+}
+
+// AttachesToTarget returns true if the set contains conditions that
+// are `attachedTo`.
+func (rs *ResolutionSet) AttachesToTarget(attachedTo *TargetNode) bool {
+ _, isPresent := rs.resolutions[attachedTo]
+ return isPresent
+}
+
+// AnyByNameAttachToTarget returns true if the set contains conditions matching
+// `names` that attach to `attachedTo`.
+func (rs *ResolutionSet) AnyByNameAttachToTarget(attachedTo *TargetNode, names ...ConditionNames) bool {
+ as, isPresent := rs.resolutions[attachedTo]
+ if !isPresent {
+ return false
+ }
+ for _, cs := range as {
+ for _, cn := range names {
+ for _, name := range cn {
+ _, isPresent = cs.conditions[name]
+ if isPresent {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+// AllByNameAttachTo returns true if the set contains at least one condition
+// matching each element of `names` for `attachedTo`.
+func (rs *ResolutionSet) AllByNameAttachToTarget(attachedTo *TargetNode, names ...ConditionNames) bool {
+ as, isPresent := rs.resolutions[attachedTo]
+ if !isPresent {
+ return false
+ }
+ for _, cn := range names {
+ found := false
+ asloop:
+ for _, cs := range as {
+ for _, name := range cn {
+ _, isPresent = cs.conditions[name]
+ if isPresent {
+ found = true
+ break asloop
+ }
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+// IsEmpty returns true if the set contains no conditions to resolve.
+func (rs *ResolutionSet) IsEmpty() bool {
+ for _, as := range rs.resolutions {
+ if !as.isEmpty() {
+ return false
+ }
+ }
+ return true
+}
+
+// compliance-only ResolutionSet methods
+
+// newResolutionSet constructs a new, empty instance of resolutionSetImp for graph `lg`.
+func newResolutionSet() *ResolutionSet {
+ return &ResolutionSet{make(map[*TargetNode]actionSet)}
+}
+
+// addConditions attaches all of the license conditions in `as` to `attachTo` to act on the originating node if not already applied.
+func (rs *ResolutionSet) addConditions(attachTo *TargetNode, as actionSet) {
+ _, ok := rs.resolutions[attachTo]
+ if !ok {
+ rs.resolutions[attachTo] = as.copy()
+ return
+ }
+ rs.resolutions[attachTo].addSet(as)
+}
+
+// add attaches all of the license conditions in `as` to `attachTo` to act on `attachTo` if not already applied.
+func (rs *ResolutionSet) addSelf(attachTo *TargetNode, as actionSet) {
+ for _, cs := range as {
+ if cs.IsEmpty() {
+ return
+ }
+ _, ok := rs.resolutions[attachTo]
+ if !ok {
+ rs.resolutions[attachTo] = make(actionSet)
+ }
+ _, ok = rs.resolutions[attachTo][attachTo]
+ if !ok {
+ rs.resolutions[attachTo][attachTo] = newLicenseConditionSet()
+ }
+ rs.resolutions[attachTo][attachTo].AddSet(cs)
+ }
+}
diff --git a/tools/compliance/resolutionset_test.go b/tools/compliance/resolutionset_test.go
new file mode 100644
index 0000000..e50e823
--- /dev/null
+++ b/tools/compliance/resolutionset_test.go
@@ -0,0 +1,201 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "sort"
+ "testing"
+)
+
+var (
+ // bottomUp describes the bottom-up resolve of a hypothetical graph
+ // the graph has a container image, a couple binaries, and a couple
+ // libraries. bin1 statically links lib1 and dynamically links lib2;
+ // bin2 dynamically links lib1 and statically links lib2.
+ // binc represents a compiler or other toolchain binary used for
+ // building the other binaries.
+ bottomUp = []res{
+ {"image", "image", "image", "notice"},
+ {"image", "image", "bin2", "restricted"},
+ {"image", "bin1", "bin1", "reciprocal"},
+ {"image", "bin2", "bin2", "restricted"},
+ {"image", "lib1", "lib1", "notice"},
+ {"image", "lib2", "lib2", "notice"},
+ {"binc", "binc", "binc", "proprietary"},
+ {"bin1", "bin1", "bin1", "reciprocal"},
+ {"bin1", "lib1", "lib1", "notice"},
+ {"bin2", "bin2", "bin2", "restricted"},
+ {"bin2", "lib2", "lib2", "notice"},
+ {"lib1", "lib1", "lib1", "notice"},
+ {"lib2", "lib2", "lib2", "notice"},
+ }
+
+ // notice describes bottomUp after a top-down notice resolve.
+ notice = []res{
+ {"image", "image", "image", "notice"},
+ {"image", "image", "bin2", "restricted"},
+ {"image", "bin1", "bin1", "reciprocal"},
+ {"image", "bin2", "bin2", "restricted"},
+ {"image", "lib1", "lib1", "notice"},
+ {"image", "lib2", "bin2", "restricted"},
+ {"image", "lib2", "lib2", "notice"},
+ {"bin1", "bin1", "bin1", "reciprocal"},
+ {"bin1", "lib1", "lib1", "notice"},
+ {"bin2", "bin2", "bin2", "restricted"},
+ {"bin2", "lib2", "bin2", "restricted"},
+ {"bin2", "lib2", "lib2", "notice"},
+ {"lib1", "lib1", "lib1", "notice"},
+ {"lib2", "lib2", "lib2", "notice"},
+ }
+
+ // share describes bottomUp after a top-down share resolve.
+ share = []res{
+ {"image", "image", "bin2", "restricted"},
+ {"image", "bin1", "bin1", "reciprocal"},
+ {"image", "bin2", "bin2", "restricted"},
+ {"image", "lib2", "bin2", "restricted"},
+ {"bin1", "bin1", "bin1", "reciprocal"},
+ {"bin2", "bin2", "bin2", "restricted"},
+ {"bin2", "lib2", "bin2", "restricted"},
+ }
+
+ // proprietary describes bottomUp after a top-down proprietary resolve.
+ // Note that the proprietary binc is not reachable through the toolchain
+ // dependency.
+ proprietary = []res{}
+)
+
+func TestResolutionSet_JoinResolutionSets(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rsNotice := toResolutionSet(lg, notice)
+ rsShare := toResolutionSet(lg, share)
+ rsExpected := toResolutionSet(lg, append(notice, share...))
+
+ rsActual := JoinResolutionSets(rsNotice, rsShare)
+ checkSame(rsActual, rsExpected, t)
+}
+
+func TestResolutionSet_JoinResolutionsEmpty(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rsShare := toResolutionSet(lg, share)
+ rsProprietary := toResolutionSet(lg, proprietary)
+ rsExpected := toResolutionSet(lg, append(share, proprietary...))
+
+ rsActual := JoinResolutionSets(rsShare, rsProprietary)
+ checkSame(rsActual, rsExpected, t)
+}
+
+func TestResolutionSet_Origins(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rsShare := toResolutionSet(lg, share)
+
+ origins := make([]string, 0)
+ for _, target := range rsShare.Origins() {
+ origins = append(origins, target.Name())
+ }
+ sort.Strings(origins)
+ if len(origins) != 2 {
+ t.Errorf("unexpected number of origins: got %v with %d elements, want [\"bin1\", \"bin2\"] with 2 elements", origins, len(origins))
+ }
+ if origins[0] != "bin1" {
+ t.Errorf("unexpected origin at element 0: got %s, want \"bin1\"", origins[0])
+ }
+ if origins[1] != "bin2" {
+ t.Errorf("unexpected origin at element 0: got %s, want \"bin2\"", origins[0])
+ }
+}
+
+func TestResolutionSet_AttachedToTarget(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rsShare := toResolutionSet(lg, share)
+
+ if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
+ t.Errorf("unexpected AttachedToTarget(\"binc\"): got true, want false")
+ }
+ if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
+ t.Errorf("unexpected AttachedToTarget(\"image\"): got false want true")
+ }
+}
+
+func TestResolutionSet_AnyByNameAttachToTarget(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rs := toResolutionSet(lg, bottomUp)
+
+ pandp := ConditionNames{"permissive", "proprietary"}
+ pandn := ConditionNames{"permissive", "notice"}
+ p := ConditionNames{"proprietary"}
+ r := ConditionNames{"restricted"}
+
+ if rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), pandp, p) {
+ t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"proprietary\", \"permissive\"): want false, got true")
+ }
+ if !rs.AnyByNameAttachToTarget(newTestNode(lg, "binc"), p) {
+ t.Errorf("unexpected AnyByNameAttachToTarget(\"binc\", \"proprietary\"): want true, got false")
+ }
+ if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), pandn) {
+ t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"permissive\", \"notice\"): want true, got false")
+ }
+ if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), r, pandn) {
+ t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"restricted\", \"notice\"): want true, got false")
+ }
+ if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), r, p) {
+ t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"restricted\", \"proprietary\"): want true, got false")
+ }
+}
+
+func TestResolutionSet_AllByNameAttachToTarget(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rs := toResolutionSet(lg, bottomUp)
+
+ pandp := ConditionNames{"permissive", "proprietary"}
+ pandn := ConditionNames{"permissive", "notice"}
+ p := ConditionNames{"proprietary"}
+ r := ConditionNames{"restricted"}
+
+ if rs.AllByNameAttachToTarget(newTestNode(lg, "image"), pandp, p) {
+ t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"proprietary\", \"permissive\"): want false, got true")
+ }
+ if !rs.AllByNameAttachToTarget(newTestNode(lg, "binc"), p) {
+ t.Errorf("unexpected AllByNameAttachToTarget(\"binc\", \"proprietary\"): want true, got false")
+ }
+ if !rs.AllByNameAttachToTarget(newTestNode(lg, "image"), pandn) {
+ t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"notice\"): want true, got false")
+ }
+ if !rs.AllByNameAttachToTarget(newTestNode(lg, "image"), r, pandn) {
+ t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"restricted\", \"notice\"): want true, got false")
+ }
+ if rs.AllByNameAttachToTarget(newTestNode(lg, "image"), r, p) {
+ t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"restricted\", \"proprietary\"): want false, got true")
+ }
+}
+
+func TestResolutionSet_AttachesToTarget(t *testing.T) {
+ lg := newLicenseGraph()
+
+ rsShare := toResolutionSet(lg, share)
+
+ if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
+ t.Errorf("unexpected hasTarget(\"binc\"): got true, want false")
+ }
+ if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
+ t.Errorf("unexpected AttachesToTarget(\"image\"): got false want true")
+ }
+}
diff --git a/tools/compliance/test_util.go b/tools/compliance/test_util.go
new file mode 100644
index 0000000..a183b90
--- /dev/null
+++ b/tools/compliance/test_util.go
@@ -0,0 +1,408 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+ "io"
+ "io/fs"
+ "sort"
+ "strings"
+ "testing"
+)
+
+const (
+ // AOSP starts a test metadata file for Android Apache-2.0 licensing.
+ AOSP = `` +
+ `package_name: "Android"
+license_kinds: "SPDX-license-identifier-Apache-2.0"
+license_conditions: "notice"
+`
+
+ // GPL starts a test metadata file for GPL 2.0 licensing.
+ GPL = `` +
+`package_name: "Free Software"
+license_kinds: "SPDX-license-identifier-GPL-2.0"
+license_conditions: "restricted"
+`
+
+ // Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing.
+ Classpath = `` +
+`package_name: "Free Software"
+license_kinds: "SPDX-license-identifier-GPL-2.0-with-classpath-exception"
+license_conditions: "restricted"
+`
+
+ // DependentModule starts a test metadata file for a module in the same package as `Classpath`.
+ DependentModule = `` +
+`package_name: "Free Software"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+`
+
+ // LGPL starts a test metadata file for a module with LGPL 2.0 licensing.
+ LGPL = `` +
+`package_name: "Free Library"
+license_kinds: "SPDX-license-identifier-LGPL-2.0"
+license_conditions: "restricted"
+`
+
+ // MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.
+ MPL = `` +
+`package_name: "Reciprocal"
+license_kinds: "SPDX-license-identifier-MPL-2.0"
+license_conditions: "reciprocal"
+`
+
+ // MIT starts a test metadata file for a module with generic notice (MIT) licensing.
+ MIT = `` +
+`package_name: "Android"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+`
+
+ // Proprietary starts a test metadata file for a module with proprietary licensing.
+ Proprietary = `` +
+`package_name: "Android"
+license_kinds: "legacy_proprietary"
+license_conditions: "proprietary"
+`
+
+ // ByException starts a test metadata file for a module with by_exception_only licensing.
+ ByException = `` +
+`package_name: "Special"
+license_kinds: "legacy_by_exception_only"
+license_conditions: "by_exception_only"
+`
+
+)
+
+var (
+ // meta maps test file names to metadata file content without dependencies.
+ meta = map[string]string{
+ "apacheBin.meta_lic": AOSP,
+ "apacheLib.meta_lic": AOSP,
+ "apacheContainer.meta_lic": AOSP + "is_container: true\n",
+ "dependentModule.meta_lic": DependentModule,
+ "gplWithClasspathException.meta_lic": Classpath,
+ "gplBin.meta_lic": GPL,
+ "gplLib.meta_lic": GPL,
+ "gplContainer.meta_lic": GPL + "is_container: true\n",
+ "lgplBin.meta_lic": LGPL,
+ "lgplLib.meta_lic": LGPL,
+ "mitBin.meta_lic": MIT,
+ "mitLib.meta_lic": MIT,
+ "mplBin.meta_lic": MPL,
+ "mplLib.meta_lic": MPL,
+ "proprietary.meta_lic": Proprietary,
+ "by_exception.meta_lic": ByException,
+ }
+)
+
+// toConditionList converts a test data map of condition name to origin names into a ConditionList.
+func toConditionList(lg *LicenseGraph, conditions map[string][]string) ConditionList {
+ cl := make(ConditionList, 0)
+ for name, origins := range conditions {
+ for _, origin := range origins {
+ cl = append(cl, LicenseCondition{name, newTestNode(lg, origin)})
+ }
+ }
+ return cl
+}
+
+// newTestNode constructs a test node in the license graph.
+func newTestNode(lg *LicenseGraph, targetName string) *TargetNode {
+ if _, ok := lg.targets[targetName]; !ok {
+ lg.targets[targetName] = &TargetNode{name: targetName}
+ }
+ return lg.targets[targetName]
+}
+
+// testFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.
+type testFS map[string][]byte
+
+// Open implements fs.FS.Open() to open a file based on the filename.
+func (fs *testFS) Open(name string) (fs.File, error) {
+ if _, ok := (*fs)[name]; !ok {
+ return nil, fmt.Errorf("unknown file %q", name)
+ }
+ return &testFile{fs, name, 0}, nil
+}
+
+// testFile implements a test file (fs.File) based on testFS above.
+type testFile struct {
+ fs *testFS
+ name string
+ posn int
+}
+
+// Stat not implemented to obviate implementing fs.FileInfo.
+func (f *testFile) Stat() (fs.FileInfo, error) {
+ return nil, fmt.Errorf("unimplemented")
+}
+
+// Read copies bytes from the testFS map.
+func (f *testFile) Read(b []byte) (int, error) {
+ if f.posn < 0 {
+ return 0, fmt.Errorf("file not open: %q", f.name)
+ }
+ if f.posn >= len((*f.fs)[f.name]) {
+ return 0, io.EOF
+ }
+ n := copy(b, (*f.fs)[f.name][f.posn:])
+ f.posn += n
+ return n, nil
+}
+
+// Close marks the testFile as no longer in use.
+func (f *testFile) Close() error {
+ if f.posn < 0 {
+ return fmt.Errorf("file already closed: %q", f.name)
+ }
+ f.posn = -1
+ return nil
+}
+
+// edge describes test data edges to define test graphs.
+type edge struct {
+ target, dep string
+}
+
+// String returns a string representation of the edge.
+func (e edge) String() string {
+ return e.target + " -> " + e.dep
+}
+
+// byEdge orders edges by target then dep name then annotations.
+type byEdge []edge
+
+// Len returns the count of elements in the slice.
+func (l byEdge) Len() int { return len(l) }
+
+// Swap rearranges 2 elements of the slice so that each occupies the other's
+// former position.
+func (l byEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than
+// the `j`th element.
+func (l byEdge) Less(i, j int) bool {
+ if l[i].target == l[j].target {
+ return l[i].dep < l[j].dep
+ }
+ return l[i].target < l[j].target
+}
+
+
+// annotated describes annotated test data edges to define test graphs.
+type annotated struct {
+ target, dep string
+ annotations []string
+}
+
+func (e annotated) String() string {
+ if e.annotations != nil {
+ return e.target + " -> " + e.dep + " [" + strings.Join(e.annotations, ", ") + "]"
+ }
+ return e.target + " -> " + e.dep
+}
+
+func (e annotated) IsEqualTo(other annotated) bool {
+ if e.target != other.target {
+ return false
+ }
+ if e.dep != other.dep {
+ return false
+ }
+ if len(e.annotations) != len(other.annotations) {
+ return false
+ }
+ a1 := append([]string{}, e.annotations...)
+ a2 := append([]string{}, other.annotations...)
+ for i := 0; i < len(a1); i++ {
+ if a1[i] != a2[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// toGraph converts a list of roots and a list of annotated edges into a test license graph.
+func toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) {
+ deps := make(map[string][]annotated)
+ for _, root := range roots {
+ deps[root] = []annotated{}
+ }
+ for _, edge := range edges {
+ if prev, ok := deps[edge.target]; ok {
+ deps[edge.target] = append(prev, edge)
+ } else {
+ deps[edge.target] = []annotated{edge}
+ }
+ if _, ok := deps[edge.dep]; !ok {
+ deps[edge.dep] = []annotated{}
+ }
+ }
+ fs := make(testFS)
+ for file, edges := range deps {
+ body := meta[file]
+ for _, edge := range edges {
+ body += fmt.Sprintf("deps: {\n file: %q\n", edge.dep)
+ for _, ann := range edge.annotations {
+ body += fmt.Sprintf(" annotations: %q\n", ann)
+ }
+ body += "}\n"
+ }
+ fs[file] = []byte(body)
+ }
+
+ return ReadLicenseGraph(&fs, stderr, roots)
+}
+
+
+// byAnnotatedEdge orders edges by target then dep name then annotations.
+type byAnnotatedEdge []annotated
+
+func (l byAnnotatedEdge) Len() int { return len(l) }
+func (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+func (l byAnnotatedEdge) Less(i, j int) bool {
+ if l[i].target == l[j].target {
+ if l[i].dep == l[j].dep {
+ ai := append([]string{}, l[i].annotations...)
+ aj := append([]string{}, l[j].annotations...)
+ sort.Strings(ai)
+ sort.Strings(aj)
+ for k := 0; k < len(ai) && k < len(aj); k++ {
+ if ai[k] == aj[k] {
+ continue
+ }
+ return ai[k] < aj[k]
+ }
+ return len(ai) < len(aj)
+ }
+ return l[i].dep < l[j].dep
+ }
+ return l[i].target < l[j].target
+}
+
+// res describes test data resolutions to define test resolution sets.
+type res struct {
+ attachesTo, actsOn, origin, condition string
+}
+
+// toResolutionSet converts a list of res test data into a test resolution set.
+func toResolutionSet(lg *LicenseGraph, data []res) *ResolutionSet {
+ rmap := make(map[*TargetNode]actionSet)
+ for _, r := range data {
+ attachesTo := newTestNode(lg, r.attachesTo)
+ actsOn := newTestNode(lg, r.actsOn)
+ origin := newTestNode(lg, r.origin)
+ if _, ok := rmap[attachesTo]; !ok {
+ rmap[attachesTo] = make(actionSet)
+ }
+ if _, ok := rmap[attachesTo][actsOn]; !ok {
+ rmap[attachesTo][actsOn] = newLicenseConditionSet()
+ }
+ rmap[attachesTo][actsOn].add(origin, r.condition)
+ }
+ return &ResolutionSet{rmap}
+}
+
+type confl struct {
+ sourceNode, share, privacy string
+}
+
+func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
+ result := make([]SourceSharePrivacyConflict, 0, len(data))
+ for _, c := range data {
+ fields := strings.Split(c.share, ":")
+ oshare := fields[0]
+ cshare := fields[1]
+ fields = strings.Split(c.privacy, ":")
+ oprivacy := fields[0]
+ cprivacy := fields[1]
+ result = append(result, SourceSharePrivacyConflict{
+ newTestNode(lg, c.sourceNode),
+ LicenseCondition{cshare, newTestNode(lg, oshare)},
+ LicenseCondition{cprivacy, newTestNode(lg, oprivacy)},
+ })
+ }
+ return result
+}
+
+// checkSameActions compares an actual action set to an expected action set for a test.
+func checkSameActions(lg *LicenseGraph, asActual, asExpected actionSet, t *testing.T) {
+ rsActual := ResolutionSet{make(map[*TargetNode]actionSet)}
+ rsExpected := ResolutionSet{make(map[*TargetNode]actionSet)}
+ testNode := newTestNode(lg, "test")
+ rsActual.resolutions[testNode] = asActual
+ rsExpected.resolutions[testNode] = asExpected
+ checkSame(&rsActual, &rsExpected, t)
+}
+
+// checkSame compares an actual resolution set to an expected resolution set for a test.
+func checkSame(rsActual, rsExpected *ResolutionSet, t *testing.T) {
+ expectedTargets := rsExpected.AttachesTo()
+ sort.Sort(expectedTargets)
+ for _, target := range expectedTargets {
+ if !rsActual.AttachesToTarget(target) {
+ t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false in %s, want true in %s", target.name, rsActual, rsExpected)
+ continue
+ }
+ expectedRl := rsExpected.Resolutions(target)
+ sort.Sort(expectedRl)
+ actualRl := rsActual.Resolutions(target)
+ sort.Sort(actualRl)
+ if len(expectedRl) != len(actualRl) {
+ t.Errorf("unexpected number of resolutions attach to %q: got %s with %d elements, want %s with %d elements",
+ target.name, actualRl, len(actualRl), expectedRl, len(expectedRl))
+ continue
+ }
+ for i := 0; i < len(expectedRl); i++ {
+ if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
+ t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
+ target.name, i, actualRl[i].asString(), expectedRl[i].asString())
+ continue
+ }
+ expectedConditions := expectedRl[i].Resolves().AsList()
+ actualConditions := actualRl[i].Resolves().AsList()
+ sort.Sort(expectedConditions)
+ sort.Sort(actualConditions)
+ if len(expectedConditions) != len(actualConditions) {
+ t.Errorf("unexpected number of conditions apply to %q acting on %q: got %s with %d elements, want %s with %d elements",
+ target.name, expectedRl[i].actsOn.name,
+ actualConditions, len(actualConditions),
+ expectedConditions, len(expectedConditions))
+ continue
+ }
+ for j := 0; j < len(expectedConditions); j++ {
+ if expectedConditions[j] != actualConditions[j] {
+ t.Errorf("unexpected condition attached to %q acting on %q at index %d: got %s at index %d in %s, want %s in %s",
+ target.name, expectedRl[i].actsOn.name, i,
+ actualConditions[j].asString(":"), j, actualConditions,
+ expectedConditions[j].asString(":"), expectedConditions)
+ }
+ }
+ }
+
+ }
+ actualTargets := rsActual.AttachesTo()
+ sort.Sort(actualTargets)
+ for i, target := range actualTargets {
+ if !rsExpected.AttachesToTarget(target) {
+ t.Errorf("unexpected target: got %q element %d in AttachesTo() %s with %d elements in %s, want %s with %d elements in %s",
+ target.name, i, actualTargets, len(actualTargets), rsActual, expectedTargets, len(expectedTargets), rsExpected)
+ }
+ }
+}
diff --git a/tools/event_log_tags.bzl b/tools/event_log_tags.bzl
new file mode 100644
index 0000000..3766da4
--- /dev/null
+++ b/tools/event_log_tags.bzl
@@ -0,0 +1,47 @@
+"""Event log tags generation rule"""
+
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@rules_android//rules:rules.bzl", "android_library")
+
+def _event_log_tags_impl(ctx):
+ out_files = []
+ for logtag_file in ctx.files.srcs:
+ out_filename = paths.replace_extension(logtag_file.basename, ".java")
+ out_file = ctx.actions.declare_file(out_filename)
+ out_files.append(out_file)
+ ctx.actions.run(
+ inputs = [logtag_file],
+ outputs = [out_file],
+ arguments = [
+ "-o",
+ out_file.path,
+ logtag_file.path,
+ ],
+ progress_message = "Generating Java logtag file from %s" % logtag_file.short_path,
+ executable = ctx.executable._logtag_to_java_tool,
+ )
+ return [DefaultInfo(files = depset(out_files))]
+
+_event_log_tags = rule(
+ implementation = _event_log_tags_impl,
+ attrs = {
+ "srcs": attr.label_list(allow_files = [".logtags"], mandatory = True),
+ "_logtag_to_java_tool": attr.label(
+ executable = True,
+ cfg = "exec",
+ allow_files = True,
+ default = Label("//build/make/tools:java-event-log-tags"),
+ ),
+ },
+)
+
+def event_log_tags(name, srcs):
+ _event_log_tags(
+ name = name + "_gen_logtags",
+ srcs = srcs,
+ )
+
+ android_library(
+ name = name,
+ srcs = [name + "_gen_logtags"],
+ )
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
index 10d25e0..63cb4eb 100644
--- a/tools/fs_config/Android.mk
+++ b/tools/fs_config/Android.mk
@@ -27,7 +27,22 @@
system_android_filesystem_config := system/core/libcutils/include/private/android_filesystem_config.h
system_capability_header := bionic/libc/kernel/uapi/linux/capability.h
-# List of supported vendor, oem, odm, vendor_dlkm, odm_dlkm, product and system_ext Partitions
+# Use snapshots if exist
+vendor_android_filesystem_config := $(strip \
+ $(if $(filter-out current,$(BOARD_VNDK_VERSION)), \
+ $(SOONG_VENDOR_$(BOARD_VNDK_VERSION)_SNAPSHOT_DIR)/include/$(system_android_filesystem_config)))
+ifeq (,$(wildcard $(vendor_android_filesystem_config)))
+vendor_android_filesystem_config := $(system_android_filesystem_config)
+endif
+
+vendor_capability_header := $(strip \
+ $(if $(filter-out current,$(BOARD_VNDK_VERSION)), \
+ $(SOONG_VENDOR_$(BOARD_VNDK_VERSION)_SNAPSHOT_DIR)/include/$(system_capability_header)))
+ifeq (,$(wildcard $(vendor_capability_header)))
+vendor_capability_header := $(system_capability_header)
+endif
+
+# List of supported vendor, oem, odm, vendor_dlkm and odm_dlkm Partitions
fs_config_generate_extra_partition_list := $(strip \
$(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),vendor) \
$(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),oem) \
@@ -206,10 +221,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -232,10 +247,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_files
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -316,10 +331,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -342,10 +357,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_files
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -371,10 +386,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -397,10 +412,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_files
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -426,10 +441,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -452,10 +467,10 @@
LOCAL_INSTALLED_MODULE_STEM := fs_config_files
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
@mkdir -p $(dir $@)
$< fsconfig \
--aid-header $(PRIVATE_ANDROID_FS_HDR) \
diff --git a/tools/generate-notice-files.py b/tools/generate-notice-files.py
index bf958fb..5e3010f 100755
--- a/tools/generate-notice-files.py
+++ b/tools/generate-notice-files.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2012 The Android Open Source Project
#
@@ -30,20 +30,18 @@
import os
import os.path
import re
+import struct
import sys
MD5_BLOCKSIZE = 1024 * 1024
HTML_ESCAPE_TABLE = {
- "&": "&",
- '"': """,
- "'": "'",
- ">": ">",
- "<": "<",
+ b"&": b"&",
+ b'"': b""",
+ b"'": b"'",
+ b">": b">",
+ b"<": b"<",
}
-def hexify(s):
- return ("%02x"*len(s)) % tuple(map(ord, s))
-
def md5sum(filename):
"""Calculate an MD5 of the file given by FILENAME,
and return hex digest as a string.
@@ -57,20 +55,26 @@
break
sum.update(block)
f.close()
- return hexify(sum.digest())
+ return sum.hexdigest()
def html_escape(text):
"""Produce entities within text."""
- return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
+ # Using for i in text doesn't work since i will be an int, not a byte.
+ # There are multiple ways to solve this, but the most performant way
+ # to iterate over a byte array is to use unpack. Using the
+ # for i in range(len(text)) and using that to get a byte using array
+ # slices is twice as slow as this method.
+ return b"".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))
-HTML_OUTPUT_CSS="""
+HTML_OUTPUT_CSS=b"""
<style type="text/css">
body { padding: 0; font-family: sans-serif; }
.same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
.label { font-weight: bold; }
.file-list { margin-left: 1em; color: blue; }
</style>
+
"""
def combine_notice_files_html(file_hash, input_dirs, output_filename):
@@ -90,13 +94,13 @@
# Open the output file, and output the header pieces
output_file = open(output_filename, "wb")
- print >> output_file, "<html><head>"
- print >> output_file, HTML_OUTPUT_CSS
- print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">'
+ output_file.write(b"<html><head>\n")
+ output_file.write(HTML_OUTPUT_CSS)
+ output_file.write(b'</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">\n')
# Output our table of contents
- print >> output_file, '<div class="toc">'
- print >> output_file, "<ul>"
+ output_file.write(b'<div class="toc">\n')
+ output_file.write(b"<ul>\n")
# Flatten the list of lists into a single list of filenames
sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
@@ -104,31 +108,29 @@
# Print out a nice table of contents
for filename in sorted_filenames:
stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
- print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename)
+ output_file.write(('<li><a href="#id%d">%s</a></li>\n' % (id_table.get(filename), stripped_filename)).encode())
- print >> output_file, "</ul>"
- print >> output_file, "</div><!-- table of contents -->"
+ output_file.write(b"</ul>\n")
+ output_file.write(b"</div><!-- table of contents -->\n")
# Output the individual notice file lists
- print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
+ output_file.write(b'<table cellpadding="0" cellspacing="0" border="0">\n')
for value in file_hash:
- print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0])
- print >> output_file, '<div class="label">Notices for file(s):</div>'
- print >> output_file, '<div class="file-list">'
+ output_file.write(b'<tr id="id%d"><td class="same-license">\n' % id_table.get(value[0]))
+ output_file.write(b'<div class="label">Notices for file(s):</div>\n')
+ output_file.write(b'<div class="file-list">\n')
for filename in value:
- print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))
- print >> output_file, "</div><!-- file-list -->"
- print >> output_file
- print >> output_file, '<pre class="license-text">'
- print >> output_file, html_escape(open(value[0]).read())
- print >> output_file, "</pre><!-- license-text -->"
- print >> output_file, "</td></tr><!-- same-license -->"
- print >> output_file
- print >> output_file
- print >> output_file
+ output_file.write(("%s <br/>\n" % SRC_DIR_STRIP_RE.sub(r"\1", filename)).encode())
+ output_file.write(b"</div><!-- file-list -->\n")
+ output_file.write(b"\n")
+ output_file.write(b'<pre class="license-text">\n')
+ with open(value[0], "rb") as notice_file:
+ output_file.write(html_escape(notice_file.read()))
+ output_file.write(b"\n</pre><!-- license-text -->\n")
+ output_file.write(b"</td></tr><!-- same-license -->\n\n\n\n")
# Finish off the file output
- print >> output_file, "</table>"
- print >> output_file, "</body></html>"
+ output_file.write(b"</table>\n")
+ output_file.write(b"</body></html>\n")
output_file.close()
def combine_notice_files_text(file_hash, input_dirs, output_filename, file_title):
@@ -136,14 +138,18 @@
SRC_DIR_STRIP_RE = re.compile("(?:" + "|".join(input_dirs) + ")(/.*).txt")
output_file = open(output_filename, "wb")
- print >> output_file, file_title
+ output_file.write(file_title.encode())
+ output_file.write(b"\n")
for value in file_hash:
- print >> output_file, "============================================================"
- print >> output_file, "Notices for file(s):"
- for filename in value:
- print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename)
- print >> output_file, "------------------------------------------------------------"
- print >> output_file, open(value[0]).read()
+ output_file.write(b"============================================================\n")
+ output_file.write(b"Notices for file(s):\n")
+ for filename in value:
+ output_file.write(SRC_DIR_STRIP_RE.sub(r"\1", filename).encode())
+ output_file.write(b"\n")
+ output_file.write(b"------------------------------------------------------------\n")
+ with open(value[0], "rb") as notice_file:
+ output_file.write(notice_file.read())
+ output_file.write(b"\n")
output_file.close()
def combine_notice_files_xml(files_with_same_hash, input_dirs, output_filename):
@@ -154,15 +160,15 @@
# Set up a filename to row id table (anchors inside tables don't work in
# most browsers, but href's to table row ids do)
id_table = {}
- for file_key in files_with_same_hash.keys():
- for filename in files_with_same_hash[file_key]:
+ for file_key, files in files_with_same_hash.items():
+ for filename in files:
id_table[filename] = file_key
# Open the output file, and output the header pieces
output_file = open(output_filename, "wb")
- print >> output_file, '<?xml version="1.0" encoding="utf-8"?>'
- print >> output_file, "<licenses>"
+ output_file.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
+ output_file.write(b"<licenses>\n")
# Flatten the list of lists into a single list of filenames
sorted_filenames = sorted(id_table.keys())
@@ -170,10 +176,8 @@
# Print out a nice table of contents
for filename in sorted_filenames:
stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
- print >> output_file, '<file-name contentId="%s">%s</file-name>' % (id_table.get(filename), stripped_filename)
-
- print >> output_file
- print >> output_file
+ output_file.write(('<file-name contentId="%s">%s</file-name>\n' % (id_table.get(filename), stripped_filename)).encode())
+ output_file.write(b"\n\n")
processed_file_keys = []
# Output the individual notice file lists
@@ -183,11 +187,13 @@
continue
processed_file_keys.append(file_key)
- print >> output_file, '<file-content contentId="%s"><![CDATA[%s]]></file-content>' % (file_key, html_escape(open(filename).read()))
- print >> output_file
+ output_file.write(('<file-content contentId="%s"><![CDATA[' % file_key).encode())
+ with open(filename, "rb") as notice_file:
+ output_file.write(html_escape(notice_file.read()))
+ output_file.write(b"]]></file-content>\n\n")
# Finish off the file output
- print >> output_file, "</licenses>"
+ output_file.write(b"</licenses>\n")
output_file.close()
def get_args():
@@ -254,7 +260,7 @@
file_md5sum = md5sum(filename)
files_with_same_hash[file_md5sum].append(filename)
- filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(files_with_same_hash.keys())]
+ filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]
combine_notice_files_text(filesets, input_dirs, txt_output_file, file_title)
if html_output_file is not None:
diff --git a/tools/rbcrun/README.md b/tools/rbcrun/README.md
index fb58c89..7f40597 100644
--- a/tools/rbcrun/README.md
+++ b/tools/rbcrun/README.md
@@ -68,6 +68,11 @@
Returns `True` if *file* exists
+#### rblf_find_files(*top*, *file-pattern*, only_files = 0)
+
+Returns all the paths under *top* whose basename matches *pattern* (which is a shell's glob pattern). If *only_files* is
+not zero, only the paths to the regular files are returned. The returned paths are relative to *top*.
+
#### rblf_wildcard(*glob*, *top* = None)
Expands *glob*. If *top* is supplied, expands "*top*/*glob*", then removes
@@ -82,3 +87,7 @@
Runs `sh -c "`*command*`"`, reads its output, converts all newlines into spaces, chops trailing newline returns this
string. This is equivalent to Make's
`shell` builtin function. *This function will be eventually removed*.
+
+#### rblf_log(*arg*,..., sep=' ')
+
+Same as `print` builtin but writes to stderr.
\ No newline at end of file
diff --git a/tools/rbcrun/cmd/rbcrun.go b/tools/rbcrun/cmd/rbcrun.go
index 7848562..4db6a0b 100644
--- a/tools/rbcrun/cmd/rbcrun.go
+++ b/tools/rbcrun/cmd/rbcrun.go
@@ -93,6 +93,6 @@
}
func quit(format string, s ...interface{}) {
- fmt.Fprintln(os.Stderr, format, s)
+ fmt.Fprintf(os.Stderr, format, s...)
os.Exit(2)
}
diff --git a/tools/rbcrun/host.go b/tools/rbcrun/host.go
index 1e43334..4915de9 100644
--- a/tools/rbcrun/host.go
+++ b/tools/rbcrun/host.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "io/fs"
"os"
"os/exec"
"path/filepath"
@@ -118,7 +119,7 @@
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &path); err != nil {
return starlark.None, err
}
- if stat, err := os.Stat(path); err != nil || stat.IsDir() {
+ if _, err := os.Stat(path); err != nil {
return starlark.False, nil
}
return starlark.True, nil
@@ -170,6 +171,46 @@
return makeStringList(files), nil
}
+// find(top, pattern, only_files = 0) returns all the paths under 'top'
+// whose basename matches 'pattern' (which is a shell's glob pattern).
+// If 'only_files' is non-zero, only the paths to the regular files are
+// returned. The returned paths are relative to 'top'.
+func find(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,
+ kwargs []starlark.Tuple) (starlark.Value, error) {
+ var top, pattern string
+ var onlyFiles int
+ if err := starlark.UnpackArgs(b.Name(), args, kwargs,
+ "top", &top, "pattern", &pattern, "only_files?", &onlyFiles); err != nil {
+ return starlark.None, err
+ }
+ top = filepath.Clean(top)
+ pattern = filepath.Clean(pattern)
+ // Go's filepath.Walk is slow, consider using OS's find
+ var res []string
+ err := filepath.WalkDir(top, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ if d != nil && d.IsDir() {
+ return fs.SkipDir
+ } else {
+ return nil
+ }
+ }
+ relPath := strings.TrimPrefix(path, top)
+ if len(relPath) > 0 && relPath[0] == os.PathSeparator {
+ relPath = relPath[1:]
+ }
+ // Do not return top-level dir
+ if len(relPath) == 0 {
+ return nil
+ }
+ if matched, err := filepath.Match(pattern, d.Name()); err == nil && matched && (onlyFiles == 0 || d.Type().IsRegular()) {
+ res = append(res, relPath)
+ }
+ return nil
+ })
+ return makeStringList(res), err
+}
+
// shell(command) runs OS shell with given command and returns back
// 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
@@ -218,6 +259,28 @@
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 {
+ return nil, err
+ }
+ for i, v := range args {
+ if i > 0 {
+ fmt.Fprint(os.Stderr, sep)
+ }
+ if s, ok := starlark.AsString(v); ok {
+ fmt.Fprint(os.Stderr, s)
+ } else if b, ok := v.(starlark.Bytes); ok {
+ fmt.Fprint(os.Stderr, string(b))
+ } else {
+ fmt.Fprintf(os.Stderr, "%s", v)
+ }
+ }
+
+ fmt.Fprintln(os.Stderr)
+ return starlark.None, nil
+}
+
func setup(env []string) {
// Create the symbols that aid makefile conversion. See README.md
builtins = starlark.StringDict{
@@ -226,10 +289,14 @@
"rblf_env": structFromEnv(os.Environ()),
// To convert makefile's $(wildcard foo)
"rblf_file_exists": starlark.NewBuiltin("rblf_file_exists", fileExists),
+ // To convert find-copy-subdir and product-copy-files-by pattern
+ "rblf_find_files": starlark.NewBuiltin("rblf_find_files", find),
// To convert makefile's $(filter ...)/$(filter-out)
"rblf_regex": starlark.NewBuiltin("rblf_regex", regexMatch),
// 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),
}
diff --git a/tools/rbcrun/testdata/file_ops.star b/tools/rbcrun/testdata/file_ops.star
index e1f1ac2..50e39bf 100644
--- a/tools/rbcrun/testdata/file_ops.star
+++ b/tools/rbcrun/testdata/file_ops.star
@@ -4,15 +4,22 @@
def test():
myname = "file_ops.star"
+ assert.true(rblf_file_exists("."), "./ exists ")
assert.true(rblf_file_exists(myname), "the file %s does exist" % myname)
assert.true(not rblf_file_exists("no_such_file"), "the file no_such_file does not exist")
files = rblf_wildcard("*.star")
assert.true(myname in files, "expected %s in %s" % (myname, files))
- # RBCDATADIR is set by the caller to the path where this file resides
files = rblf_wildcard("*.star", rblf_env.TEST_DATA_DIR)
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 + "/../", "*")
+ 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)
+ 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")
+ assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
test()
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 5ee53c8..bf7f9a0 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -50,6 +50,7 @@
],
libs: [
"releasetools_common",
+ "releasetools_fsverity_metadata_generator",
"releasetools_verity_utils",
],
required: [
@@ -100,14 +101,6 @@
python_library_host {
name: "ota_metadata_proto",
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: true,
- },
- },
srcs: [
"ota_metadata.proto",
],
@@ -164,6 +157,7 @@
"releasetools_common",
"releasetools_verity_utils",
"apex_manifest",
+ "care_map_proto_py",
],
required: [
"brillo_update_payload",
@@ -186,29 +180,15 @@
// Host libraries.
//
-python_defaults {
- name: "releasetools_library_defaults",
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: true,
- },
- },
-}
-
python_library_host {
name: "releasetools_add_img_to_target_files",
defaults: [
- "releasetools_library_defaults",
"releasetools_add_img_to_target_files_defaults",
],
}
python_library_host {
name: "releasetools_apex_utils",
- defaults: ["releasetools_library_defaults"],
srcs: [
"apex_utils.py",
],
@@ -222,7 +202,6 @@
python_library_host {
name: "releasetools_build_image",
defaults: [
- "releasetools_library_defaults",
"releasetools_build_image_defaults",
],
}
@@ -230,7 +209,6 @@
python_library_host {
name: "releasetools_build_super_image",
defaults: [
- "releasetools_library_defaults",
"releasetools_build_super_image_defaults",
],
}
@@ -238,14 +216,12 @@
python_library_host {
name: "releasetools_check_target_files_vintf",
defaults: [
- "releasetools_library_defaults",
"releasetools_check_target_files_vintf_defaults",
],
}
python_library_host {
name: "releasetools_common",
- defaults: ["releasetools_library_defaults"],
srcs: [
"blockimgdiff.py",
"common.py",
@@ -273,7 +249,6 @@
python_library_host {
name: "releasetools_img_from_target_files",
defaults: [
- "releasetools_library_defaults",
"releasetools_img_from_target_files_defaults",
],
}
@@ -281,14 +256,22 @@
python_library_host {
name: "releasetools_ota_from_target_files",
defaults: [
- "releasetools_library_defaults",
"releasetools_ota_from_target_files_defaults",
],
}
python_library_host {
+ name: "releasetools_fsverity_metadata_generator",
+ srcs: [
+ "fsverity_metadata_generator.py",
+ ],
+ libs: [
+ "fsverity_digests_proto_python",
+ ],
+}
+
+python_library_host {
name: "releasetools_verity_utils",
- defaults: ["releasetools_library_defaults"],
srcs: [
"verity_utils.py",
],
@@ -307,13 +290,8 @@
python_defaults {
name: "releasetools_binary_defaults",
version: {
- py2: {
- enabled: true,
- embedded_launcher: true,
- },
py3: {
- enabled: false,
- embedded_launcher: false,
+ embedded_launcher: true,
},
},
// TODO (b/140144201) Build imgdiff from releasetools_common
@@ -400,7 +378,7 @@
"releasetools_common",
],
required: [
- "aapt",
+ "aapt2",
],
}
@@ -442,7 +420,6 @@
name: "releasetools_find_shareduid_violation",
defaults: [
"releasetools_find_shareduid_violation_defaults",
- "releasetools_library_defaults",
],
}
@@ -576,6 +553,19 @@
],
}
+python_binary_host {
+ name: "fsverity_metadata_generator",
+ srcs: [
+ "fsverity_metadata_generator.py",
+ ],
+ libs: [
+ "fsverity_digests_proto_python",
+ ],
+ required: [
+ "fsverity",
+ ],
+}
+
//
// Tests.
//
@@ -627,39 +617,9 @@
name: "releasetools_test",
defaults: ["releasetools_test_defaults"],
main: "test_utils.py",
- version: {
- py2: {
- enabled: true,
- // When using embedded launcher, atest will try (but may fail) to load libc++.so from
- // host, because the test executable won't be able to find the needed libs via its
- // runpath.
- embedded_launcher: false,
- },
- py3: {
- enabled: false,
- embedded_launcher: false,
- },
- },
- test_options: {
- unit_test: true,
- },
-}
-
-python_test_host {
- name: "releasetools_py3_test",
- defaults: ["releasetools_test_defaults"],
- main: "test_utils.py",
- test_suites: ["general-tests"],
- version: {
- py2: {
- enabled: false,
- embedded_launcher: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: false,
- },
- },
+ // Don't use embedded_launcher, atest will try (but may fail) to load libc++.so from
+ // host, because the test executable won't be able to find the needed libs via its
+ // runpath.
test_options: {
unit_test: true,
},
diff --git a/tools/releasetools/OWNERS b/tools/releasetools/OWNERS
index d7fc540..9962836 100644
--- a/tools/releasetools/OWNERS
+++ b/tools/releasetools/OWNERS
@@ -1,4 +1,6 @@
elsk@google.com
nhdo@google.com
xunchang@google.com
-zhaojiac@google.com
+
+per-file merge_*.py = danielnorman@google.com
+
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index babfc7d..2a4b56b 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -589,7 +589,7 @@
AssertionError: If it can't find an image.
"""
for partition in ab_partitions:
- img_name = partition.strip() + ".img"
+ img_name = partition + ".img"
# Assert that the image is present under IMAGES/ now.
if output_zip:
@@ -687,8 +687,10 @@
os.path.join(OPTIONS.input_tmp, "IMAGES",
"{}.img".format(partition_name))))
+
def AddApexInfo(output_zip):
- apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system')
+ apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
+ compressed_only=False)
apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
apex_metadata_proto.apex_info.extend(apex_infos)
apex_info_bytes = apex_metadata_proto.SerializeToString()
@@ -710,8 +712,10 @@
# Calculate the vbmeta digest and put the result in to META/
boot_images = OPTIONS.info_dict.get("boot_images")
# Disable the digest calculation if the target_file is used as a container
- # for boot images.
- boot_container = boot_images and len(boot_images.split()) >= 2
+ # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img
+ # etc., instead of just a boot.img and will fail in vbmeta digest calculation.
+ boot_container = boot_images and (
+ len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')
if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
avbtool = OPTIONS.info_dict["avb_avbtool"]
@@ -854,39 +858,23 @@
if output_zip:
recovery_two_step_image.AddToZip(output_zip)
- if has_system:
- banner("system")
- partitions['system'] = AddSystem(
- output_zip, recovery_img=recovery_image, boot_img=boot_image)
+ def add_partition(partition, has_partition, add_func, add_args):
+ if has_partition:
+ banner(partition)
+ partitions[partition] = add_func(output_zip, *add_args)
- if has_vendor:
- banner("vendor")
- partitions['vendor'] = AddVendor(
- output_zip, recovery_img=recovery_image, boot_img=boot_image)
-
- if has_product:
- banner("product")
- partitions['product'] = AddProduct(output_zip)
-
- if has_system_ext:
- banner("system_ext")
- partitions['system_ext'] = AddSystemExt(output_zip)
-
- if has_odm:
- banner("odm")
- partitions['odm'] = AddOdm(output_zip)
-
- if has_vendor_dlkm:
- banner("vendor_dlkm")
- partitions['vendor_dlkm'] = AddVendorDlkm(output_zip)
-
- if has_odm_dlkm:
- banner("odm_dlkm")
- partitions['odm_dlkm'] = AddOdmDlkm(output_zip)
-
- if has_system_other:
- banner("system_other")
- AddSystemOther(output_zip)
+ add_partition_calls = (
+ ("system", has_system, AddSystem, [recovery_image, boot_image]),
+ ("vendor", has_vendor, AddVendor, [recovery_image, boot_image]),
+ ("product", has_product, AddProduct, []),
+ ("system_ext", has_system_ext, AddSystemExt, []),
+ ("odm", has_odm, AddOdm, []),
+ ("vendor_dlkm", has_vendor_dlkm, AddVendorDlkm, []),
+ ("odm_dlkm", has_odm_dlkm, AddOdmDlkm, []),
+ ("system_other", has_system_other, AddSystemOther, []),
+ )
+ for call in add_partition_calls:
+ add_partition(*call)
AddApexInfo(output_zip)
@@ -900,13 +888,10 @@
banner("partition-table")
AddPartitionTable(output_zip)
- if OPTIONS.info_dict.get("has_dtbo") == "true":
- banner("dtbo")
- partitions['dtbo'] = AddDtbo(output_zip)
-
- if OPTIONS.info_dict.get("has_pvmfw") == "true":
- banner("pvmfw")
- partitions['pvmfw'] = AddPvmfw(output_zip)
+ add_partition("dtbo",
+ OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
+ add_partition("pvmfw",
+ OPTIONS.info_dict.get("has_pvmfw") == "true", AddPvmfw, [])
# Custom images.
custom_partitions = OPTIONS.info_dict.get(
@@ -963,7 +948,7 @@
"ab_partitions.txt")
if os.path.exists(ab_partitions_txt):
with open(ab_partitions_txt) as f:
- ab_partitions = f.readlines()
+ ab_partitions = f.read().splitlines()
# For devices using A/B update, make sure we have all the needed images
# ready under IMAGES/ or RADIO/.
@@ -1028,8 +1013,5 @@
try:
common.CloseInheritedPipes()
main(sys.argv[1:])
- except common.ExternalError:
- logger.exception("\n ERROR:\n")
- sys.exit(1)
finally:
common.Cleanup()
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index ef4c69c..ee0feae 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -52,9 +52,9 @@
class ApexApkSigner(object):
- """Class to sign the apk files in a apex payload image and repack the apex"""
+ """Class to sign the apk files and other files in an apex payload image and repack the apex"""
- def __init__(self, apex_path, key_passwords, codename_to_api_level_map):
+ def __init__(self, apex_path, key_passwords, codename_to_api_level_map, avbtool=None, sign_tool=None):
self.apex_path = apex_path
if not key_passwords:
self.key_passwords = dict()
@@ -63,9 +63,11 @@
self.codename_to_api_level_map = codename_to_api_level_map
self.debugfs_path = os.path.join(
OPTIONS.search_path, "bin", "debugfs_static")
+ self.avbtool = avbtool if avbtool else "avbtool"
+ self.sign_tool = sign_tool
def ProcessApexFile(self, apk_keys, payload_key, signing_args=None):
- """Scans and signs the apk files and repack the apex
+ """Scans and signs the payload files and repack the apex
Args:
apk_keys: A dict that holds the signing keys for apk files.
@@ -84,7 +86,7 @@
apk_entries = [name for name in entries_names if name.endswith('.apk')]
# No need to sign and repack, return the original apex path.
- if not apk_entries:
+ if not apk_entries and self.sign_tool is None:
logger.info('No apk file to sign in %s', self.apex_path)
return self.apex_path
@@ -99,15 +101,15 @@
logger.warning('Apk path does not contain the intended directory name:'
' %s', entry)
- payload_dir, has_signed_apk = self.ExtractApexPayloadAndSignApks(
- apk_entries, apk_keys)
- if not has_signed_apk:
- logger.info('No apk file has been signed in %s', self.apex_path)
+ payload_dir, has_signed_content = self.ExtractApexPayloadAndSignContents(
+ apk_entries, apk_keys, payload_key)
+ if not has_signed_content:
+ logger.info('No contents has been signed in %s', self.apex_path)
return self.apex_path
return self.RepackApexPayload(payload_dir, payload_key, signing_args)
- def ExtractApexPayloadAndSignApks(self, apk_entries, apk_keys):
+ def ExtractApexPayloadAndSignContents(self, apk_entries, apk_keys, payload_key):
"""Extracts the payload image and signs the containing apk files."""
if not os.path.exists(self.debugfs_path):
raise ApexSigningError(
@@ -119,7 +121,7 @@
self.debugfs_path, 'extract', self.apex_path, payload_dir]
common.RunAndCheckOutput(extract_cmd)
- has_signed_apk = False
+ has_signed_content = False
for entry in apk_entries:
apk_path = os.path.join(payload_dir, entry)
assert os.path.exists(self.apex_path)
@@ -137,8 +139,15 @@
common.SignFile(
unsigned_apk, apk_path, key_name, self.key_passwords.get(key_name),
codename_to_api_level_map=self.codename_to_api_level_map)
- has_signed_apk = True
- return payload_dir, has_signed_apk
+ has_signed_content = True
+
+ if self.sign_tool:
+ logger.info('Signing payload contents in apex %s with %s', self.apex_path, self.sign_tool)
+ cmd = [self.sign_tool, '--avbtool', self.avbtool, payload_key, payload_dir]
+ common.RunAndCheckOutput(cmd)
+ has_signed_content = True
+
+ return payload_dir, has_signed_content
def RepackApexPayload(self, payload_dir, payload_key, signing_args=None):
"""Rebuilds the apex file with the updated payload directory."""
@@ -310,7 +319,7 @@
def SignUncompressedApex(avbtool, apex_file, payload_key, container_key,
container_pw, apk_keys, codename_to_api_level_map,
- no_hashtree, signing_args=None):
+ no_hashtree, signing_args=None, sign_tool=None):
"""Signs the current uncompressed APEX with the given payload/container keys.
Args:
@@ -322,14 +331,16 @@
codename_to_api_level_map: A dict that maps from codename to API level.
no_hashtree: Don't include hashtree in the signed APEX.
signing_args: Additional args to be passed to the payload signer.
+ sign_tool: A tool to sign the contents of the APEX.
Returns:
The path to the signed APEX file.
"""
- # 1. Extract the apex payload image and sign the containing apk files. Repack
+ # 1. Extract the apex payload image and sign the files (e.g. APKs). Repack
# the apex file after signing.
apk_signer = ApexApkSigner(apex_file, container_pw,
- codename_to_api_level_map)
+ codename_to_api_level_map,
+ avbtool, sign_tool)
apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key, signing_args)
# 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
@@ -363,20 +374,16 @@
common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
common.ZipClose(apex_zip)
- # 3. Align the files at page boundary (same as in apexer).
- aligned_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
- common.RunAndCheckOutput(['zipalign', '-f', '4096', apex_file, aligned_apex])
-
- # 4. Sign the APEX container with container_key.
+ # 3. Sign the APEX container with container_key.
signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
# Specify the 4K alignment when calling SignApk.
extra_signapk_args = OPTIONS.extra_signapk_args[:]
- extra_signapk_args.extend(['-a', '4096'])
+ extra_signapk_args.extend(['-a', '4096', '--align-file-size'])
password = container_pw.get(container_key) if container_pw else None
common.SignFile(
- aligned_apex,
+ apex_file,
signed_apex,
container_key,
password,
@@ -388,7 +395,7 @@
def SignCompressedApex(avbtool, apex_file, payload_key, container_key,
container_pw, apk_keys, codename_to_api_level_map,
- no_hashtree, signing_args=None):
+ no_hashtree, signing_args=None, sign_tool=None):
"""Signs the current compressed APEX with the given payload/container keys.
Args:
@@ -425,7 +432,8 @@
apk_keys,
codename_to_api_level_map,
no_hashtree,
- signing_args)
+ signing_args,
+ sign_tool)
# 3. Compress signed original apex.
compressed_apex_file = common.MakeTempFile(prefix='apex-container-',
@@ -436,33 +444,24 @@
'--input', signed_original_apex_file,
'--output', compressed_apex_file])
- # 4. Align apex
- aligned_apex = common.MakeTempFile(prefix='apex-container-', suffix='.capex')
- common.RunAndCheckOutput(['zipalign', '-f', '4096', compressed_apex_file,
- aligned_apex])
-
- # 5. Sign the APEX container with container_key.
+ # 4. Sign the APEX container with container_key.
signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.capex')
- # Specify the 4K alignment when calling SignApk.
- extra_signapk_args = OPTIONS.extra_signapk_args[:]
- extra_signapk_args.extend(['-a', '4096'])
-
password = container_pw.get(container_key) if container_pw else None
common.SignFile(
- aligned_apex,
+ compressed_apex_file,
signed_apex,
container_key,
password,
codename_to_api_level_map=codename_to_api_level_map,
- extra_signapk_args=extra_signapk_args)
+ extra_signapk_args=OPTIONS.extra_signapk_args)
return signed_apex
def SignApex(avbtool, apex_data, payload_key, container_key, container_pw,
apk_keys, codename_to_api_level_map,
- no_hashtree, signing_args=None):
+ no_hashtree, signing_args=None, sign_tool=None):
"""Signs the current APEX with the given payload/container keys.
Args:
@@ -498,7 +497,8 @@
codename_to_api_level_map=codename_to_api_level_map,
no_hashtree=no_hashtree,
apk_keys=apk_keys,
- signing_args=signing_args)
+ signing_args=signing_args,
+ sign_tool=sign_tool)
elif apex_type == 'COMPRESSED':
return SignCompressedApex(
avbtool,
@@ -509,7 +509,8 @@
codename_to_api_level_map=codename_to_api_level_map,
no_hashtree=no_hashtree,
apk_keys=apk_keys,
- signing_args=signing_args)
+ signing_args=signing_args,
+ sign_tool=sign_tool)
else:
# TODO(b/172912232): support signing compressed apex
raise ApexInfoError('Unsupported apex type {}'.format(apex_type))
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index fa4a152..34aa1a6 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -24,6 +24,7 @@
from __future__ import print_function
+import glob
import logging
import os
import os.path
@@ -34,6 +35,9 @@
import common
import verity_utils
+from fsverity_digests_pb2 import FSVerityDigests
+from fsverity_metadata_generator import FSVerityMetadataGenerator
+
logger = logging.getLogger(__name__)
OPTIONS = common.OPTIONS
@@ -231,6 +235,22 @@
mount_point, total_blocks, used_blocks, headroom_blocks,
adjusted_blocks))
+def CalculateSizeAndReserved(prop_dict, size):
+ fs_type = prop_dict.get("fs_type", "")
+ partition_headroom = int(prop_dict.get("partition_headroom", 0))
+ # If not specified, give us 16MB margin for GetDiskUsage error ...
+ reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
+
+ if fs_type == "erofs":
+ reserved_size = int(prop_dict.get("partition_reserved_size", 0))
+ if reserved_size == 0:
+ # give .3% margin or a minimum size for AVB footer
+ return max(size * 1003 // 1000, 256 * 1024)
+
+ if fs_type.startswith("ext4") and partition_headroom > reserved_size:
+ reserved_size = partition_headroom
+
+ return size + reserved_size
def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):
"""Builds a pure image for the files under in_dir and writes it to out_file.
@@ -256,9 +276,11 @@
needs_casefold = prop_dict.get("needs_casefold", 0)
needs_compress = prop_dict.get("needs_compress", 0)
+ disable_sparse = "disable_sparse" in prop_dict
+
if fs_type.startswith("ext"):
build_command = [prop_dict["ext_mkuserimg"]]
- if "extfs_sparse_flag" in prop_dict:
+ if "extfs_sparse_flag" in prop_dict and not disable_sparse:
build_command.append(prop_dict["extfs_sparse_flag"])
run_e2fsck = True
build_command.extend([in_dir, out_file, fs_type,
@@ -287,7 +309,7 @@
if "flash_logical_block_size" in prop_dict:
build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
# Specify UUID and hash_seed if using mke2fs.
- if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":
+ if os.path.basename(prop_dict["ext_mkuserimg"]) == "mkuserimg_mke2fs":
if "uuid" in prop_dict:
build_command.extend(["-U", prop_dict["uuid"]])
if "hash_seed" in prop_dict:
@@ -303,7 +325,7 @@
elif fs_type.startswith("erofs"):
build_command = ["mkerofsimage.sh"]
build_command.extend([in_dir, out_file])
- if "erofs_sparse_flag" in prop_dict:
+ if "erofs_sparse_flag" in prop_dict and not disable_sparse:
build_command.extend([prop_dict["erofs_sparse_flag"]])
build_command.extend(["-m", prop_dict["mount_point"]])
if target_out:
@@ -312,14 +334,27 @@
build_command.extend(["-C", fs_config])
if "selinux_fc" in prop_dict:
build_command.extend(["-c", prop_dict["selinux_fc"]])
+ compressor = None
+ if "erofs_default_compressor" in prop_dict:
+ compressor = prop_dict["erofs_default_compressor"]
+ if "erofs_compressor" in prop_dict:
+ compressor = prop_dict["erofs_compressor"]
+ if compressor:
+ build_command.extend(["-z", compressor])
if "timestamp" in prop_dict:
build_command.extend(["-T", str(prop_dict["timestamp"])])
if "uuid" in prop_dict:
build_command.extend(["-U", prop_dict["uuid"]])
+ if "block_list" in prop_dict:
+ build_command.extend(["-B", prop_dict["block_list"]])
+ if "erofs_pcluster_size" in prop_dict:
+ build_command.extend(["-P", prop_dict["erofs_pcluster_size"]])
+ if "erofs_share_dup_blocks" in prop_dict:
+ build_command.extend(["-k", "4096"])
elif fs_type.startswith("squash"):
build_command = ["mksquashfsimage.sh"]
build_command.extend([in_dir, out_file])
- if "squashfs_sparse_flag" in prop_dict:
+ if "squashfs_sparse_flag" in prop_dict and not disable_sparse:
build_command.extend([prop_dict["squashfs_sparse_flag"]])
build_command.extend(["-m", prop_dict["mount_point"]])
if target_out:
@@ -341,7 +376,7 @@
elif fs_type.startswith("f2fs"):
build_command = ["mkf2fsuserimg.sh"]
build_command.extend([out_file, prop_dict["image_size"]])
- if "f2fs_sparse_flag" in prop_dict:
+ if "f2fs_sparse_flag" in prop_dict and not disable_sparse:
build_command.extend([prop_dict["f2fs_sparse_flag"]])
if fs_config:
build_command.extend(["-C", fs_config])
@@ -353,6 +388,8 @@
build_command.extend(["-t", prop_dict["mount_point"]])
if "timestamp" in prop_dict:
build_command.extend(["-T", str(prop_dict["timestamp"])])
+ if "block_list" in prop_dict:
+ build_command.extend(["-B", prop_dict["block_list"]])
build_command.extend(["-L", prop_dict["mount_point"]])
if (needs_projid):
build_command.append("--prjquota")
@@ -360,8 +397,9 @@
build_command.append("--casefold")
if (needs_compress or prop_dict.get("f2fs_compress") == "true"):
build_command.append("--compression")
- if (prop_dict.get("f2fs_compress") == "true"):
+ if (prop_dict.get("mount_point") != "data"):
build_command.append("--readonly")
+ if (prop_dict.get("f2fs_compress") == "true"):
build_command.append("--sldc")
if (prop_dict.get("f2fs_sldc_flags") == None):
build_command.append(str(0))
@@ -413,6 +451,68 @@
return mkfs_output
+def GenerateFSVerityMetadata(in_dir, fsverity_path, apk_key_path, apk_manifest_path, apk_out_path):
+ """Generates fsverity metadata files.
+
+ By setting PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA := true, fsverity
+ metadata files will be generated. For the input files, see `patterns` below.
+
+ One metadata file per one input file will be generated with the suffix
+ .fsv_meta. e.g. system/framework/foo.jar -> system/framework/foo.jar.fsv_meta
+ Also a mapping file containing fsverity digests will be generated to
+ system/etc/security/fsverity/BuildManifest.apk.
+
+ Args:
+ in_dir: temporary working directory (same as BuildImage)
+ fsverity_path: path to host tool fsverity
+ apk_key_path: path to key (e.g. build/make/target/product/security/platform)
+ apk_manifest_path: path to AndroidManifest.xml for APK
+ apk_out_path: path to the output APK
+
+ Returns:
+ None. The files are generated directly under in_dir.
+ """
+
+ patterns = [
+ "system/framework/*.jar",
+ "system/framework/oat/*/*.oat",
+ "system/framework/oat/*/*.vdex",
+ "system/framework/oat/*/*.art",
+ "system/etc/boot-image.prof",
+ "system/etc/dirty-image-objects",
+ ]
+ files = []
+ for pattern in patterns:
+ files += glob.glob(os.path.join(in_dir, pattern))
+ files = sorted(set(files))
+
+ generator = FSVerityMetadataGenerator(fsverity_path)
+ generator.set_hash_alg("sha256")
+
+ digests = FSVerityDigests()
+ for f in files:
+ generator.generate(f)
+ # f is a full path for now; make it relative so it starts with {mount_point}/
+ digest = digests.digests[os.path.relpath(f, in_dir)]
+ digest.digest = generator.digest(f)
+ digest.hash_alg = "sha256"
+
+ temp_dir = common.MakeTempDir()
+
+ os.mkdir(os.path.join(temp_dir, "assets"))
+ metadata_path = os.path.join(temp_dir, "assets", "build_manifest")
+ with open(metadata_path, "wb") as f:
+ f.write(digests.SerializeToString())
+
+ apk_path = os.path.join(in_dir, apk_out_path)
+
+ common.RunAndCheckOutput(["aapt2", "link",
+ "-A", os.path.join(temp_dir, "assets"),
+ "-o", apk_path,
+ "--manifest", apk_manifest_path])
+ common.RunAndCheckOutput(["apksigner", "sign", "--in", apk_path,
+ "--cert", apk_key_path + ".x509.pem",
+ "--key", apk_key_path + ".pk8"])
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
"""Builds an image for the files under in_dir and writes it to out_file.
@@ -441,31 +541,36 @@
elif fs_type.startswith("f2fs") and prop_dict.get("f2fs_compress") == "true":
fs_spans_partition = False
+ if "fsverity_generate_metadata" in prop_dict:
+ GenerateFSVerityMetadata(in_dir,
+ fsverity_path=prop_dict["fsverity"],
+ apk_key_path=prop_dict["fsverity_apk_key"],
+ apk_manifest_path=prop_dict["fsverity_apk_manifest"],
+ apk_out_path=prop_dict["fsverity_apk_out"])
+
# Get a builder for creating an image that's to be verified by Verified Boot,
# or None if not applicable.
verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
+ disable_sparse = "disable_sparse" in prop_dict
+ mkfs_output = None
if (prop_dict.get("use_dynamic_partition_size") == "true" and
"partition_size" not in prop_dict):
# If partition_size is not defined, use output of `du' + reserved_size.
# For compressed file system, it's better to use the compressed size to avoid wasting space.
if fs_type.startswith("erofs"):
- tmp_dict = prop_dict.copy()
- if "erofs_sparse_flag" in tmp_dict:
- tmp_dict.pop("erofs_sparse_flag")
- BuildImageMkfs(in_dir, tmp_dict, out_file, target_out, fs_config)
- size = GetDiskUsage(out_file)
- os.remove(out_file)
+ mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
+ if "erofs_sparse_flag" in prop_dict and not disable_sparse:
+ image_path = UnsparseImage(out_file, replace=False)
+ size = GetDiskUsage(image_path)
+ os.remove(image_path)
+ else:
+ size = GetDiskUsage(out_file)
else:
size = GetDiskUsage(in_dir)
logger.info(
"The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
- # If not specified, give us 16MB margin for GetDiskUsage error ...
- reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
- partition_headroom = int(prop_dict.get("partition_headroom", 0))
- if fs_type.startswith("ext4") and partition_headroom > reserved_size:
- reserved_size = partition_headroom
- size += reserved_size
+ size = CalculateSizeAndReserved(prop_dict, size)
# Round this up to a multiple of 4K so that avbtool works
size = common.RoundUpTo4K(size)
if fs_type.startswith("ext"):
@@ -478,7 +583,7 @@
size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
sparse_image = False
- if "extfs_sparse_flag" in prop_dict:
+ if "extfs_sparse_flag" in prop_dict and not disable_sparse:
sparse_image = True
fs_dict = GetFilesystemCharacteristics(fs_type, out_file, sparse_image)
os.remove(out_file)
@@ -522,7 +627,7 @@
prop_dict["image_size"] = str(size)
BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
sparse_image = False
- if "f2fs_sparse_flag" in prop_dict:
+ if "f2fs_sparse_flag" in prop_dict and not disable_sparse:
sparse_image = True
fs_dict = GetFilesystemCharacteristics(fs_type, out_file, sparse_image)
os.remove(out_file)
@@ -543,7 +648,12 @@
max_image_size = verity_image_builder.CalculateMaxImageSize()
prop_dict["image_size"] = str(max_image_size)
- mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
+ if not mkfs_output:
+ mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
+
+ # Update the image (eg filesystem size). This can be different eg if mkfs
+ # rounds the requested size down due to alignment.
+ prop_dict["image_size"] = common.sparse_img.GetImagePartitionSize(out_file)
# Check if there's enough headroom space available for ext4 image.
if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
@@ -556,7 +666,6 @@
if verity_image_builder:
verity_image_builder.Build(out_file)
-
def ImagePropFromGlobalDict(glob_dict, mount_point):
"""Build an image property dictionary from the global dictionary.
@@ -587,6 +696,9 @@
common_props = (
"extfs_sparse_flag",
+ "erofs_default_compressor",
+ "erofs_pcluster_size",
+ "erofs_share_dup_blocks",
"erofs_sparse_flag",
"squashfs_sparse_flag",
"system_f2fs_compress",
@@ -606,238 +718,101 @@
for p in common_props:
copy_prop(p, p)
+ ro_mount_points = set([
+ "odm",
+ "odm_dlkm",
+ "oem",
+ "product",
+ "system",
+ "system_ext",
+ "system_other",
+ "vendor",
+ "vendor_dlkm",
+ ])
+
+ # Tuple layout: (readonly, specific prop, general prop)
+ fmt_props = (
+ # Generic first, then specific file type.
+ (False, "fs_type", "fs_type"),
+ (False, "{}_fs_type", "fs_type"),
+
+ # Ordering for these doesn't matter.
+ (False, "{}_selinux_fc", "selinux_fc"),
+ (False, "{}_size", "partition_size"),
+ (True, "avb_{}_add_hashtree_footer_args", "avb_add_hashtree_footer_args"),
+ (True, "avb_{}_algorithm", "avb_algorithm"),
+ (True, "avb_{}_hashtree_enable", "avb_hashtree_enable"),
+ (True, "avb_{}_key_path", "avb_key_path"),
+ (True, "avb_{}_salt", "avb_salt"),
+ (True, "ext4_share_dup_blocks", "ext4_share_dup_blocks"),
+ (True, "{}_base_fs_file", "base_fs_file"),
+ (True, "{}_disable_sparse", "disable_sparse"),
+ (True, "{}_erofs_compressor", "erofs_compressor"),
+ (True, "{}_erofs_pcluster_size", "erofs_pcluster_size"),
+ (True, "{}_erofs_share_dup_blocks", "erofs_share_dup_blocks"),
+ (True, "{}_extfs_inode_count", "extfs_inode_count"),
+ (True, "{}_f2fs_compress", "f2fs_compress"),
+ (True, "{}_f2fs_sldc_flags", "f2fs_sldc_flags"),
+ (True, "{}_reserved_size", "partition_reserved_size"),
+ (True, "{}_squashfs_block_size", "squashfs_block_size"),
+ (True, "{}_squashfs_compressor", "squashfs_compressor"),
+ (True, "{}_squashfs_compressor_opt", "squashfs_compressor_opt"),
+ (True, "{}_squashfs_disable_4k_align", "squashfs_disable_4k_align"),
+ (True, "{}_verity_block_device", "verity_block_device"),
+ )
+
+ # Translate prefixed properties into generic ones.
+ if mount_point == "data":
+ prefix = "userdata"
+ else:
+ prefix = mount_point
+
+ for readonly, src_prop, dest_prop in fmt_props:
+ if readonly and mount_point not in ro_mount_points:
+ continue
+
+ if src_prop == "fs_type":
+ # This property is legacy and only used on a few partitions. b/202600377
+ allowed_partitions = set(["system", "system_other", "data", "oem"])
+ if mount_point not in allowed_partitions:
+ continue
+
+ if mount_point == "system_other":
+ # Propagate system properties to system_other. They'll get overridden
+ # after as needed.
+ copy_prop(src_prop.format("system"), dest_prop)
+
+ copy_prop(src_prop.format(prefix), dest_prop)
+
+ # Set prefixed properties that need a default value.
+ if mount_point in ro_mount_points:
+ prop = "{}_journal_size".format(prefix)
+ if not copy_prop(prop, "journal_size"):
+ d["journal_size"] = "0"
+
+ prop = "{}_extfs_rsv_pct".format(prefix)
+ if not copy_prop(prop, "extfs_rsv_pct"):
+ d["extfs_rsv_pct"] = "0"
+
+ # Copy partition-specific properties.
d["mount_point"] = mount_point
if mount_point == "system":
- copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_system_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_system_key_path", "avb_key_path")
- copy_prop("avb_system_algorithm", "avb_algorithm")
- copy_prop("avb_system_salt", "avb_salt")
- copy_prop("fs_type", "fs_type")
- # Copy the generic system fs type first, override with specific one if
- # available.
- copy_prop("system_fs_type", "fs_type")
copy_prop("system_headroom", "partition_headroom")
- copy_prop("system_size", "partition_size")
- if not copy_prop("system_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("system_verity_block_device", "verity_block_device")
copy_prop("system_root_image", "system_root_image")
copy_prop("root_dir", "root_dir")
copy_prop("root_fs_config", "root_fs_config")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("system_f2fs_compress", "f2fs_compress")
- copy_prop("system_f2fs_sldc_flags", "f2fs_sldc_flags")
- copy_prop("system_squashfs_compressor", "squashfs_compressor")
- copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("system_squashfs_block_size", "squashfs_block_size")
- copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")
- copy_prop("system_base_fs_file", "base_fs_file")
- copy_prop("system_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("system_reserved_size", "partition_reserved_size")
- copy_prop("system_selinux_fc", "selinux_fc")
- elif mount_point == "system_other":
- # We inherit the selinux policies of /system since we contain some of its
- # files.
- copy_prop("avb_system_other_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_system_other_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_system_other_key_path", "avb_key_path")
- copy_prop("avb_system_other_algorithm", "avb_algorithm")
- copy_prop("avb_system_other_salt", "avb_salt")
- copy_prop("fs_type", "fs_type")
- copy_prop("system_fs_type", "fs_type")
- copy_prop("system_other_size", "partition_size")
- if not copy_prop("system_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("system_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("system_f2fs_compress", "f2fs_compress")
- copy_prop("system_f2fs_sldc_flags", "f2fs_sldc_flags")
- copy_prop("system_squashfs_compressor", "squashfs_compressor")
- copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("system_squashfs_block_size", "squashfs_block_size")
- copy_prop("system_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("system_reserved_size", "partition_reserved_size")
- copy_prop("system_selinux_fc", "selinux_fc")
+ copy_prop("fsverity", "fsverity")
+ copy_prop("fsverity_generate_metadata", "fsverity_generate_metadata")
+ copy_prop("fsverity_apk_key","fsverity_apk_key")
+ copy_prop("fsverity_apk_manifest","fsverity_apk_manifest")
+ copy_prop("fsverity_apk_out","fsverity_apk_out")
elif mount_point == "data":
# Copy the generic fs type first, override with specific one if available.
- copy_prop("fs_type", "fs_type")
- copy_prop("userdata_fs_type", "fs_type")
- copy_prop("userdata_size", "partition_size")
copy_prop("flash_logical_block_size", "flash_logical_block_size")
copy_prop("flash_erase_block_size", "flash_erase_block_size")
- copy_prop("userdata_selinux_fc", "selinux_fc")
copy_prop("needs_casefold", "needs_casefold")
copy_prop("needs_projid", "needs_projid")
copy_prop("needs_compress", "needs_compress")
- elif mount_point == "cache":
- copy_prop("cache_fs_type", "fs_type")
- copy_prop("cache_size", "partition_size")
- copy_prop("cache_selinux_fc", "selinux_fc")
- elif mount_point == "vendor":
- copy_prop("avb_vendor_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_vendor_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_vendor_key_path", "avb_key_path")
- copy_prop("avb_vendor_algorithm", "avb_algorithm")
- copy_prop("avb_vendor_salt", "avb_salt")
- copy_prop("vendor_fs_type", "fs_type")
- copy_prop("vendor_size", "partition_size")
- if not copy_prop("vendor_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("vendor_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("vendor_f2fs_compress", "f2fs_compress")
- copy_prop("vendor_f2fs_sldc_flags", "f2fs_sldc_flags")
- copy_prop("vendor_squashfs_compressor", "squashfs_compressor")
- copy_prop("vendor_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("vendor_squashfs_block_size", "squashfs_block_size")
- copy_prop("vendor_squashfs_disable_4k_align", "squashfs_disable_4k_align")
- copy_prop("vendor_base_fs_file", "base_fs_file")
- copy_prop("vendor_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("vendor_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("vendor_reserved_size", "partition_reserved_size")
- copy_prop("vendor_selinux_fc", "selinux_fc")
- elif mount_point == "product":
- copy_prop("avb_product_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_product_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_product_key_path", "avb_key_path")
- copy_prop("avb_product_algorithm", "avb_algorithm")
- copy_prop("avb_product_salt", "avb_salt")
- copy_prop("product_fs_type", "fs_type")
- copy_prop("product_size", "partition_size")
- if not copy_prop("product_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("product_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("product_f2fs_compress", "f2fs_compress")
- copy_prop("product_f2fs_sldc_flags", "f2fs_sldc_flags")
- copy_prop("product_squashfs_compressor", "squashfs_compressor")
- copy_prop("product_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("product_squashfs_block_size", "squashfs_block_size")
- copy_prop("product_squashfs_disable_4k_align", "squashfs_disable_4k_align")
- copy_prop("product_base_fs_file", "base_fs_file")
- copy_prop("product_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("product_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("product_reserved_size", "partition_reserved_size")
- copy_prop("product_selinux_fc", "selinux_fc")
- elif mount_point == "system_ext":
- copy_prop("avb_system_ext_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_system_ext_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_system_ext_key_path", "avb_key_path")
- copy_prop("avb_system_ext_algorithm", "avb_algorithm")
- copy_prop("avb_system_ext_salt", "avb_salt")
- copy_prop("system_ext_fs_type", "fs_type")
- copy_prop("system_ext_size", "partition_size")
- if not copy_prop("system_ext_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("system_ext_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("system_ext_f2fs_compress", "f2fs_compress")
- copy_prop("system_ext_f2fs_sldc_flags", "f2fs_sldc_flags")
- copy_prop("system_ext_squashfs_compressor", "squashfs_compressor")
- copy_prop("system_ext_squashfs_compressor_opt",
- "squashfs_compressor_opt")
- copy_prop("system_ext_squashfs_block_size", "squashfs_block_size")
- copy_prop("system_ext_squashfs_disable_4k_align",
- "squashfs_disable_4k_align")
- copy_prop("system_ext_base_fs_file", "base_fs_file")
- copy_prop("system_ext_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("system_ext_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("system_ext_reserved_size", "partition_reserved_size")
- copy_prop("system_ext_selinux_fc", "selinux_fc")
- elif mount_point == "odm":
- copy_prop("avb_odm_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_odm_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_odm_key_path", "avb_key_path")
- copy_prop("avb_odm_algorithm", "avb_algorithm")
- copy_prop("avb_odm_salt", "avb_salt")
- copy_prop("odm_fs_type", "fs_type")
- copy_prop("odm_size", "partition_size")
- if not copy_prop("odm_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("odm_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("odm_squashfs_compressor", "squashfs_compressor")
- copy_prop("odm_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("odm_squashfs_block_size", "squashfs_block_size")
- copy_prop("odm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
- copy_prop("odm_base_fs_file", "base_fs_file")
- copy_prop("odm_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("odm_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("odm_reserved_size", "partition_reserved_size")
- copy_prop("odm_selinux_fc", "selinux_fc")
- elif mount_point == "vendor_dlkm":
- copy_prop("avb_vendor_dlkm_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_vendor_dlkm_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_vendor_dlkm_key_path", "avb_key_path")
- copy_prop("avb_vendor_dlkm_algorithm", "avb_algorithm")
- copy_prop("avb_vendor_dlkm_salt", "avb_salt")
- copy_prop("vendor_dlkm_fs_type", "fs_type")
- copy_prop("vendor_dlkm_size", "partition_size")
- copy_prop("vendor_dlkm_f2fs_compress", "f2fs_compress")
- copy_prop("vendor_dlkm_f2fs_sldc_flags", "f2fs_sldc_flags")
- if not copy_prop("vendor_dlkm_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("vendor_dlkm_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("vendor_dlkm_squashfs_compressor", "squashfs_compressor")
- copy_prop("vendor_dlkm_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("vendor_dlkm_squashfs_block_size", "squashfs_block_size")
- copy_prop("vendor_dlkm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
- copy_prop("vendor_dlkm_base_fs_file", "base_fs_file")
- copy_prop("vendor_dlkm_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("vendor_dlkm_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("vendor_dlkm_reserved_size", "partition_reserved_size")
- copy_prop("vendor_dlkm_selinux_fc", "selinux_fc")
- elif mount_point == "odm_dlkm":
- copy_prop("avb_odm_dlkm_hashtree_enable", "avb_hashtree_enable")
- copy_prop("avb_odm_dlkm_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
- copy_prop("avb_odm_dlkm_key_path", "avb_key_path")
- copy_prop("avb_odm_dlkm_algorithm", "avb_algorithm")
- copy_prop("avb_odm_dlkm_salt", "avb_salt")
- copy_prop("odm_dlkm_fs_type", "fs_type")
- copy_prop("odm_dlkm_size", "partition_size")
- if not copy_prop("odm_dlkm_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("odm_dlkm_verity_block_device", "verity_block_device")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- copy_prop("odm_dlkm_squashfs_compressor", "squashfs_compressor")
- copy_prop("odm_dlkm_squashfs_compressor_opt", "squashfs_compressor_opt")
- copy_prop("odm_dlkm_squashfs_block_size", "squashfs_block_size")
- copy_prop("odm_dlkm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
- copy_prop("odm_dlkm_base_fs_file", "base_fs_file")
- copy_prop("odm_dlkm_extfs_inode_count", "extfs_inode_count")
- if not copy_prop("odm_dlkm_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("odm_dlkm_reserved_size", "partition_reserved_size")
- copy_prop("odm_dlkm_selinux_fc", "selinux_fc")
- elif mount_point == "oem":
- copy_prop("fs_type", "fs_type")
- copy_prop("oem_size", "partition_size")
- if not copy_prop("oem_journal_size", "journal_size"):
- d["journal_size"] = "0"
- copy_prop("oem_extfs_inode_count", "extfs_inode_count")
- copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
- if not copy_prop("oem_extfs_rsv_pct", "extfs_rsv_pct"):
- d["extfs_rsv_pct"] = "0"
- copy_prop("oem_selinux_fc", "selinux_fc")
d["partition_name"] = mount_point
return d
diff --git a/tools/releasetools/care_map_pb2.py b/tools/releasetools/care_map_pb2.py
new file mode 100644
index 0000000..06aee25
--- /dev/null
+++ b/tools/releasetools/care_map_pb2.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: bootable/recovery/update_verifier/care_map.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='bootable/recovery/update_verifier/care_map.proto',
+ package='recovery_update_verifier',
+ syntax='proto3',
+ serialized_options=_b('H\003'),
+ serialized_pb=_b('\n0bootable/recovery/update_verifier/care_map.proto\x12\x18recovery_update_verifier\"\x9e\x01\n\x07\x43\x61reMap\x12\x43\n\npartitions\x18\x01 \x03(\x0b\x32/.recovery_update_verifier.CareMap.PartitionInfo\x1aN\n\rPartitionInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06ranges\x18\x02 \x01(\t\x12\n\n\x02id\x18\x03 \x01(\t\x12\x13\n\x0b\x66ingerprint\x18\x04 \x01(\tB\x02H\x03\x62\x06proto3')
+)
+
+
+
+
+_CAREMAP_PARTITIONINFO = _descriptor.Descriptor(
+ name='PartitionInfo',
+ full_name='recovery_update_verifier.CareMap.PartitionInfo',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='name', full_name='recovery_update_verifier.CareMap.PartitionInfo.name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='ranges', full_name='recovery_update_verifier.CareMap.PartitionInfo.ranges', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='id', full_name='recovery_update_verifier.CareMap.PartitionInfo.id', index=2,
+ number=3, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='fingerprint', full_name='recovery_update_verifier.CareMap.PartitionInfo.fingerprint', index=3,
+ number=4, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=159,
+ serialized_end=237,
+)
+
+_CAREMAP = _descriptor.Descriptor(
+ name='CareMap',
+ full_name='recovery_update_verifier.CareMap',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='partitions', full_name='recovery_update_verifier.CareMap.partitions', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[_CAREMAP_PARTITIONINFO, ],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=79,
+ serialized_end=237,
+)
+
+_CAREMAP_PARTITIONINFO.containing_type = _CAREMAP
+_CAREMAP.fields_by_name['partitions'].message_type = _CAREMAP_PARTITIONINFO
+DESCRIPTOR.message_types_by_name['CareMap'] = _CAREMAP
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+CareMap = _reflection.GeneratedProtocolMessageType('CareMap', (_message.Message,), {
+
+ 'PartitionInfo' : _reflection.GeneratedProtocolMessageType('PartitionInfo', (_message.Message,), {
+ 'DESCRIPTOR' : _CAREMAP_PARTITIONINFO,
+ '__module__' : 'bootable.recovery.update_verifier.care_map_pb2'
+ # @@protoc_insertion_point(class_scope:recovery_update_verifier.CareMap.PartitionInfo)
+ })
+ ,
+ 'DESCRIPTOR' : _CAREMAP,
+ '__module__' : 'bootable.recovery.update_verifier.care_map_pb2'
+ # @@protoc_insertion_point(class_scope:recovery_update_verifier.CareMap)
+ })
+_sym_db.RegisterMessage(CareMap)
+_sym_db.RegisterMessage(CareMap.PartitionInfo)
+
+
+DESCRIPTOR._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/tools/releasetools/check_target_files_vintf.py b/tools/releasetools/check_target_files_vintf.py
index a2ddfe7..213ae21 100755
--- a/tools/releasetools/check_target_files_vintf.py
+++ b/tools/releasetools/check_target_files_vintf.py
@@ -132,7 +132,7 @@
'checkvintf',
'--check-compat',
]
- for device_path, real_path in dirmap.items():
+ for device_path, real_path in sorted(dirmap.items()):
common_command += ['--dirmap', '{}:{}'.format(device_path, real_path)]
common_command += kernel_args
common_command += shipping_api_level_args
@@ -165,7 +165,15 @@
def PathToPatterns(path):
if path[-1] == '/':
path += '*'
- for device_path, target_files_rel_paths in DIR_SEARCH_PATHS.items():
+
+ # Loop over all the entries in DIR_SEARCH_PATHS and find one where the key
+ # is a prefix of path. In order to get find the correct prefix, sort the
+ # entries by decreasing length of their keys, so that we check if longer
+ # strings are prefixes before shorter strings. This is so that keys that
+ # are substrings of other keys (like /system vs /system_ext) are checked
+ # later, and we don't mistakenly mark a path that starts with /system_ext
+ # as starting with only /system.
+ for device_path, target_files_rel_paths in sorted(DIR_SEARCH_PATHS.items(), key=lambda i: len(i[0]), reverse=True):
if path.startswith(device_path):
suffix = path[len(device_path):]
return [rel_path + suffix for rel_path in target_files_rel_paths]
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index f678d08..64ac95a 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -68,6 +68,9 @@
self.search_path = os.path.dirname(os.path.dirname(exec_path))
self.signapk_path = "framework/signapk.jar" # Relative to search_path
+ if not os.path.exists(os.path.join(self.search_path, self.signapk_path)):
+ if "ANDROID_HOST_OUT" in os.environ:
+ self.search_path = os.environ["ANDROID_HOST_OUT"]
self.signapk_shared_library_path = "lib64" # Relative to search_path
self.extra_signapk_args = []
self.java_path = "java" # Use the one on the path by default.
@@ -80,11 +83,6 @@
self.boot_signer_args = []
self.verity_signer_path = None
self.verity_signer_args = []
- self.aftl_tool_path = None
- self.aftl_server = None
- self.aftl_key_path = None
- self.aftl_manufacturer_key_path = None
- self.aftl_signer_helper = None
self.verbose = False
self.tempfiles = []
self.device_specific = None
@@ -276,6 +274,9 @@
args = args[:]
args[0] = FindHostToolPath(args[0])
+ if verbose is None:
+ verbose = OPTIONS.verbose
+
# Don't log any if caller explicitly says so.
if verbose:
logger.info(" Running: \"%s\"", " ".join(args))
@@ -451,6 +452,13 @@
return vabc_enabled
@property
+ def is_vabc_xor(self):
+ vendor_prop = self.info_dict.get("vendor.build.prop")
+ vabc_xor_enabled = vendor_prop and \
+ vendor_prop.GetProp("ro.virtual_ab.compression.xor.enabled") == "true"
+ return vabc_xor_enabled
+
+ @property
def vendor_suppressed_vabc(self):
vendor_prop = self.info_dict.get("vendor.build.prop")
vabc_suppressed = vendor_prop and \
@@ -461,6 +469,10 @@
def oem_props(self):
return self._oem_props
+ @property
+ def avb_enabled(self):
+ return self.get("avb_enable") == "true"
+
def __getitem__(self, key):
return self.info_dict[key]
@@ -964,6 +976,8 @@
break
except KeyError:
logger.warning('Failed to read %s', prop_file)
+ if data == '':
+ logger.warning("Failed to read build.prop for partition {}".format(name))
return data
@staticmethod
@@ -1380,46 +1394,6 @@
return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
-def ConstructAftlMakeImageCommands(output_image):
- """Constructs the command to append the aftl image to vbmeta."""
-
- # Ensure the other AFTL parameters are set as well.
- assert OPTIONS.aftl_tool_path is not None, 'No aftl tool provided.'
- assert OPTIONS.aftl_key_path is not None, 'No AFTL key provided.'
- assert OPTIONS.aftl_manufacturer_key_path is not None, \
- 'No AFTL manufacturer key provided.'
-
- vbmeta_image = MakeTempFile()
- os.rename(output_image, vbmeta_image)
- build_info = BuildInfo(OPTIONS.info_dict, use_legacy_id=True)
- version_incremental = build_info.GetBuildProp("ro.build.version.incremental")
- aftltool = OPTIONS.aftl_tool_path
- server_argument_list = [OPTIONS.aftl_server, OPTIONS.aftl_key_path]
- aftl_cmd = [aftltool, "make_icp_from_vbmeta",
- "--vbmeta_image_path", vbmeta_image,
- "--output", output_image,
- "--version_incremental", version_incremental,
- "--transparency_log_servers", ','.join(server_argument_list),
- "--manufacturer_key", OPTIONS.aftl_manufacturer_key_path,
- "--algorithm", "SHA256_RSA4096",
- "--padding", "4096"]
- if OPTIONS.aftl_signer_helper:
- aftl_cmd.extend(shlex.split(OPTIONS.aftl_signer_helper))
- return aftl_cmd
-
-
-def AddAftlInclusionProof(output_image):
- """Appends the aftl inclusion proof to the vbmeta image."""
-
- aftl_cmd = ConstructAftlMakeImageCommands(output_image)
- RunAndCheckOutput(aftl_cmd)
-
- verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
- output_image, '--transparency_log_pub_keys',
- OPTIONS.aftl_key_path]
- RunAndCheckOutput(verify_cmd)
-
-
def AppendGkiSigningArgs(cmd):
"""Append GKI signing arguments for mkbootimg."""
# e.g., --gki_signing_key path/to/signing_key
@@ -1513,10 +1487,6 @@
RunAndCheckOutput(cmd)
- # Generate the AFTL inclusion proof.
- if OPTIONS.aftl_server is not None:
- AddAftlInclusionProof(image_path)
-
def _MakeRamdisk(sourcedir, fs_config_file=None,
ramdisk_format=RamdiskFormat.GZ):
@@ -1960,14 +1930,14 @@
RunAndCheckOutput(cmd)
-def UnzipTemp(filename, pattern=None):
+def UnzipTemp(filename, patterns=None):
"""Unzips the given archive into a temporary directory and returns the name.
Args:
filename: If filename is of the form "foo.zip+bar.zip", unzip foo.zip into
a temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
- pattern: Files to unzip from the archive. If omitted, will unzip the entire
+ patterns: Files to unzip from the archive. If omitted, will unzip the entire
archvie.
Returns:
@@ -1977,11 +1947,11 @@
tmp = MakeTempDir(prefix="targetfiles-")
m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
if m:
- UnzipToDir(m.group(1), tmp, pattern)
- UnzipToDir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"), pattern)
+ UnzipToDir(m.group(1), tmp, patterns)
+ UnzipToDir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"), patterns)
filename = m.group(1)
else:
- UnzipToDir(filename, tmp, pattern)
+ UnzipToDir(filename, tmp, patterns)
return tmp
@@ -2018,6 +1988,8 @@
info_dict = LoadInfoDict(input_zip)
is_sparse = info_dict.get("extfs_sparse_flag")
+ if info_dict.get(which + "_disable_sparse"):
+ is_sparse = False
# When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
# shared blocks (i.e. some blocks will show up in multiple files' block
@@ -2138,9 +2110,11 @@
need_passwords = []
key_passwords = {}
devnull = open("/dev/null", "w+b")
- for k in sorted(keylist):
+
+ # sorted() can't compare strings to None, so convert Nones to strings
+ for k in sorted(keylist, key=lambda x: x if x is not None else ""):
# We don't need a password for things that aren't really keys.
- if k in SPECIAL_CERT_STRINGS:
+ if k in SPECIAL_CERT_STRINGS or k is None:
no_passwords.append(k)
continue
@@ -2474,9 +2448,7 @@
"java_path=", "java_args=", "android_jar_path=", "public_key_suffix=",
"private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
"verity_signer_path=", "verity_signer_args=", "device_specific=",
- "extra=", "logfile=", "aftl_tool_path=", "aftl_server=",
- "aftl_key_path=", "aftl_manufacturer_key_path=",
- "aftl_signer_helper="] + list(extra_long_opts))
+ "extra=", "logfile="] + list(extra_long_opts))
except getopt.GetoptError as err:
Usage(docstring)
print("**", str(err), "**")
@@ -2514,16 +2486,6 @@
OPTIONS.verity_signer_path = a
elif o in ("--verity_signer_args",):
OPTIONS.verity_signer_args = shlex.split(a)
- elif o in ("--aftl_tool_path",):
- OPTIONS.aftl_tool_path = a
- elif o in ("--aftl_server",):
- OPTIONS.aftl_server = a
- elif o in ("--aftl_key_path",):
- OPTIONS.aftl_key_path = a
- elif o in ("--aftl_manufacturer_key_path",):
- OPTIONS.aftl_manufacturer_key_path = a
- elif o in ("--aftl_signer_helper",):
- OPTIONS.aftl_signer_helper = a
elif o in ("-s", "--device_specific"):
OPTIONS.device_specific = a
elif o in ("-x", "--extra"):
@@ -2991,7 +2953,7 @@
th.join()
if p.returncode != 0:
- logger.warning("Failure running %s:\n%s\n", diff_program, "".join(err))
+ logger.warning("Failure running %s:\n%s\n", cmd, "".join(err))
self.patch = None
return None, None, None
diff = ptemp.read()
@@ -3898,12 +3860,14 @@
if not image_size:
return None
+ disable_sparse = OPTIONS.info_dict.get(which + "_disable_sparse")
+
image_blocks = int(image_size) // 4096 - 1
assert image_blocks > 0, "blocks for {} must be positive".format(which)
# For sparse images, we will only check the blocks that are listed in the care
# map, i.e. the ones with meaningful data.
- if "extfs_sparse_flag" in OPTIONS.info_dict:
+ if "extfs_sparse_flag" in OPTIONS.info_dict and not disable_sparse:
simg = sparse_img.SparseImage(imgname)
care_map_ranges = simg.care_map.intersect(
rangelib.RangeSet("0-{}".format(image_blocks)))
@@ -3996,3 +3960,10 @@
OPTIONS.replace_updated_files_list.append(care_map_path)
else:
ZipWrite(output_file, temp_care_map, arcname=care_map_path)
+
+
+def IsSparseImage(filepath):
+ with open(filepath, 'rb') as fp:
+ # Magic for android sparse image format
+ # https://source.android.com/devices/bootloader/images
+ return fp.read(4) == b'\x3A\xFF\x26\xED'
diff --git a/tools/releasetools/fsverity_metadata_generator.py b/tools/releasetools/fsverity_metadata_generator.py
new file mode 100644
index 0000000..a300d2e
--- /dev/null
+++ b/tools/releasetools/fsverity_metadata_generator.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+#
+# Copyright 2021 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+`fsverity_metadata_generator` generates fsverity metadata and signature to a
+container file
+
+This actually is a simple wrapper around the `fsverity` program. A file is
+signed by the program which produces the PKCS#7 signature file, merkle tree file
+, and the fsverity_descriptor file. Then the files are packed into a single
+output file so that the information about the signing stays together.
+
+Currently, the output of this script is used by `fd_server` which is the host-
+side backend of an authfs filesystem. `fd_server` uses this file in case when
+the underlying filesystem (ext4, etc.) on the device doesn't support the
+fsverity feature natively in which case the information is read directly from
+the filesystem using ioctl.
+"""
+
+import argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+from struct import *
+
+class TempDirectory(object):
+ def __enter__(self):
+ self.name = tempfile.mkdtemp()
+ return self.name
+
+ def __exit__(self, *unused):
+ shutil.rmtree(self.name)
+
+class FSVerityMetadataGenerator:
+ def __init__(self, fsverity_path):
+ self._fsverity_path = fsverity_path
+
+ # Default values for some properties
+ self.set_hash_alg("sha256")
+ self.set_signature('none')
+
+ def set_key_format(self, key_format):
+ self._key_format = key_format
+
+ def set_key(self, key):
+ self._key = key
+
+ def set_cert(self, cert):
+ self._cert = cert
+
+ def set_hash_alg(self, hash_alg):
+ self._hash_alg = hash_alg
+
+ def set_signature(self, signature):
+ self._signature = signature
+
+ def _raw_signature(pkcs7_sig_file):
+ """ Extracts raw signature from DER formatted PKCS#7 detached signature file
+
+ Do that by parsing the ASN.1 tree to get the location of the signature
+ in the file and then read the portion.
+ """
+
+ # Note: there seems to be no public python API (even in 3p modules) that
+ # provides direct access to the raw signature at this moment. So, `openssl
+ # asn1parse` commandline tool is used instead.
+ cmd = ['openssl', 'asn1parse']
+ cmd.extend(['-inform', 'DER'])
+ cmd.extend(['-in', pkcs7_sig_file])
+ out = subprocess.check_output(cmd, universal_newlines=True)
+
+ # The signature is the last element in the tree
+ last_line = out.splitlines()[-1]
+ m = re.search('(\d+):.*hl=\s*(\d+)\s*l=\s*(\d+)\s*.*OCTET STRING', last_line)
+ if not m:
+ raise RuntimeError("Failed to parse asn1parse output: " + out)
+ offset = int(m.group(1))
+ header_len = int(m.group(2))
+ size = int(m.group(3))
+ with open(pkcs7_sig_file, 'rb') as f:
+ f.seek(offset + header_len)
+ return f.read(size)
+
+ def digest(self, input_file):
+ cmd = [self._fsverity_path, 'digest', input_file]
+ cmd.extend(['--compact'])
+ cmd.extend(['--hash-alg', self._hash_alg])
+ out = subprocess.check_output(cmd, universal_newlines=True).strip()
+ return bytes(bytearray.fromhex(out))
+
+ def generate(self, input_file, output_file=None):
+ if self._signature != 'none':
+ if not self._key:
+ raise RuntimeError("key must be specified.")
+ if not self._cert:
+ raise RuntimeError("cert must be specified.")
+
+ if not output_file:
+ output_file = input_file + '.fsv_meta'
+
+ with TempDirectory() as temp_dir:
+ self._do_generate(input_file, output_file, temp_dir)
+
+ def _do_generate(self, input_file, output_file, work_dir):
+ # temporary files
+ desc_file = os.path.join(work_dir, 'desc')
+ merkletree_file = os.path.join(work_dir, 'merkletree')
+ sig_file = os.path.join(work_dir, 'signature')
+
+ # run the fsverity util to create the temporary files
+ cmd = [self._fsverity_path]
+ if self._signature == 'none':
+ cmd.append('digest')
+ cmd.append(input_file)
+ else:
+ cmd.append('sign')
+ cmd.append(input_file)
+ cmd.append(sig_file)
+
+ # If key is DER, convert DER private key to PEM
+ if self._key_format == 'der':
+ pem_key = os.path.join(work_dir, 'key.pem')
+ key_cmd = ['openssl', 'pkcs8']
+ key_cmd.extend(['-inform', 'DER'])
+ key_cmd.extend(['-in', self._key])
+ key_cmd.extend(['-nocrypt'])
+ key_cmd.extend(['-out', pem_key])
+ subprocess.check_call(key_cmd)
+ else:
+ pem_key = self._key
+
+ cmd.extend(['--key', pem_key])
+ cmd.extend(['--cert', self._cert])
+ cmd.extend(['--hash-alg', self._hash_alg])
+ cmd.extend(['--block-size', '4096'])
+ cmd.extend(['--out-merkle-tree', merkletree_file])
+ cmd.extend(['--out-descriptor', desc_file])
+ subprocess.check_call(cmd, stdout=open(os.devnull, 'w'))
+
+ with open(output_file, 'wb') as out:
+ # 1. version
+ out.write(pack('<I', 1))
+
+ # 2. fsverity_descriptor
+ with open(desc_file, 'rb') as f:
+ out.write(f.read())
+
+ # 3. signature
+ SIG_TYPE_NONE = 0
+ SIG_TYPE_PKCS7 = 1
+ SIG_TYPE_RAW = 2
+ if self._signature == 'raw':
+ out.write(pack('<I', SIG_TYPE_RAW))
+ sig = self._raw_signature(sig_file)
+ out.write(pack('<I', len(sig)))
+ out.write(sig)
+ elif self._signature == 'pkcs7':
+ with open(sig_file, 'rb') as f:
+ out.write(pack('<I', SIG_TYPE_PKCS7))
+ sig = f.read()
+ out.write(pack('<I', len(sig)))
+ out.write(sig)
+ else:
+ out.write(pack('<I', SIG_TYPE_NONE))
+
+ # 4. merkle tree
+ with open(merkletree_file, 'rb') as f:
+ # merkle tree is placed at the next nearest page boundary to make
+ # mmapping possible
+ out.seek(next_page(out.tell()))
+ out.write(f.read())
+
+def next_page(n):
+ """ Returns the next nearest page boundary from `n` """
+ PAGE_SIZE = 4096
+ return (n + PAGE_SIZE - 1) // PAGE_SIZE * PAGE_SIZE
+
+if __name__ == '__main__':
+ p = argparse.ArgumentParser()
+ p.add_argument(
+ '--output',
+ help='output file. If omitted, print to <INPUT>.fsv_meta',
+ metavar='output',
+ default=None)
+ p.add_argument(
+ 'input',
+ help='input file to be signed')
+ p.add_argument(
+ '--key-format',
+ choices=['pem', 'der'],
+ default='der',
+ help='format of the input key. Default is der')
+ p.add_argument(
+ '--key',
+ help='PKCS#8 private key file')
+ p.add_argument(
+ '--cert',
+ help='x509 certificate file in PEM format')
+ p.add_argument(
+ '--hash-alg',
+ help='hash algorithm to use to build the merkle tree',
+ choices=['sha256', 'sha512'],
+ default='sha256')
+ p.add_argument(
+ '--signature',
+ help='format for signature',
+ choices=['none', 'raw', 'pkcs7'],
+ default='none')
+ p.add_argument(
+ '--fsverity-path',
+ help='path to the fsverity program',
+ required=True)
+ args = p.parse_args(sys.argv[1:])
+
+ generator = FSVerityMetadataGenerator(args.fsverity_path)
+ generator.set_signature(args.signature)
+ if args.signature == 'none':
+ if args.key or args.cert:
+ raise ValueError("When signature is none, key and cert can't be set")
+ else:
+ if not args.key or not args.cert:
+ raise ValueError("To generate signature, key and cert must be set")
+ generator.set_key(args.key)
+ generator.set_cert(args.cert)
+ generator.set_key_format(args.key_format)
+ generator.set_hash_alg(args.hash_alg)
+ generator.generate(args.input, args.output)
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 5e6c42d..46ffdb7 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -78,13 +78,33 @@
If provided, duplicate APK/APEX keys are ignored and the value from the
framework is used.
+ --rebuild-sepolicy
+ If provided, rebuilds odm.img or vendor.img to include merged sepolicy
+ files. If odm is present then odm is preferred.
+
+ --vendor-otatools otatools.zip
+ If provided, use this otatools.zip when recompiling the odm or vendor
+ image to include sepolicy.
+
--keep-tmp
Keep tempoary files for debugging purposes.
+
+ The following only apply when using the VSDK to perform dexopt on vendor apps:
+
+ --framework-dexpreopt-config
+ If provided, the location of framwework's dexpreopt_config.zip.
+
+ --framework-dexpreopt-tools
+ if provided, the location of framework's dexpreopt_tools.zip.
+
+ --vendor-dexpreopt-config
+ If provided, the location of vendor's dexpreopt_config.zip.
"""
from __future__ import print_function
import fnmatch
+import glob
import json
import logging
import os
@@ -129,7 +149,12 @@
OPTIONS.rebuild_recovery = False
# TODO(b/150582573): Remove this option.
OPTIONS.allow_duplicate_apkapex_keys = False
+OPTIONS.vendor_otatools = None
+OPTIONS.rebuild_sepolicy = False
OPTIONS.keep_tmp = False
+OPTIONS.framework_dexpreopt_config = None
+OPTIONS.framework_dexpreopt_tools = None
+OPTIONS.vendor_dexpreopt_config = None
# In an item list (framework or vendor), we may see entries that select whole
# partitions. Such an entry might look like this 'SYSTEM/*' (e.g., for the
@@ -666,7 +691,7 @@
os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
-def compile_split_sepolicy(product_out, partition_map, output_policy):
+def compile_split_sepolicy(product_out, partition_map):
"""Uses secilc to compile a split sepolicy file.
Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
@@ -674,7 +699,6 @@
Args:
product_out: PRODUCT_OUT directory, containing partition directories.
partition_map: A map of partition name -> relative path within product_out.
- output_policy: The name of the output policy created by secilc.
Returns:
A command list that can be executed to create the compiled sepolicy.
@@ -709,7 +733,7 @@
# Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
cmd.extend(['-c', kernel_sepolicy_version])
- cmd.extend(['-o', output_policy])
+ cmd.extend(['-o', os.path.join(product_out, 'META/combined_sepolicy')])
cmd.extend(['-f', '/dev/null'])
required_policy_files = (
@@ -747,7 +771,8 @@
Depends on the <partition>/apex/* APEX files within partitions.
Args:
- output_target_files_dir: Output directory containing merged partition directories.
+ output_target_files_dir: Output directory containing merged partition
+ directories.
partitions: A list of all the partitions in the output directory.
Raises:
@@ -805,21 +830,23 @@
PARTITIONS_WITH_CARE_MAP, partition_image_map)
-def process_special_cases(framework_target_files_temp_dir,
- vendor_target_files_temp_dir,
+def process_special_cases(temp_dir, framework_meta, vendor_meta,
output_target_files_temp_dir,
framework_misc_info_keys, framework_partition_set,
- vendor_partition_set):
+ vendor_partition_set, framework_dexpreopt_tools,
+ framework_dexpreopt_config, vendor_dexpreopt_config):
"""Performs special-case processing for certain target files items.
Certain files in the output target files package require special-case
processing. This function performs all that special-case processing.
Args:
- framework_target_files_temp_dir: The name of a directory containing the
- special items extracted from the framework target files package.
- vendor_target_files_temp_dir: The name of a directory containing the special
- items extracted from the vendor target files package.
+ temp_dir: Location containing an 'output' directory where target files have
+ been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES, etc.
+ framework_meta: The name of a directory containing the special items
+ extracted from the framework target files package.
+ vendor_meta: The name of a directory containing the special items
+ extracted from the vendor target files package.
output_target_files_temp_dir: The name of a directory that will be used to
create the output target files package after all the special cases are
processed.
@@ -830,50 +857,361 @@
partitions. Used to filter apexkeys.txt and apkcerts.txt.
vendor_partition_set: Partitions that are considered vendor partitions. Used
to filter apexkeys.txt and apkcerts.txt.
+
+ The following are only used if dexpreopt is applied:
+
+ framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+ framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+ vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
"""
if 'ab_update' in framework_misc_info_keys:
process_ab_partitions_txt(
- framework_target_files_temp_dir=framework_target_files_temp_dir,
- vendor_target_files_temp_dir=vendor_target_files_temp_dir,
+ framework_target_files_temp_dir=framework_meta,
+ vendor_target_files_temp_dir=vendor_meta,
output_target_files_temp_dir=output_target_files_temp_dir)
copy_file_contexts(
- framework_target_files_dir=framework_target_files_temp_dir,
- vendor_target_files_dir=vendor_target_files_temp_dir,
+ framework_target_files_dir=framework_meta,
+ vendor_target_files_dir=vendor_meta,
output_target_files_dir=output_target_files_temp_dir)
process_misc_info_txt(
- framework_target_files_temp_dir=framework_target_files_temp_dir,
- vendor_target_files_temp_dir=vendor_target_files_temp_dir,
+ framework_target_files_temp_dir=framework_meta,
+ vendor_target_files_temp_dir=vendor_meta,
output_target_files_temp_dir=output_target_files_temp_dir,
framework_misc_info_keys=framework_misc_info_keys)
process_dynamic_partitions_info_txt(
- framework_target_files_dir=framework_target_files_temp_dir,
- vendor_target_files_dir=vendor_target_files_temp_dir,
+ framework_target_files_dir=framework_meta,
+ vendor_target_files_dir=vendor_meta,
output_target_files_dir=output_target_files_temp_dir)
process_apex_keys_apk_certs_common(
- framework_target_files_dir=framework_target_files_temp_dir,
- vendor_target_files_dir=vendor_target_files_temp_dir,
+ framework_target_files_dir=framework_meta,
+ vendor_target_files_dir=vendor_meta,
output_target_files_dir=output_target_files_temp_dir,
framework_partition_set=framework_partition_set,
vendor_partition_set=vendor_partition_set,
file_name='apkcerts.txt')
process_apex_keys_apk_certs_common(
- framework_target_files_dir=framework_target_files_temp_dir,
- vendor_target_files_dir=vendor_target_files_temp_dir,
+ framework_target_files_dir=framework_meta,
+ vendor_target_files_dir=vendor_meta,
output_target_files_dir=output_target_files_temp_dir,
framework_partition_set=framework_partition_set,
vendor_partition_set=vendor_partition_set,
file_name='apexkeys.txt')
+ process_dexopt(
+ temp_dir=temp_dir,
+ framework_meta=framework_meta,
+ vendor_meta=vendor_meta,
+ output_target_files_temp_dir=output_target_files_temp_dir,
+ framework_dexpreopt_tools=framework_dexpreopt_tools,
+ framework_dexpreopt_config=framework_dexpreopt_config,
+ vendor_dexpreopt_config=vendor_dexpreopt_config)
+
+
+def process_dexopt(temp_dir, framework_meta, vendor_meta,
+ output_target_files_temp_dir,
+ framework_dexpreopt_tools, framework_dexpreopt_config,
+ vendor_dexpreopt_config):
+ """If needed, generates dexopt files for vendor apps.
+
+ Args:
+ temp_dir: Location containing an 'output' directory where target files have
+ been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES, etc.
+ framework_meta: The name of a directory containing the special items
+ extracted from the framework target files package.
+ vendor_meta: The name of a directory containing the special items extracted
+ from the vendor target files package.
+ output_target_files_temp_dir: The name of a directory that will be used to
+ create the output target files package after all the special cases are
+ processed.
+ framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+ framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+ vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
+ """
+ # Load vendor and framework META/misc_info.txt.
+ misc_info_path = ['META', 'misc_info.txt']
+ vendor_misc_info_dict = common.LoadDictionaryFromFile(
+ os.path.join(vendor_meta, *misc_info_path))
+
+ if (vendor_misc_info_dict.get('building_with_vsdk') != 'true' or
+ framework_dexpreopt_tools is None or
+ framework_dexpreopt_config is None or
+ vendor_dexpreopt_config is None):
+ return
+
+ logger.info('applying dexpreopt')
+
+ # The directory structure to apply dexpreopt is:
+ #
+ # <temp_dir>/
+ # framework_meta/
+ # META/
+ # vendor_meta/
+ # META/
+ # output/
+ # SYSTEM/
+ # VENDOR/
+ # IMAGES/
+ # <other items extracted from system and vendor target files>
+ # tools/
+ # <contents of dexpreopt_tools.zip>
+ # system_config/
+ # <contents of system dexpreopt_config.zip>
+ # vendor_config/
+ # <contents of vendor dexpreopt_config.zip>
+ # system -> output/SYSTEM
+ # vendor -> output/VENDOR
+ # apex -> output/SYSTEM/apex (only for flattened APEX builds)
+ # apex/ (extracted updatable APEX)
+ # <apex 1>/
+ # ...
+ # <apex 2>/
+ # ...
+ # ...
+ # out/dex2oat_result/vendor/
+ # <app>
+ # oat/arm64/
+ # package.vdex
+ # package.odex
+ # <priv-app>
+ # oat/arm64/
+ # package.vdex
+ # package.odex
+ dexpreopt_tools_files_temp_dir = os.path.join(temp_dir, 'tools')
+ dexpreopt_framework_config_files_temp_dir = os.path.join(temp_dir, 'system_config')
+ dexpreopt_vendor_config_files_temp_dir = os.path.join(temp_dir, 'vendor_config')
+
+ extract_items(
+ target_files=OPTIONS.framework_dexpreopt_tools,
+ target_files_temp_dir=dexpreopt_tools_files_temp_dir,
+ extract_item_list=('*',))
+ extract_items(
+ target_files=OPTIONS.framework_dexpreopt_config,
+ target_files_temp_dir=dexpreopt_framework_config_files_temp_dir,
+ extract_item_list=('*',))
+ extract_items(
+ target_files=OPTIONS.vendor_dexpreopt_config,
+ target_files_temp_dir=dexpreopt_vendor_config_files_temp_dir,
+ extract_item_list=('*',))
+
+ os.symlink(os.path.join(output_target_files_temp_dir, "SYSTEM"),
+ os.path.join(temp_dir, "system"))
+ os.symlink(os.path.join(output_target_files_temp_dir, "VENDOR"),
+ os.path.join(temp_dir, "vendor"))
+
+ # The directory structure for flatteded APEXes is:
+ #
+ # SYSTEM
+ # apex
+ # <APEX name, e.g., com.android.wifi>
+ # apex_manifest.pb
+ # apex_pubkey
+ # etc/
+ # javalib/
+ # lib/
+ # lib64/
+ # priv-app/
+ #
+ # The directory structure for updatable APEXes is:
+ #
+ # SYSTEM
+ # apex
+ # com.android.adbd.apex
+ # com.android.appsearch.apex
+ # com.android.art.apex
+ # ...
+ apex_root = os.path.join(output_target_files_temp_dir, "SYSTEM", "apex")
+ framework_misc_info_dict = common.LoadDictionaryFromFile(
+ os.path.join(framework_meta, *misc_info_path))
+
+ # Check for flattended versus updatable APEX.
+ if framework_misc_info_dict.get('target_flatten_apex') == 'false':
+ # Extract APEX.
+ logging.info('extracting APEX')
+
+ apex_extract_root_dir = os.path.join(temp_dir, 'apex')
+ os.makedirs(apex_extract_root_dir)
+
+ for apex in (glob.glob(os.path.join(apex_root, '*.apex')) +
+ glob.glob(os.path.join(apex_root, '*.capex'))):
+ logging.info(' apex: %s', apex)
+ # deapexer is in the same directory as the merge_target_files binary extracted
+ # from otatools.zip.
+ apex_json_info = subprocess.check_output(['deapexer', 'info', apex])
+ logging.info(' info: %s', apex_json_info)
+ apex_info = json.loads(apex_json_info)
+ apex_name = apex_info['name']
+ logging.info(' name: %s', apex_name)
+
+ apex_extract_dir = os.path.join(apex_extract_root_dir, apex_name)
+ os.makedirs(apex_extract_dir)
+
+ # deapexer uses debugfs_static, which is part of otatools.zip.
+ command = [
+ 'deapexer',
+ '--debugfs_path',
+ 'debugfs_static',
+ 'extract',
+ apex,
+ apex_extract_dir,
+ ]
+ logging.info(' running %s', command)
+ subprocess.check_call(command)
+ else:
+ # Flattened APEXes don't need to be extracted since they have the necessary
+ # directory structure.
+ os.symlink(os.path.join(apex_root), os.path.join(temp_dir, 'apex'))
+
+ # Modify system config to point to the tools that have been extracted.
+ # Absolute or .. paths are not allowed by the dexpreopt_gen tool in
+ # dexpreopt_soong.config.
+ dexpreopt_framework_soon_config = os.path.join(
+ dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config')
+ with open(dexpreopt_framework_soon_config, 'w') as f:
+ dexpreopt_soong_config = {
+ 'Profman': 'tools/profman',
+ 'Dex2oat': 'tools/dex2oatd',
+ 'Aapt': 'tools/aapt2',
+ 'SoongZip': 'tools/soong_zip',
+ 'Zip2zip': 'tools/zip2zip',
+ 'ManifestCheck': 'tools/manifest_check',
+ 'ConstructContext': 'tools/construct_context',
+ }
+ json.dump(dexpreopt_soong_config, f)
+
+ # TODO(b/188179859): Make *dex location configurable to vendor or system_other.
+ use_system_other_odex = False
+
+ if use_system_other_odex:
+ dex_img = 'SYSTEM_OTHER'
+ else:
+ dex_img = 'VENDOR'
+ # Open vendor_filesystem_config to append the items generated by dexopt.
+ vendor_file_system_config = open(
+ os.path.join(temp_dir, 'output', 'META', 'vendor_filesystem_config.txt'),
+ 'a')
+
+ # Dexpreopt vendor apps.
+ dexpreopt_config_suffix = '_dexpreopt.config'
+ for config in glob.glob(os.path.join(
+ dexpreopt_vendor_config_files_temp_dir, '*' + dexpreopt_config_suffix)):
+ app = os.path.basename(config)[:-len(dexpreopt_config_suffix)]
+ logging.info('dexpreopt config: %s %s', config, app)
+
+ apk_dir = 'app'
+ apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
+ if not os.path.exists(apk_path):
+ apk_dir = 'priv-app'
+ apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
+ if not os.path.exists(apk_path):
+ logging.warning('skipping dexpreopt for %s, no apk found in vendor/app '
+ 'or vendor/priv-app', app)
+ continue
+
+ # Generate dexpreopting script. Note 'out_dir' is not the output directory
+ # where the script is generated, but the OUT_DIR at build time referenced
+ # in the dexpreot config files, e.g., "out/.../core-oj.jar", so the tool knows
+ # how to adjust the path.
+ command = [
+ os.path.join(dexpreopt_tools_files_temp_dir, 'dexpreopt_gen'),
+ '-global',
+ os.path.join(dexpreopt_framework_config_files_temp_dir, 'dexpreopt.config'),
+ '-global_soong',
+ os.path.join(
+ dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config'),
+ '-module',
+ config,
+ '-dexpreopt_script',
+ 'dexpreopt_app.sh',
+ '-out_dir',
+ 'out',
+ '-base_path',
+ '.',
+ '--uses_target_files',
+ ]
+
+ # Run the command from temp_dir so all tool paths are its descendants.
+ logging.info("running %s", command)
+ subprocess.check_call(command, cwd = temp_dir)
+
+ # Call the generated script.
+ command = ['sh', 'dexpreopt_app.sh', apk_path]
+ logging.info("running %s", command)
+ subprocess.check_call(command, cwd = temp_dir)
+
+ # Output files are in:
+ #
+ # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.vdex
+ # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.odex
+ # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.vdex
+ # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.odex
+ #
+ # Copy the files to their destination. The structure of system_other is:
+ #
+ # system_other/
+ # system-other-odex-marker
+ # system/
+ # app/
+ # <app>/oat/arm64/
+ # <app>.odex
+ # <app>.vdex
+ # ...
+ # priv-app/
+ # <app>/oat/arm64/
+ # <app>.odex
+ # <app>.vdex
+ # ...
+
+ # TODO(b/188179859): Support for other architectures.
+ arch = 'arm64'
+
+ dex_destination = os.path.join(temp_dir, 'output', dex_img, apk_dir, app, 'oat', arch)
+ os.makedirs(dex_destination)
+ dex2oat_path = os.path.join(
+ temp_dir, 'out', 'dex2oat_result', 'vendor', apk_dir, app, 'oat', arch)
+ shutil.copy(os.path.join(dex2oat_path, 'package.vdex'),
+ os.path.join(dex_destination, app + '.vdex'))
+ shutil.copy(os.path.join(dex2oat_path, 'package.odex'),
+ os.path.join(dex_destination, app + '.odex'))
+
+ # Append entries to vendor_file_system_config.txt, such as:
+ #
+ # vendor/app/<app>/oat 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+ # vendor/app/<app>/oat/arm64 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+ # vendor/app/<app>/oat/arm64/<app>.odex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+ # vendor/app/<app>/oat/arm64/<app>.vdex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+ if not use_system_other_odex:
+ vendor_app_prefix = 'vendor/' + apk_dir + '/' + app + '/oat'
+ selabel = 'selabel=u:object_r:vendor_app_file:s0 capabilities=0x0'
+ vendor_file_system_config.writelines([
+ vendor_app_prefix + ' 0 2000 755 ' + selabel + '\n',
+ vendor_app_prefix + '/' + arch + ' 0 2000 755 ' + selabel + '\n',
+ vendor_app_prefix + '/' + arch + '/' + app + '.odex 0 0 644 ' + selabel + '\n',
+ vendor_app_prefix + '/' + arch + '/' + app + '.vdex 0 0 644 ' + selabel + '\n',
+ ])
+
+ if not use_system_other_odex:
+ vendor_file_system_config.close()
+ # Delete vendor.img so that it will be regenerated.
+ # TODO(b/188179859): Rebuilding a vendor image in GRF mode (e.g., T(framework)
+ # and S(vendor) may require logic similar to that in
+ # rebuild_image_with_sepolicy.
+ vendor_img = os.path.join(output_target_files_temp_dir, 'IMAGES', 'vendor.img')
+ if os.path.exists(vendor_img):
+ logging.info('Deleting %s', vendor_img)
+ os.remove(vendor_img)
+
def create_merged_package(temp_dir, framework_target_files, framework_item_list,
vendor_target_files, vendor_item_list,
- framework_misc_info_keys, rebuild_recovery):
+ framework_misc_info_keys, rebuild_recovery,
+ framework_dexpreopt_tools, framework_dexpreopt_config,
+ vendor_dexpreopt_config):
"""Merges two target files packages into one target files structure.
Args:
@@ -898,6 +1236,12 @@
rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
devices and write it to the system image.
+ The following are only used if dexpreopt is applied:
+
+ framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+ framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+ vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
+
Returns:
Path to merged package under temp directory.
"""
@@ -918,23 +1262,27 @@
# Perform special case processing on META/* items.
# After this function completes successfully, all the files we need to create
# the output target files package are in place.
- framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
- vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
+ framework_meta = os.path.join(temp_dir, 'framework_meta')
+ vendor_meta = os.path.join(temp_dir, 'vendor_meta')
extract_items(
target_files=framework_target_files,
- target_files_temp_dir=framework_target_files_temp_dir,
+ target_files_temp_dir=framework_meta,
extract_item_list=('META/*',))
extract_items(
target_files=vendor_target_files,
- target_files_temp_dir=vendor_target_files_temp_dir,
+ target_files_temp_dir=vendor_meta,
extract_item_list=('META/*',))
process_special_cases(
- framework_target_files_temp_dir=framework_target_files_temp_dir,
- vendor_target_files_temp_dir=vendor_target_files_temp_dir,
+ temp_dir=temp_dir,
+ framework_meta=framework_meta,
+ vendor_meta=vendor_meta,
output_target_files_temp_dir=output_target_files_temp_dir,
framework_misc_info_keys=framework_misc_info_keys,
framework_partition_set=item_list_to_partition_set(framework_item_list),
- vendor_partition_set=item_list_to_partition_set(vendor_item_list))
+ vendor_partition_set=item_list_to_partition_set(vendor_item_list),
+ framework_dexpreopt_tools=framework_dexpreopt_tools,
+ framework_dexpreopt_config=framework_dexpreopt_config,
+ vendor_dexpreopt_config=vendor_dexpreopt_config)
return output_target_files_temp_dir
@@ -965,6 +1313,102 @@
add_img_to_target_files.main(add_img_args)
+def rebuild_image_with_sepolicy(target_files_dir,
+ vendor_otatools=None,
+ vendor_target_files=None):
+ """Rebuilds odm.img or vendor.img to include merged sepolicy files.
+
+ If odm is present then odm is preferred -- otherwise vendor is used.
+
+ Args:
+ target_files_dir: Path to the extracted merged target-files package.
+ vendor_otatools: If not None, path to an otatools.zip from the vendor build
+ that is used when recompiling the image.
+ vendor_target_files: Expected if vendor_otatools is not None. Path to the
+ vendor target-files zip.
+ """
+ partition = 'vendor'
+ if os.path.exists(os.path.join(target_files_dir, 'ODM')) or os.path.exists(
+ os.path.join(target_files_dir, 'IMAGES/odm.img')):
+ partition = 'odm'
+ partition_img = '{}.img'.format(partition)
+
+ logger.info('Recompiling %s using the merged sepolicy files.', partition_img)
+
+ # Copy the combined SEPolicy file and framework hashes to the image that is
+ # being rebuilt.
+ def copy_selinux_file(input_path, output_filename):
+ input_filename = os.path.join(target_files_dir, input_path)
+ if not os.path.exists(input_filename):
+ input_filename = input_filename.replace('SYSTEM_EXT/', 'SYSTEM/system_ext/') \
+ .replace('PRODUCT/', 'SYSTEM/product/')
+ if not os.path.exists(input_filename):
+ logger.info('Skipping copy_selinux_file for %s', input_filename)
+ return
+ shutil.copy(
+ input_filename,
+ os.path.join(target_files_dir, partition.upper(), 'etc/selinux',
+ output_filename))
+
+ copy_selinux_file('META/combined_sepolicy', 'precompiled_sepolicy')
+ copy_selinux_file('SYSTEM/etc/selinux/plat_sepolicy_and_mapping.sha256',
+ 'precompiled_sepolicy.plat_sepolicy_and_mapping.sha256')
+ copy_selinux_file(
+ 'SYSTEM_EXT/etc/selinux/system_ext_sepolicy_and_mapping.sha256',
+ 'precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256')
+ copy_selinux_file('PRODUCT/etc/selinux/product_sepolicy_and_mapping.sha256',
+ 'precompiled_sepolicy.product_sepolicy_and_mapping.sha256')
+
+ if not vendor_otatools:
+ # Remove the partition from the merged target-files archive. It will be
+ # rebuilt later automatically by generate_images().
+ os.remove(os.path.join(target_files_dir, 'IMAGES', partition_img))
+ else:
+ # TODO(b/192253131): Remove the need for vendor_otatools by fixing
+ # backwards-compatibility issues when compiling images on R from S+.
+ if not vendor_target_files:
+ raise ValueError(
+ 'Expected vendor_target_files if vendor_otatools is not None.')
+ logger.info(
+ '%s recompilation will be performed using the vendor otatools.zip',
+ partition_img)
+
+ # Unzip the vendor build's otatools.zip and target-files archive.
+ vendor_otatools_dir = common.MakeTempDir(
+ prefix='merge_target_files_vendor_otatools_')
+ vendor_target_files_dir = common.MakeTempDir(
+ prefix='merge_target_files_vendor_target_files_')
+ common.UnzipToDir(vendor_otatools, vendor_otatools_dir)
+ common.UnzipToDir(vendor_target_files, vendor_target_files_dir)
+
+ # Copy the partition contents from the merged target-files archive to the
+ # vendor target-files archive.
+ shutil.rmtree(os.path.join(vendor_target_files_dir, partition.upper()))
+ shutil.copytree(
+ os.path.join(target_files_dir, partition.upper()),
+ os.path.join(vendor_target_files_dir, partition.upper()),
+ symlinks=True)
+
+ # Delete then rebuild the partition.
+ os.remove(os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))
+ rebuild_partition_command = [
+ os.path.join(vendor_otatools_dir, 'bin', 'add_img_to_target_files'),
+ '--verbose',
+ '--add_missing',
+ vendor_target_files_dir,
+ ]
+ logger.info('Recompiling %s: %s', partition_img,
+ ' '.join(rebuild_partition_command))
+ common.RunAndCheckOutput(rebuild_partition_command, verbose=True)
+
+ # Move the newly-created image to the merged target files dir.
+ if not os.path.exists(os.path.join(target_files_dir, 'IMAGES')):
+ os.makedirs(os.path.join(target_files_dir, 'IMAGES'))
+ shutil.move(
+ os.path.join(vendor_target_files_dir, 'IMAGES', partition_img),
+ os.path.join(target_files_dir, 'IMAGES', partition_img))
+
+
def generate_super_empty_image(target_dir, output_super_empty):
"""Generates super_empty image from target package.
@@ -1049,7 +1493,9 @@
framework_misc_info_keys, vendor_target_files,
vendor_item_list, output_target_files, output_dir,
output_item_list, output_ota, output_img,
- output_super_empty, rebuild_recovery):
+ output_super_empty, rebuild_recovery, vendor_otatools,
+ rebuild_sepolicy, framework_dexpreopt_tools,
+ framework_dexpreopt_config, vendor_dexpreopt_config):
"""Merges two target files packages together.
This function takes framework and vendor target files packages as input,
@@ -1085,6 +1531,15 @@
merged target files package and saves it at this path.
rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
devices and write it to the system image.
+ vendor_otatools: Path to an otatools zip used for recompiling vendor images.
+ rebuild_sepolicy: If true, rebuild odm.img (if target uses ODM) or
+ vendor.img using a merged precompiled_sepolicy file.
+
+ The following are only used if dexpreopt is applied:
+
+ framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+ framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+ vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
"""
logger.info('starting: merge framework %s and vendor %s into output %s',
@@ -1093,7 +1548,8 @@
output_target_files_temp_dir = create_merged_package(
temp_dir, framework_target_files, framework_item_list,
vendor_target_files, vendor_item_list, framework_misc_info_keys,
- rebuild_recovery)
+ rebuild_recovery, framework_dexpreopt_tools, framework_dexpreopt_config,
+ vendor_dexpreopt_config)
if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
raise RuntimeError('Incompatible VINTF metadata')
@@ -1137,14 +1593,14 @@
partition_map=filtered_partitions)
# Check that the split sepolicy from the multiple builds can compile.
- split_sepolicy_cmd = compile_split_sepolicy(
- product_out=output_target_files_temp_dir,
- partition_map=filtered_partitions,
- output_policy=os.path.join(output_target_files_temp_dir,
- 'META/combined.policy'))
+ split_sepolicy_cmd = compile_split_sepolicy(output_target_files_temp_dir,
+ filtered_partitions)
logger.info('Compiling split sepolicy: %s', ' '.join(split_sepolicy_cmd))
common.RunAndCheckOutput(split_sepolicy_cmd)
- # TODO(b/178864050): Run tests on the combined.policy file.
+ # Include the compiled policy in an image if requested.
+ if rebuild_sepolicy:
+ rebuild_image_with_sepolicy(output_target_files_temp_dir, vendor_otatools,
+ vendor_target_files)
# Run validation checks on the pre-installed APEX files.
validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
@@ -1261,8 +1717,18 @@
OPTIONS.rebuild_recovery = True
elif o == '--allow-duplicate-apkapex-keys':
OPTIONS.allow_duplicate_apkapex_keys = True
+ elif o == '--vendor-otatools':
+ OPTIONS.vendor_otatools = a
+ elif o == '--rebuild-sepolicy':
+ OPTIONS.rebuild_sepolicy = True
elif o == '--keep-tmp':
OPTIONS.keep_tmp = True
+ elif o == '--framework-dexpreopt-config':
+ OPTIONS.framework_dexpreopt_config = a
+ elif o == '--framework-dexpreopt-tools':
+ OPTIONS.framework_dexpreopt_tools = a
+ elif o == '--vendor-dexpreopt-config':
+ OPTIONS.vendor_dexpreopt_config = a
else:
return False
return True
@@ -1287,8 +1753,13 @@
'output-ota=',
'output-img=',
'output-super-empty=',
+ 'framework-dexpreopt-config=',
+ 'framework-dexpreopt-tools=',
+ 'vendor-dexpreopt-config=',
'rebuild_recovery',
'allow-duplicate-apkapex-keys',
+ 'vendor-otatools=',
+ 'rebuild-sepolicy',
'keep-tmp',
],
extra_option_handler=option_handler)
@@ -1342,7 +1813,12 @@
output_ota=OPTIONS.output_ota,
output_img=OPTIONS.output_img,
output_super_empty=OPTIONS.output_super_empty,
- rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
+ rebuild_recovery=OPTIONS.rebuild_recovery,
+ vendor_otatools=OPTIONS.vendor_otatools,
+ rebuild_sepolicy=OPTIONS.rebuild_sepolicy,
+ framework_dexpreopt_tools=OPTIONS.framework_dexpreopt_tools,
+ framework_dexpreopt_config=OPTIONS.framework_dexpreopt_config,
+ vendor_dexpreopt_config=OPTIONS.vendor_dexpreopt_config), OPTIONS.keep_tmp)
if __name__ == '__main__':
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 42d1211..c21de14 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -221,6 +221,15 @@
For VABC downgrades, we must finish merging before doing data wipe, and
since data wipe is required for downgrading OTA, this might cause long
wait time in recovery.
+
+ --enable_vabc_xor
+ Enable the VABC xor feature. Will reduce space requirements for OTA
+
+ --force_minor_version
+ Override the update_engine minor version for delta generation.
+
+ --compressor_types
+ A colon ':' separated list of compressors. Allowed values are bz2 and brotli.
"""
from __future__ import print_function
@@ -237,10 +246,12 @@
import sys
import zipfile
+import care_map_pb2
import common
import ota_utils
from ota_utils import (UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata,
- PropertyFiles, SECURITY_PATCH_LEVEL_PROP_NAME)
+ PropertyFiles, SECURITY_PATCH_LEVEL_PROP_NAME, GetZipEntryOffset)
+from common import IsSparseImage
import target_files_diff
from check_target_files_vintf import CheckVintfIfTrebleEnabled
from non_ab_ota import GenerateNonAbOtaPackage
@@ -285,6 +296,9 @@
OPTIONS.disable_vabc = False
OPTIONS.spl_downgrade = False
OPTIONS.vabc_downgrade = False
+OPTIONS.enable_vabc_xor = True
+OPTIONS.force_minor_version = None
+OPTIONS.compressor_types = None
POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
@@ -529,6 +543,8 @@
'payload_properties.txt',
)
self.optional = (
+ # apex_info.pb isn't directly used in the update flow
+ 'apex_info.pb',
# care_map is available only if dm-verity is enabled.
'care_map.pb',
'care_map.txt',
@@ -599,20 +615,20 @@
payload, till the end of 'medatada_signature_message'.
"""
payload_info = input_zip.getinfo('payload.bin')
- payload_offset = payload_info.header_offset
- payload_offset += zipfile.sizeFileHeader
- payload_offset += len(payload_info.extra) + len(payload_info.filename)
- payload_size = payload_info.file_size
+ (payload_offset, payload_size) = GetZipEntryOffset(input_zip, payload_info)
- with input_zip.open('payload.bin') as payload_fp:
- header_bin = payload_fp.read(24)
+ # Read the underlying raw zipfile at specified offset
+ payload_fp = input_zip.fp
+ payload_fp.seek(payload_offset)
+ header_bin = payload_fp.read(24)
# network byte order (big-endian)
header = struct.unpack("!IQQL", header_bin)
# 'CrAU'
magic = header[0]
- assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
+ assert magic == 0x43724155, "Invalid magic: {:x}, computed offset {}" \
+ .format(magic, payload_offset)
manifest_size = header[2]
metadata_signature_size = header[3]
@@ -832,6 +848,17 @@
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())
+
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)
@@ -841,7 +868,8 @@
content, lambda p: p in ab_partitions)
common.ZipWriteStr(partial_target_zip, info_file, modified_info)
- # TODO(xunchang) handle 'META/care_map.pb', 'META/postinstall_config.txt'
+ # TODO(xunchang) handle META/postinstall_config.txt'
+
common.ZipClose(partial_target_zip)
return partial_target_file
@@ -994,13 +1022,6 @@
]
-def IsSparseImage(filepath):
- with open(filepath, 'rb') as fp:
- # Magic for android sparse image format
- # https://source.android.com/devices/bootloader/images
- return fp.read(4) == b'\x3A\xFF\x26\xED'
-
-
def SupportsMainlineGkiUpdates(target_file):
"""Return True if the build supports MainlineGKIUpdates.
@@ -1075,6 +1096,9 @@
if target_info.vendor_suppressed_vabc:
logger.info("Vendor suppressed VABC. Disabling")
OPTIONS.disable_vabc = True
+ if not target_info.is_vabc_xor or OPTIONS.disable_vabc:
+ logger.info("VABC XOR Not supported, disabling")
+ OPTIONS.enable_vabc_xor = False
additional_args = []
# Prepare custom images.
@@ -1097,6 +1121,8 @@
target_info.info_dict['ab_partitions'] = zfp.read(
AB_PARTITIONS).decode().strip().split("\n")
+ CheckVintfIfTrebleEnabled(target_file, target_info)
+
# Metadata to comply with Android OTA package format.
metadata = GetPackageMetadata(target_info, source_info)
# Generate payload.
@@ -1117,6 +1143,12 @@
if OPTIONS.disable_vabc:
additional_args += ["--disable_vabc", "true"]
+ if OPTIONS.enable_vabc_xor:
+ additional_args += ["--enable_vabc_xor", "true"]
+ if OPTIONS.force_minor_version:
+ additional_args += ["--force_minor_version", OPTIONS.force_minor_version]
+ if OPTIONS.compressor_types:
+ additional_args += ["--compressor_types", OPTIONS.compressor_types]
additional_args += ["--max_timestamp", max_timestamp]
if SupportsMainlineGkiUpdates(source_file):
@@ -1170,19 +1202,15 @@
else:
logger.warning("Cannot find care map file in target_file package")
- # Copy apex_info.pb over to generated OTA package.
- try:
- apex_info_entry = target_zip.getinfo("META/apex_info.pb")
- with target_zip.open(apex_info_entry, "r") as zfp:
- common.ZipWriteStr(output_zip, "apex_info.pb", zfp.read(),
- compress_type=zipfile.ZIP_STORED)
- except KeyError:
- logger.warning("target_file doesn't contain apex_info.pb %s", target_file)
+ # Add the source apex version for incremental ota updates, and write the
+ # result apex info to the ota package.
+ ota_apex_info = ota_utils.ConstructOtaApexInfo(target_zip, source_file)
+ if ota_apex_info is not None:
+ common.ZipWriteStr(output_zip, "apex_info.pb", ota_apex_info,
+ compress_type=zipfile.ZIP_STORED)
common.ZipClose(target_zip)
- CheckVintfIfTrebleEnabled(target_file, target_info)
-
# We haven't written the metadata entry yet, which will be handled in
# FinalizeMetadata().
common.ZipClose(output_zip)
@@ -1292,6 +1320,12 @@
OPTIONS.wipe_user_data = True
elif o == "--vabc_downgrade":
OPTIONS.vabc_downgrade = True
+ elif o == "--enable_vabc_xor":
+ OPTIONS.enable_vabc_xor = a.lower() != "false"
+ elif o == "--force_minor_version":
+ OPTIONS.force_minor_version = a
+ elif o == "--compressor_types":
+ OPTIONS.compressor_types = a
else:
return False
return True
@@ -1336,6 +1370,9 @@
"disable_vabc",
"spl_downgrade",
"vabc_downgrade",
+ "enable_vabc_xor=",
+ "force_minor_version=",
+ "compressor_types=",
], extra_option_handler=option_handler)
if len(args) != 2:
@@ -1367,8 +1404,8 @@
# We should only allow downgrading incrementals (as opposed to full).
# Otherwise the device may go back from arbitrary build with this full
# OTA package.
- if OPTIONS.incremental_source is None:
- raise ValueError("Cannot generate downgradable full OTAs")
+ if OPTIONS.incremental_source is None and OPTIONS.downgrade:
+ raise ValueError("Cannot generate downgradable full OTAs")
# TODO(xunchang) for retrofit and partial updates, maybe we should rebuild the
# target-file and reload the info_dict. So the info will be consistent with
@@ -1436,13 +1473,23 @@
"build/make/target/product/security/testkey")
# Get signing keys
OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
- private_key_path = OPTIONS.package_key + OPTIONS.private_key_suffix
- if not os.path.exists(private_key_path):
- raise common.ExternalError(
- "Private key {} doesn't exist. Make sure you passed the"
- " correct key path through -k option".format(
- private_key_path)
- )
+
+ # Only check for existence of key file if using the default signer.
+ # Because the custom signer might not need the key file AT all.
+ # b/191704641
+ if not OPTIONS.payload_signer:
+ private_key_path = OPTIONS.package_key + OPTIONS.private_key_suffix
+ if not os.path.exists(private_key_path):
+ raise common.ExternalError(
+ "Private key {} doesn't exist. Make sure you passed the"
+ " correct key path through -k option".format(
+ private_key_path)
+ )
+ signapk_abs_path = os.path.join(
+ OPTIONS.search_path, OPTIONS.signapk_path)
+ if not os.path.exists(signapk_abs_path):
+ raise common.ExternalError(
+ "Failed to find sign apk binary {} in search path {}. Make sure the correct search path is passed via -p".format(OPTIONS.signapk_path, OPTIONS.search_path))
if OPTIONS.source_info_dict:
source_build_prop = OPTIONS.source_info_dict["build.prop"]
@@ -1494,8 +1541,5 @@
try:
common.CloseInheritedPipes()
main(sys.argv[1:])
- except common.ExternalError:
- logger.exception("\n ERROR:\n")
- sys.exit(1)
finally:
common.Cleanup()
diff --git a/tools/releasetools/ota_metadata.proto b/tools/releasetools/ota_metadata.proto
index ed9d0c3..689ce80 100644
--- a/tools/releasetools/ota_metadata.proto
+++ b/tools/releasetools/ota_metadata.proto
@@ -72,6 +72,8 @@
int64 version = 2;
bool is_compressed = 3;
int64 decompressed_size = 4;
+ // Used in OTA
+ int64 source_version = 5;
}
// Just a container to hold repeated apex_info, so that we can easily serialize
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 104f02f..6c5fc05 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -16,6 +16,7 @@
import itertools
import logging
import os
+import struct
import zipfile
import ota_metadata_pb2
@@ -153,7 +154,7 @@
compress_type=zipfile.ZIP_STORED)
return
- with open('{}.pb'.format(output), 'w') as f:
+ with open('{}.pb'.format(output), 'wb') as f:
f.write(metadata_proto.SerializeToString())
with open(output, 'w') as f:
f.write(legacy_metadata)
@@ -399,6 +400,35 @@
return device_names, fingerprints
+def GetZipEntryOffset(zfp, entry_info):
+ """Get offset to a beginning of a particular zip entry
+ Args:
+ fp: zipfile.ZipFile
+ entry_info: zipfile.ZipInfo
+
+ Returns:
+ (offset, size) tuple
+ """
+ # Don't use len(entry_info.extra). Because that returns size of extra
+ # fields in central directory. We need to look at local file directory,
+ # as these two might have different sizes.
+
+ # We cannot work with zipfile.ZipFile instances, we need a |fp| for the underlying file.
+ zfp = zfp.fp
+ zfp.seek(entry_info.header_offset)
+ data = zfp.read(zipfile.sizeFileHeader)
+ fheader = struct.unpack(zipfile.structFileHeader, data)
+ # Last two fields of local file header are filename length and
+ # extra length
+ filename_len = fheader[-2]
+ extra_len = fheader[-1]
+ offset = entry_info.header_offset
+ offset += zipfile.sizeFileHeader
+ offset += filename_len + extra_len
+ size = entry_info.file_size
+ return (offset, size)
+
+
class PropertyFiles(object):
"""A class that computes the property-files string for an OTA package.
@@ -517,10 +547,7 @@
def ComputeEntryOffsetSize(name):
"""Computes the zip entry offset and size."""
info = zip_file.getinfo(name)
- offset = info.header_offset
- offset += zipfile.sizeFileHeader
- offset += len(info.extra) + len(info.filename)
- size = info.file_size
+ (offset, size) = GetZipEntryOffset(zip_file, info)
return '%s:%d:%d' % (os.path.basename(name), offset, size)
tokens = []
@@ -569,3 +596,45 @@
SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
whole_file=True)
+
+
+def ConstructOtaApexInfo(target_zip, source_file=None):
+ """If applicable, add the source version to the apex info."""
+
+ def _ReadApexInfo(input_zip):
+ if "META/apex_info.pb" not in input_zip.namelist():
+ logger.warning("target_file doesn't contain apex_info.pb %s", input_zip)
+ return None
+
+ with input_zip.open("META/apex_info.pb", "r") as zfp:
+ return zfp.read()
+
+ target_apex_string = _ReadApexInfo(target_zip)
+ # Return early if the target apex info doesn't exist or is empty.
+ if not target_apex_string:
+ return target_apex_string
+
+ # If the source apex info isn't available, just return the target info
+ if not source_file:
+ return target_apex_string
+
+ with zipfile.ZipFile(source_file, "r", allowZip64=True) as source_zip:
+ source_apex_string = _ReadApexInfo(source_zip)
+ if not source_apex_string:
+ return target_apex_string
+
+ source_apex_proto = ota_metadata_pb2.ApexMetadata()
+ source_apex_proto.ParseFromString(source_apex_string)
+ source_apex_versions = {apex.package_name: apex.version for apex in
+ source_apex_proto.apex_info}
+
+ # If the apex package is available in the source build, initialize the source
+ # apex version.
+ target_apex_proto = ota_metadata_pb2.ApexMetadata()
+ target_apex_proto.ParseFromString(target_apex_string)
+ for target_apex in target_apex_proto.apex_info:
+ name = target_apex.package_name
+ if name in source_apex_versions:
+ target_apex.source_version = source_apex_versions[name]
+
+ return target_apex_proto.SerializeToString()
diff --git a/tools/releasetools/sign_apex.py b/tools/releasetools/sign_apex.py
index fb947f4..679f57a 100755
--- a/tools/releasetools/sign_apex.py
+++ b/tools/releasetools/sign_apex.py
@@ -39,6 +39,9 @@
--codename_to_api_level_map Q:29,R:30,...
A Mapping of codename to api level. This is useful to provide sdk targeting
information to APK Signer.
+
+ --sign_tool <sign_tool>
+ Optional flag that specifies a custom signing tool for the contents of the apex.
"""
import logging
@@ -52,7 +55,7 @@
def SignApexFile(avbtool, apex_file, payload_key, container_key, no_hashtree,
- apk_keys=None, signing_args=None, codename_to_api_level_map=None):
+ apk_keys=None, signing_args=None, codename_to_api_level_map=None, sign_tool=None):
"""Signs the given apex file."""
with open(apex_file, 'rb') as input_fp:
apex_data = input_fp.read()
@@ -66,7 +69,8 @@
codename_to_api_level_map=codename_to_api_level_map,
no_hashtree=no_hashtree,
apk_keys=apk_keys,
- signing_args=signing_args)
+ signing_args=signing_args,
+ sign_tool=sign_tool)
def main(argv):
@@ -100,6 +104,8 @@
if 'extra_apks' not in options:
options['extra_apks'] = {}
options['extra_apks'].update({n: key})
+ elif o == '--sign_tool':
+ options['sign_tool'] = a
else:
return False
return True
@@ -114,6 +120,7 @@
'payload_extra_args=',
'payload_key=',
'extra_apks=',
+ 'sign_tool=',
],
extra_option_handler=option_handler)
@@ -133,7 +140,8 @@
apk_keys=options.get('extra_apks', {}),
signing_args=options.get('payload_extra_args'),
codename_to_api_level_map=options.get(
- 'codename_to_api_level_map', {}))
+ 'codename_to_api_level_map', {}),
+ sign_tool=options['sign_tool'])
shutil.copyfile(signed_apex, args[1])
logger.info("done.")
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 0842af9..5626980 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -136,6 +136,11 @@
--android_jar_path <path>
Path to the android.jar to repack the apex file.
+
+ --allow_gsi_debug_sepolicy
+ Allow the existence of the file 'userdebug_plat_sepolicy.cil' under
+ (/system/system_ext|/system_ext)/etc/selinux.
+ If not set, error out when the file exists.
"""
from __future__ import print_function
@@ -189,6 +194,9 @@
OPTIONS.gki_signing_algorithm = None
OPTIONS.gki_signing_extra_args = None
OPTIONS.android_jar_path = None
+OPTIONS.vendor_partitions = set()
+OPTIONS.vendor_otatools = None
+OPTIONS.allow_gsi_debug_sepolicy = False
AVB_FOOTER_ARGS_BY_PARTITION = {
@@ -216,6 +224,10 @@
if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
+# Partitions that can be regenerated after signing using a separate
+# vendor otatools package.
+ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
+
def IsApexFile(filename):
return filename.endswith(".apex") or filename.endswith(".capex")
@@ -250,7 +262,7 @@
Args:
keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
- container_key).
+ container_key, sign_tool).
key_map: A dict that overrides the keys, specified via command-line input.
Returns:
@@ -268,11 +280,11 @@
if apex not in keys_info:
logger.warning('Failed to find %s in target_files; Ignored', apex)
continue
- keys_info[apex] = (key, keys_info[apex][1])
+ keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
# Apply the key remapping to container keys.
- for apex, (payload_key, container_key) in keys_info.items():
- keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
+ for apex, (payload_key, container_key, sign_tool) in keys_info.items():
+ keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
# Apply all the --extra_apks options to override the container keys.
for apex, key in OPTIONS.extra_apks.items():
@@ -281,13 +293,13 @@
continue
if not key:
key = 'PRESIGNED'
- keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
+ keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
# A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
# APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
# (overridden via commandline) indicates a config error, which should not be
# allowed.
- for apex, (payload_key, container_key) in keys_info.items():
+ for apex, (payload_key, container_key, sign_tool) in keys_info.items():
if container_key != 'PRESIGNED':
continue
if apex in OPTIONS.extra_apex_payload_keys:
@@ -299,7 +311,7 @@
print(
"Setting {} payload as PRESIGNED due to PRESIGNED container".format(
apex))
- keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
+ keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
return keys_info
@@ -360,7 +372,7 @@
compressed_extension: The extension string of compressed APKs, such as
'.gz', or None if there's no compressed APKs.
apex_keys: A dict that contains the key mapping from APEX name to
- (payload_key, container_key).
+ (payload_key, container_key, sign_tool).
Raises:
AssertionError: On finding unknown APKs and APEXes.
@@ -405,7 +417,7 @@
name = GetApexFilename(info.filename)
- (payload_key, container_key) = apex_keys[name]
+ (payload_key, container_key, _) = apex_keys[name]
if ((payload_key in common.SPECIAL_CERT_STRINGS and
container_key not in common.SPECIAL_CERT_STRINGS) or
(payload_key not in common.SPECIAL_CERT_STRINGS and
@@ -557,7 +569,7 @@
elif IsApexFile(filename):
name = GetApexFilename(filename)
- payload_key, container_key = apex_keys[name]
+ payload_key, container_key, sign_tool = apex_keys[name]
# We've asserted not having a case with only one of them PRESIGNED.
if (payload_key not in common.SPECIAL_CERT_STRINGS and
@@ -576,7 +588,8 @@
apk_keys,
codename_to_api_level_map,
no_hashtree=None, # Let apex_util determine if hash tree is needed
- signing_args=OPTIONS.avb_extra_args.get('apex'))
+ signing_args=OPTIONS.avb_extra_args.get('apex'),
+ sign_tool=sign_tool)
common.ZipWrite(output_tf_zip, signed_apex, filename)
else:
@@ -658,7 +671,7 @@
# Updates system_other.avbpubkey in /product/etc/.
elif filename in (
"PRODUCT/etc/security/avb/system_other.avbpubkey",
- "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
+ "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
# Only update system_other's public key, if the corresponding signing
# key is specified via --avb_system_other_key.
signing_key = OPTIONS.avb_keys.get("system_other")
@@ -671,9 +684,19 @@
# Should NOT sign boot-debug.img.
elif filename in (
"BOOT/RAMDISK/force_debuggable",
- "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
+ "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
raise common.ExternalError("debuggable boot.img cannot be signed")
+ # Should NOT sign userdebug sepolicy file.
+ elif filename in (
+ "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
+ "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
+ if not OPTIONS.allow_gsi_debug_sepolicy:
+ raise common.ExternalError("debug sepolicy shouldn't be included")
+ else:
+ # Copy it verbatim if we allow the file to exist.
+ common.ZipWriteStr(output_tf_zip, out_info, data)
+
# A non-APK file; copy it verbatim.
else:
common.ZipWriteStr(output_tf_zip, out_info, data)
@@ -1125,15 +1148,16 @@
Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
- tuple of (payload_key, container_key).
+ tuple of (payload_key, container_key, sign_tool).
Args:
tf_zip: The input target_files ZipFile (already open).
Returns:
- (payload_key, container_key): payload_key contains the path to the payload
- signing key; container_key contains the path to the container signing
- key.
+ (payload_key, container_key, sign_tool):
+ - payload_key contains the path to the payload signing key
+ - container_key contains the path to the container signing key
+ - sign_tool is an apex-specific signing tool for its payload contents
"""
keys = {}
for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
@@ -1146,7 +1170,8 @@
r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
- r'(\s+partition="(?P<PARTITION>.*?)")?$',
+ r'(\s+partition="(?P<PARTITION>.*?)")?'
+ r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
line)
if not matches:
continue
@@ -1175,11 +1200,69 @@
else:
raise ValueError("Failed to parse container keys: \n{}".format(line))
- keys[name] = (payload_private_key, container_key)
+ sign_tool = matches.group("SIGN_TOOL")
+ keys[name] = (payload_private_key, container_key, sign_tool)
return keys
+def BuildVendorPartitions(output_zip_path):
+ """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
+ if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
+ logger.warning("Allowed --vendor_partitions: %s",
+ ",".join(ALLOWED_VENDOR_PARTITIONS))
+ OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
+ OPTIONS.vendor_partitions)
+
+ logger.info("Building vendor partitions using vendor otatools.")
+ vendor_tempdir = common.UnzipTemp(output_zip_path, [
+ "META/*",
+ ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
+
+ # Disable various partitions that build based on misc_info fields.
+ # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
+ # vendor otatools. These other partitions will be rebuilt using the main
+ # otatools if necessary.
+ vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
+ vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
+ vendor_misc_info["no_boot"] = "true" # boot
+ vendor_misc_info["vendor_boot"] = "false" # vendor_boot
+ vendor_misc_info["no_recovery"] = "true" # recovery
+ vendor_misc_info["board_bpt_enable"] = "false" # partition-table
+ vendor_misc_info["has_dtbo"] = "false" # dtbo
+ vendor_misc_info["has_pvmfw"] = "false" # pvmfw
+ vendor_misc_info["avb_custom_images_partition_list"] = "" # custom images
+ vendor_misc_info["avb_enable"] = "false" # vbmeta
+ vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
+ vendor_misc_info["build_super_partition"] = "false" # super split
+ with open(vendor_misc_info_path, "w") as output:
+ for key in sorted(vendor_misc_info):
+ output.write("{}={}\n".format(key, vendor_misc_info[key]))
+
+ # Disable care_map.pb as not all ab_partitions are available when
+ # vendor otatools regenerates vendor images.
+ os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
+
+ # Build vendor images using vendor otatools.
+ vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
+ common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
+ cmd = [
+ os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
+ "--is_signing",
+ "--verbose",
+ vendor_tempdir,
+ ]
+ common.RunAndCheckOutput(cmd, verbose=True)
+
+ logger.info("Writing vendor partitions to output archive.")
+ with zipfile.ZipFile(
+ output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
+ allowZip64=True) as output_zip:
+ for p in OPTIONS.vendor_partitions:
+ path = "IMAGES/{}.img".format(p)
+ common.ZipWrite(output_zip, os.path.join(vendor_tempdir, path), path)
+
+
def main(argv):
key_mapping_options = []
@@ -1289,6 +1372,12 @@
OPTIONS.gki_signing_algorithm = a
elif o == "--gki_signing_extra_args":
OPTIONS.gki_signing_extra_args = a
+ elif o == "--vendor_otatools":
+ OPTIONS.vendor_otatools = a
+ elif o == "--vendor_partitions":
+ OPTIONS.vendor_partitions = set(a.split(","))
+ elif o == "--allow_gsi_debug_sepolicy":
+ OPTIONS.allow_gsi_debug_sepolicy = True
else:
return False
return True
@@ -1339,6 +1428,9 @@
"gki_signing_key=",
"gki_signing_algorithm=",
"gki_signing_extra_args=",
+ "vendor_partitions=",
+ "vendor_otatools=",
+ "allow_gsi_debug_sepolicy",
],
extra_option_handler=option_handler)
@@ -1384,8 +1476,11 @@
common.ZipClose(input_zip)
common.ZipClose(output_zip)
+ if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
+ BuildVendorPartitions(args[1])
+
# Skip building userdata.img and cache.img when signing the target files.
- new_args = ["--is_signing"]
+ new_args = ["--is_signing", "--add_missing", "--verbose"]
# add_img_to_target_files builds the system image from scratch, so the
# recovery patch is guaranteed to be regenerated there.
if OPTIONS.rebuild_recovery:
diff --git a/tools/releasetools/target_files_diff.py b/tools/releasetools/target_files_diff.py
index 4402c8d..fa94c5b 100755
--- a/tools/releasetools/target_files_diff.py
+++ b/tools/releasetools/target_files_diff.py
@@ -82,7 +82,7 @@
skip = True
break
if not skip:
- new.write(line)
+ new.write(line.encode())
def trim_install_recovery(original, new):
@@ -91,7 +91,7 @@
partition.
"""
for line in original:
- new.write(re.sub(r'[0-9a-f]{40}', '0'*40, line))
+ new.write(re.sub(r'[0-9a-f]{40}', '0'*40, line).encode())
def sort_file(original, new):
"""
@@ -101,7 +101,7 @@
lines = original.readlines()
lines.sort()
for line in lines:
- new.write(line)
+ new.write(line.encode())
# Map files to the functions that will modify them for diffing
REWRITE_RULES = {
@@ -148,7 +148,7 @@
if stdout == 'Binary files %s and %s differ' % (f1, f2):
print("%s: Binary files differ" % name, file=out_file)
else:
- for line in stdout.strip().split('\n'):
+ for line in stdout.strip().split(b'\n'):
print("%s: %s" % (name, line), file=out_file)
def recursiveDiff(prefix, dir1, dir2, out_file):
diff --git a/tools/releasetools/test_apex_utils.py b/tools/releasetools/test_apex_utils.py
index 71f6433..ed920f2 100644
--- a/tools/releasetools/test_apex_utils.py
+++ b/tools/releasetools/test_apex_utils.py
@@ -187,3 +187,19 @@
self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
signer.ProcessApexFile(apk_keys, self.payload_key)
+
+ @test_utils.SkipIfExternalToolsUnavailable()
+ def test_ApexApkSigner_invokesCustomSignTool(self):
+ apex_path = common.MakeTempFile(suffix='.apex')
+ shutil.copy(self.apex_with_apk, apex_path)
+ apk_keys = {'wifi-service-resources.apk': os.path.join(
+ self.testdata_dir, 'testkey')}
+ self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
+
+ # pass `false` as a sign_tool to see the invocation error
+ with self.assertRaises(common.ExternalError) as cm:
+ signer = apex_utils.ApexApkSigner(apex_path, None, None, sign_tool='false')
+ signer.ProcessApexFile(apk_keys, self.payload_key)
+
+ the_exception = cm.exception
+ self.assertIn('Failed to run command \'[\'false\'', the_exception.message)
diff --git a/tools/releasetools/test_build_image.py b/tools/releasetools/test_build_image.py
index b24805f..cfae7a5 100644
--- a/tools/releasetools/test_build_image.py
+++ b/tools/releasetools/test_build_image.py
@@ -196,7 +196,7 @@
p.communicate()
self.assertEqual(0, p.returncode)
- fs_dict = GetFilesystemCharacteristics(output_file)
+ fs_dict = GetFilesystemCharacteristics('ext4', output_file)
self.assertEqual(int(fs_dict['Block size']), 4096)
self.assertGreaterEqual(int(fs_dict['Free blocks']), 0) # expect ~88
self.assertGreater(int(fs_dict['Inode count']), 0) # expect ~64
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 1a00549..e42d417 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1631,88 +1631,6 @@
self.assertEqual('3', chained_partition_args[1])
self.assertTrue(os.path.exists(chained_partition_args[2]))
- def test_BuildVBMeta_appendAftlCommandSyntax(self):
- testdata_dir = test_utils.get_testdata_dir()
- common.OPTIONS.info_dict = {
- 'ab_update': 'true',
- 'avb_avbtool': 'avbtool',
- 'build.prop': common.PartitionBuildProps.FromDictionary(
- 'system', {
- 'ro.build.version.incremental': '6285659',
- 'ro.product.device': 'coral',
- 'ro.build.fingerprint':
- 'google/coral/coral:R/RP1A.200311.002/'
- '6285659:userdebug/dev-keys'}
- ),
- }
- common.OPTIONS.aftl_tool_path = 'aftltool'
- common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
- common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
- 'test_transparency_key.pub')
- common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
- testdata_dir, 'test_aftl_rsa4096.pem')
-
- vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
- cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
- expected_cmd = [
- 'aftltool', 'make_icp_from_vbmeta',
- '--vbmeta_image_path', 'place_holder',
- '--output', vbmeta_image.name,
- '--version_incremental', '6285659',
- '--transparency_log_servers',
- 'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
- common.OPTIONS.aftl_key_path),
- '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
- '--algorithm', 'SHA256_RSA4096',
- '--padding', '4096']
-
- # ignore the place holder, i.e. path to a temp file
- self.assertEqual(cmd[:3], expected_cmd[:3])
- self.assertEqual(cmd[4:], expected_cmd[4:])
-
- @unittest.skip("enable after we have a server for public")
- def test_BuildVBMeta_appendAftlContactServer(self):
- testdata_dir = test_utils.get_testdata_dir()
- common.OPTIONS.info_dict = {
- 'ab_update': 'true',
- 'avb_avbtool': 'avbtool',
- 'build.prop': common.PartitionBuildProps.FromDictionary(
- 'system', {
- 'ro.build.version.incremental': '6285659',
- 'ro.product.device': 'coral',
- 'ro.build.fingerprint':
- 'google/coral/coral:R/RP1A.200311.002/'
- '6285659:userdebug/dev-keys'}
- )
- }
- common.OPTIONS.aftl_tool_path = "aftltool"
- common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
- common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
- 'test_transparency_key.pub')
- common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
- testdata_dir, 'test_aftl_rsa4096.pem')
-
- input_dir = common.MakeTempDir()
- system_image = common.MakeTempFile()
- build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
- '/system', str(4096 * 100), '-j', '0', '-s']
- common.RunAndCheckOutput(build_image_cmd)
-
- add_footer_cmd = ['avbtool', 'add_hashtree_footer',
- '--partition_size', str(4096 * 150),
- '--partition_name', 'system',
- '--image', system_image]
- common.RunAndCheckOutput(add_footer_cmd)
-
- vbmeta_image = common.MakeTempFile()
- common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
- ['system'])
-
- verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
- vbmeta_image, '--transparency_log_pub_keys',
- common.OPTIONS.aftl_key_path]
- common.RunAndCheckOutput(verify_cmd)
-
@test_utils.SkipIfExternalToolsUnavailable()
def test_AppendGkiSigningArgs_NoSigningKeyPath(self):
# A non-GKI boot.img has no gki_signing_key_path.
diff --git a/tools/releasetools/test_merge_target_files.py b/tools/releasetools/test_merge_target_files.py
index 4f61472..835edab 100644
--- a/tools/releasetools/test_merge_target_files.py
+++ b/tools/releasetools/test_merge_target_files.py
@@ -265,10 +265,10 @@
'system': 'system',
'product': 'product',
'vendor': 'vendor',
- }, os.path.join(product_out_dir, 'policy'))
+ })
self.assertEqual(' '.join(cmd),
('secilc -m -M true -G -N -c 30 '
- '-o {OTP}/policy -f /dev/null '
+ '-o {OTP}/META/combined_sepolicy -f /dev/null '
'{OTP}/system/etc/selinux/plat_sepolicy.cil '
'{OTP}/system/etc/selinux/mapping/30.0.cil '
'{OTP}/vendor/etc/selinux/vendor_sepolicy.cil '
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index 661712a..11cfee1 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -24,7 +24,7 @@
import test_utils
from ota_utils import (
BuildLegacyOtaMetadata, CalculateRuntimeDevicesAndFingerprints,
- FinalizeMetadata, GetPackageMetadata, PropertyFiles)
+ ConstructOtaApexInfo, FinalizeMetadata, GetPackageMetadata, PropertyFiles)
from ota_from_target_files import (
_LoadOemDicts, AbOtaPropertyFiles,
GetTargetFilesZipForCustomImagesUpdates,
@@ -295,6 +295,35 @@
uncompressed_apex_size = os.path.getsize(original_apex_filepath)
self.assertEqual(apex_infos[0].decompressed_size, uncompressed_apex_size)
+ @staticmethod
+ def construct_tf_with_apex_info(infos):
+ apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
+ apex_metadata_proto.apex_info.extend(infos)
+
+ output = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(output, 'w') as zfp:
+ common.ZipWriteStr(zfp, "META/apex_info.pb",
+ apex_metadata_proto.SerializeToString())
+ return output
+
+ def test_ConstructOtaApexInfo_incremental_package(self):
+ infos = [ota_metadata_pb2.ApexInfo(package_name='com.android.apex.1',
+ version=1000, is_compressed=False),
+ ota_metadata_pb2.ApexInfo(package_name='com.android.apex.2',
+ version=2000, is_compressed=True)]
+ target_file = self.construct_tf_with_apex_info(infos)
+
+ with zipfile.ZipFile(target_file) as target_zip:
+ info_bytes = ConstructOtaApexInfo(target_zip, source_file=target_file)
+ apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
+ apex_metadata_proto.ParseFromString(info_bytes)
+
+ info_list = apex_metadata_proto.apex_info
+ self.assertEqual(2, len(info_list))
+ self.assertEqual('com.android.apex.1', info_list[0].package_name)
+ self.assertEqual(1000, info_list[0].version)
+ self.assertEqual(1000, info_list[0].source_version)
+
def test_GetPackageMetadata_retrofitDynamicPartitions(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
common.OPTIONS.retrofit_dynamic_partitions = True
@@ -834,6 +863,7 @@
property_files.required)
self.assertEqual(
(
+ 'apex_info.pb',
'care_map.pb',
'care_map.txt',
'compatibility.zip',
@@ -933,6 +963,7 @@
property_files.required)
self.assertEqual(
(
+ 'apex_info.pb',
'care_map.pb',
'care_map.txt',
'compatibility.zip',
diff --git a/tools/releasetools/test_ota_utils.py b/tools/releasetools/test_ota_utils.py
new file mode 100644
index 0000000..9a82e6f
--- /dev/null
+++ b/tools/releasetools/test_ota_utils.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import unittest
+import io
+import ota_utils
+import zipfile
+
+
+class TestZipEntryOffset(unittest.TestCase):
+ def test_extra_length_differ(self):
+ # This is a magic zip file such that:
+ # 1. It has 1 entry, `file.txt'`
+ # 2. The central directory entry for the entry contains an extra field of#
+ # length 24, while the local file header for the entry contains an extra#
+ # field of length 28.
+ # It is key that the entry contains extra field of different length.
+ # The sole purpose of this test case is make sure our offset computing
+ # logic works in this scenario.
+
+ # This is created by:
+ # touch file.txt
+ # zip -0 test.zip file.txt
+ # Above command may or may not work on all platforms.
+ # Some zip implementation will keep the extra field size consistent.
+ # Some don't
+ magic_zip = b'PK\x03\x04\n\x00\x00\x00\x00\x00nY\xfcR\x00\x00\x00\x00\x00\x00\x00' +\
+ b'\x00\x00\x00\x00\x00\x08\x00\x1c\x00file.txtUT\t\x00\x03' +\
+ b'\xa0s\x01a\xa0s\x01aux\x0b\x00\x01\x04\x88\xc4\t\x00\x04S_\x01\x00' +\
+ b'PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00nY\xfcR\x00\x00\x00\x00' +\
+ b'\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x18\x00\x00\x00\x00\x00' +\
+ b'\x00\x00\x00\x00\x80\x81\x00\x00\x00\x00file.txt' +\
+ b'UT\x05\x00\x03\xa0s\x01aux\x0b\x00\x01\x04\x88\xc4\t\x00\x04' +\
+ b'S_\x01\x00PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00N\x00\x00' +\
+ b'\x00B\x00\x00\x00\x00\x00'
+ # Just making sure we concatenated the bytes correctly
+ self.assertEqual(len(magic_zip), 166)
+ fp = io.BytesIO(magic_zip)
+ with zipfile.ZipFile(fp, 'r') as zfp:
+ self.assertGreater(len(zfp.infolist()), 0)
+ zinfo = zfp.getinfo("file.txt")
+ (offset, size) = ota_utils.GetZipEntryOffset(zfp, zinfo)
+ self.assertEqual(size, zinfo.file_size)
+ self.assertEqual(offset, zipfile.sizeFileHeader+len(zinfo.filename) + 28)
diff --git a/tools/releasetools/test_sign_apex.py b/tools/releasetools/test_sign_apex.py
index 646b04d..8470f20 100644
--- a/tools/releasetools/test_sign_apex.py
+++ b/tools/releasetools/test_sign_apex.py
@@ -69,5 +69,5 @@
payload_key,
container_key,
False,
- codename_to_api_level_map={'S': 31})
+ codename_to_api_level_map={'S': 31, 'Tiramisu' : 32})
self.assertTrue(os.path.exists(signed_apex))
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index ad9e657..92dca9a 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -328,23 +328,23 @@
'Apex3.apex' : 'key3',
}
apex_keys = {
- 'Apex1.apex' : ('payload-key1', 'container-key1'),
- 'Apex2.apex' : ('payload-key2', 'container-key2'),
+ 'Apex1.apex' : ('payload-key1', 'container-key1', None),
+ 'Apex2.apex' : ('payload-key2', 'container-key2', None),
}
with zipfile.ZipFile(input_file) as input_zip:
CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
# Fine to have both keys as PRESIGNED.
- apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED')
+ apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED', None)
CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
# Having only one of them as PRESIGNED is not allowed.
- apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED')
+ apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED', None)
self.assertRaises(
AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
None, apex_keys)
- apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1')
+ apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1', None)
self.assertRaises(
AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
None, apex_keys)
@@ -475,10 +475,10 @@
self.assertEqual({
'apex.apexd_test.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
'apex.apexd_test_different_app.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
}, keys_info)
def test_ReadApexKeysInfo_mismatchingContainerKeys(self):
@@ -514,10 +514,10 @@
self.assertEqual({
'apex.apexd_test.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
'apex.apexd_test_different_app.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
}, keys_info)
def test_ReadApexKeysInfo_missingPayloadPublicKey(self):
@@ -537,10 +537,10 @@
self.assertEqual({
'apex.apexd_test.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
'apex.apexd_test_different_app.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
}, keys_info)
def test_ReadApexKeysInfo_presignedKeys(self):
@@ -560,10 +560,10 @@
self.assertEqual({
'apex.apexd_test.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
'apex.apexd_test_different_app.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
}, keys_info)
def test_ReadApexKeysInfo_presignedKeys(self):
@@ -583,10 +583,10 @@
self.assertEqual({
'apex.apexd_test.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
'apex.apexd_test_different_app.apex': (
'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/make/target/product/security/testkey'),
+ 'build/make/target/product/security/testkey', None),
}, keys_info)
def test_ReplaceGkiSigningKey(self):
diff --git a/tools/releasetools/testdata/test_aftl_rsa4096.pem b/tools/releasetools/testdata/test_aftl_rsa4096.pem
deleted file mode 100644
index 89f1ef3..0000000
--- a/tools/releasetools/testdata/test_aftl_rsa4096.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDDlhUPUgtWL6LB
-Wybp6wsEJeioV1aRLPGSA2/xIpTiJUK46cb/MD5eBTWjKENoIgX23eL/ePy2I68e
-+WvcZ5ITGOTRQqNVZIdc5qvr03wkV0BsJQMHSMAHacePpB/4xM5MzN/6Ku1wA8Dw
-uK+v/Cw4hqq8H/gP0oPVQ1bwcIePzRPX4YkkyXusoyzTIm5DJ9reVtyFucKqANCN
-aFmGxcaEc2nADtARQWJpO95joFsMvr68+JBxpCt8aWbxuSz/rLJ9Y8Z46V/++XG+
-E4QEob/WVY5pUD/RyogLrfhIf+zO7R3wJklXElSFacIX9+RzR9dgkQVbqxLfBKIP
-XWLCsF4I4EnvqUtaVjIMl8UpZpoq8pDLRqZ71Os5xZYq06x9E02M6DnvFbZEdaOX
-MCz2mmNX3g5FahvJayBhCuNhyTkd79MFR71Wp48TvWxKz3S7q0T0cWHNhtPkHSCa
-KwD93AQnqtLKYDGkHIZBzJPcs+QxbzdHyGzhXZb+qh5KmQvNA9HRBQY1RkMmzIbI
-8pzYTwpOkbCEhVoCWcRaaF1Pgl+zcpgJOMbBBUabx/dConFIhMDW/I5fHgKgwGqm
-tWUibrMPdnfS6W5MXi8jC0eDuZl0VwmdE+4dLujiOofUYnb7D+GXojf3PrSLcTw1
-PmG0f7l5xDKN9a0N+IXqvD2oAANTsQIDAQABAoICAQCW5HXw8OogHvYg2HMIKrbA
-B4McRO1baWIhtRcq4PQeGIMGaA2HmS+0l65O5uRCNWWGlJ7pW+0TlCop6mHFk/4F
-T8JQk2mxmrI4ARqIAQwYeVwRUuioOP81eO1mK0gjQ6qpY7I0reOq9KpozQN18UYo
-gfS82Kkng9EDukUbkKV1UtFJTw3gXLVWdjlB1qFcnCXmPPs7DBpbz+8V+XiAWpsS
-WnwumP77IQeMiozDLdaw2YQMBHRjyDVocWTjfmpyAkleJZjcdagC7W1MKIBElomL
-EUyigTALaYZWBGy1ekQ3TIY5XUBdtZ2RpAsDNNOCAN3v+VI565zOhCOHWRO1gh24
-vyhBFR0HYqBRoLbLAqo8bM5iLPz1EWGyaTnfxt38J8Va0TD7KihcBnphiA+dkhEF
-oc0yIp/8S2o3CfkNok7Ju8Amb7M4JJuKhuP8wxn86fAHpjjd3Y4SlZp0NrTrd7T2
-msLIneb1OUZZxFxyJG1XQGEZplLPalnGadIF4p3q/3nd1rVb491qCNl/A5QwhI9r
-ZV62O90M9fu3+cAynBLbMT09IZecNwP1gXmunlY6YH+ymM+3NFqC8q2tnzomiz8/
-Fee0ftZ2C/jK62fET0Y8LPWGkVQGHtvZH0FPg4suA0GMmYAe0tQl93A+jFltfKKZ
-RgCDrYs6Wv76E9gnWVnEdQKCAQEA8L76LjZUTKOg83Bra+hP+cXnwGsgwOwJfGBp
-OM++5HzlpYjtbD38esBZVJtwb/8xJGdsHtP2n7ZgbSDuAnRj5S50QHIApvRkz1Y+
-1hL8tAdgVP2JkYjpyG3bPk4QVKyXkKvBcp2BCidXs75+HzfOxqkazumaYOYo2guh
-azHdka2xSqxcZqo4yyORc/oue25RU4skmuNDOlP0+OTxU/uXnl7QZmlaOfT5TqO4
-s7uER4BXt/87j44mnOBdXmtqrsL49+R9bzVskx76aeuaBbwf7jnpR058E71OZwSd
-F1P3fx6hl0yLOZF/5Jnq+14rEna6jH50XtzlhB6deSZFTOw2gwKCAQEAz/qXRzwH
-I0YWISgkUG2zBJseHmfHqV4CDzb5+tTJ3B2I8cXE0m2sQJXi2s7oMhWSc1cQOHCX
-txpgWaD59uBz2lcwnGRNp27TRXv8Wo+X0+O+lGWU2cO+j8AB2Vtb7F7rCySp0+Uu
-z+dBfoQ2zhKEQlkX0YldVILGzCL3QBHVvPC4iDlwkMRbcejDoh9NsBtHL8lG+MAw
-ZXbwJjhaJkhTXJFpJpejq70naS8VVlLt8Os80iuBXe5JK/ecAHtsNcJlXO02sMNZ
-Fbcy8WosGyvRKQ/tHtTjAlxZ7Ey8usWE8BvWBdUgiIBkIcjLtE2GrA8eOGNb3v1I
-HRt8NsV8yaLWuwKCAQAR7SaT6le8nTKO7gARuOq7npDzMwbtVqYeLM+o+08rlGFF
-QjzronH6cfg05J4quMXgABN8+CuVGO91MM6IQEJv/lWJtvN1ex1GkxV6u0812JbD
-vV1RCPDfi86XhRiSNYfTrfZponDJYMSXDcg2auFqyYzFe3+TV5ATLGqIoN3uyxA4
-jz0SJ/qypaNfD3IGnuBPaD0Bi4ql/TpwjhuqNUHE+SprdczSI/usb2SBfaUL7fKa
-MNcuiVc2tz48maMIAFypmMn+TewXyGa9HF4Lr0ZxZr6IIL/8eEwuP5my8v2q6Yz+
-xyRW1Q7A5vUoYoqyhUS+0Wu45JnyjJUNQFxIrg4hAoIBAF1uBIGSvN4iwRQ6FT4w
-WahrCre8BVzXh3NQTjJZXylL91YtcwLZE/Wbn+KN6o99U2IPLZE9O1qdNcVt5Hz8
-Te87FfJbuOrLhYuEbFQ+h4U/nUDK9XhyT+wB5JLBUOU5qrtByC0Rmtr411o/iONA
-PDwWC/YskEnDygywdIRKvsr3FN7VdvUB0Na2KxRsnZjMWElmUUS0Ccm7CZ0R2aWy
-/gfqpuMYYgVnnwnIhfxWmt+MvbDorGAHCMYAoQsyZuUrpB9/zP7RcvanavI6sP+v
-ynF43xvnpOdNl3Po8SuyScsXpijOmqPXkaP/sUsZPLOUww2vzPi6raetzjpIs4td
-ZLsCggEAe42Zj3FEbruJZeDgmd9lSc0j8UF90mNw8KH44IbuA6R9fGv3WkrNHEVd
-XZOwjWqAxhOj6pFoJk8n6h5d8iS/yXFZ0AfBMc21XMecu9mnfx9E9LFAIWmv7Wut
-vy3h2BqY+crglpg5RAw+3J97HAGMYCvp+hH2il+9zzjpmCtTD21LRMkw34szY7RR
-CDy9G5FTmKVlxw5eegvyj164olQRLurEdUIfSr5UnBjrWftJHy9JW8KWCeFDSmm9
-xCl3nGDyQuZmOTngxPtrOYAhb5LoKR9BeGcy6jlom7V4nYYqm3t1IDBgMqjYGT9c
-vqQgxO2OFsQOJQ/4PRYEKd1neTlZrw==
------END PRIVATE KEY-----
diff --git a/tools/releasetools/testdata/test_transparency_key.pub b/tools/releasetools/testdata/test_transparency_key.pub
deleted file mode 100644
index 8bfd816..0000000
--- a/tools/releasetools/testdata/test_transparency_key.pub
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4ilqCNsenNA013iCdwgD
-YPxZ853nbHG9lMBp9boXiwRcqT/8bUKHIL7YX5z7s+QoRYVY3rkMKppRabclXzyx
-H59YnPMaU4uv7NqwWzjgaZo7E+vo7IF+KBjV3cJulId5Av0yIYUCsrwd7MpGtWdC
-Q3S+7Vd4zwzCKEhcvliNIhnNlp1U3wNkPCxOyCAsMEn6k8O5ar12ke5TvxDv15db
-rPDeHh8G2OYWoCkWL+lSN35L2kOJqKqVbLKWrrOd96RCYrrtbPCi580OADJRcUlG
-lgcjwmNwmypBWvQMZ6ITj0P0ksHnl1zZz1DE2rXe1goLI1doghb5KxLaezlR8c2C
-E3w/uo9KJgNmNgUVzzqZZ6FE0moyIDNOpP7KtZAL0DvEZj6jqLbB0ccPQElrg52m
-Dv2/A3nYSr0mYBKeskT4+Bg7PGgoC8p7WyLSxMyzJEDYdtrj9OFx6eZaA23oqTQx
-k3Qq5H8RfNBeeSUEeKF7pKH/7gyqZ2bNzBFMA2EBZgBozwRfaeN/HCv3qbaCnwvu
-6caacmAsK+RxiYxSL1QsJqyhCWWGxVyenmxdc1KG/u5ypi7OIioztyzR3t2tAzD3
-Nb+2t8lgHBRxbV24yiPlnvPmB1ZYEctXnlRR9Evpl1o9xA9NnybPHKr9rozN39CZ
-V/USB8K6ao1y5xPZxa8CZksCAwEAAQ==
------END PUBLIC KEY-----
-
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 401857f..282dc99 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -36,7 +36,9 @@
import os.path
import re
import zipfile
+
from hashlib import sha1
+from common import IsSparseImage
import common
import rangelib
@@ -71,10 +73,16 @@
def CheckAllFiles(which):
logging.info('Checking %s image.', which)
- # Allow having shared blocks when loading the sparse image, because allowing
- # that doesn't affect the checks below (we will have all the blocks on file,
- # unless it's skipped due to the holes).
- image = common.GetSparseImage(which, input_tmp, input_zip, True)
+ path = os.path.join(input_tmp, "IMAGES", which + ".img")
+ if not IsSparseImage(path):
+ logging.info("%s is non-sparse image", which)
+ image = common.GetNonSparseImage(which, input_tmp)
+ else:
+ logging.info("%s is sparse image", which)
+ # Allow having shared blocks when loading the sparse image, because allowing
+ # that doesn't affect the checks below (we will have all the blocks on file,
+ # unless it's skipped due to the holes).
+ image = common.GetSparseImage(which, input_tmp, input_zip, True)
prefix = '/' + which
for entry in image.file_map:
# Skip entries like '__NONZERO-0'.
@@ -194,7 +202,8 @@
# Check we have the same recovery target in the check and flash commands.
assert check_partition == flash_partition, \
- "Mismatching targets: {} vs {}".format(check_partition, flash_partition)
+ "Mismatching targets: {} vs {}".format(
+ check_partition, flash_partition)
# Validate the SHA-1 of the recovery image.
recovery_sha1 = flash_partition.split(':')[3]
@@ -248,6 +257,39 @@
os.symlink(os.path.join(src, filename), os.path.join(dst, filename))
+def ValidatePartitionFingerprints(input_tmp, info_dict):
+ build_info = common.BuildInfo(info_dict)
+ if not build_info.avb_enabled:
+ logging.info("AVB not enabled, skipping partition fingerprint checks")
+ return
+ # Expected format:
+ # Prop: com.android.build.vendor.fingerprint -> 'generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7335886:userdebug/test-keys'
+ # Prop: com.android.build.vendor_boot.fingerprint -> 'generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7335886:userdebug/test-keys'
+ p = re.compile(
+ r"Prop: com.android.build.(?P<partition>\w+).fingerprint -> '(?P<fingerprint>[\w\/:\.-]+)'")
+ for vbmeta_partition in ["vbmeta", "vbmeta_system"]:
+ image = os.path.join(input_tmp, "IMAGES", vbmeta_partition + ".img")
+ if not os.path.exists(image):
+ assert vbmeta_partition != "vbmeta",\
+ "{} is a required partition for AVB.".format(
+ vbmeta_partition)
+ logging.info("vb partition %s not present, skipping", vbmeta_partition)
+ continue
+
+ output = common.RunAndCheckOutput(
+ [info_dict["avb_avbtool"], "info_image", "--image", image])
+ matches = p.findall(output)
+ for (partition, fingerprint) in matches:
+ actual_fingerprint = build_info.GetPartitionFingerprint(
+ partition)
+ if actual_fingerprint is None:
+ logging.warning(
+ "Failed to get fingerprint for partition %s", partition)
+ continue
+ assert fingerprint == actual_fingerprint, "Fingerprint mismatch for partition {}, expected: {} actual: {}".format(
+ partition, fingerprint, actual_fingerprint)
+
+
def ValidateVerifiedBootImages(input_tmp, info_dict, options):
"""Validates the Verified Boot related images.
@@ -273,7 +315,7 @@
# longer copied from RADIO to the IMAGES folder. But avbtool assumes that
# images are in IMAGES folder. So we symlink them.
symlinkIfNotExists(os.path.join(input_tmp, "RADIO"),
- os.path.join(input_tmp, "IMAGES"))
+ os.path.join(input_tmp, "IMAGES"))
# Verified boot 1.0 (images signed with boot_signer and verity_signer).
if info_dict.get('boot_signer') == 'true':
logging.info('Verifying Verified Boot images...')
@@ -325,11 +367,12 @@
if info_dict.get("system_root_image") != "true":
verity_key_ramdisk = os.path.join(
input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
- assert os.path.exists(verity_key_ramdisk), 'Missing verity_key in ramdisk'
+ assert os.path.exists(
+ verity_key_ramdisk), 'Missing verity_key in ramdisk'
assert filecmp.cmp(
verity_key_mincrypt, verity_key_ramdisk, shallow=False), \
- 'Mismatching verity_key files in root and ramdisk'
+ 'Mismatching verity_key files in root and ramdisk'
logging.info('Verified the content of /verity_key in ramdisk')
# Then verify the verity signed system/vendor/product images, against the
@@ -362,6 +405,8 @@
if key is None:
key = info_dict['avb_vbmeta_key_path']
+ ValidatePartitionFingerprints(input_tmp, info_dict)
+
# avbtool verifies all the images that have descriptors listed in vbmeta.
# Using `--follow_chain_partitions` so it would additionally verify chained
# vbmeta partitions (e.g. vbmeta_system).
@@ -411,7 +456,7 @@
# avbtool verifies recovery image for non-A/B devices.
if (info_dict.get('ab_update') != 'true' and
- info_dict.get('no_recovery') != 'true'):
+ info_dict.get('no_recovery') != 'true'):
image = os.path.join(input_tmp, 'IMAGES', 'recovery.img')
key = info_dict['avb_recovery_key_path']
cmd = [info_dict['avb_avbtool'], 'verify_image', '--image', image,
@@ -427,21 +472,21 @@
def CheckDataInconsistency(lines):
- build_prop = {}
- for line in lines:
- if line.startswith("import") or line.startswith("#"):
- continue
- if "=" not in line:
- continue
+ build_prop = {}
+ for line in lines:
+ if line.startswith("import") or line.startswith("#"):
+ continue
+ if "=" not in line:
+ continue
- key, value = line.rstrip().split("=", 1)
- if key in build_prop:
- logging.info("Duplicated key found for {}".format(key))
- if value != build_prop[key]:
- logging.error("Key {} is defined twice with different values {} vs {}"
- .format(key, value, build_prop[key]))
- return key
- build_prop[key] = value
+ key, value = line.rstrip().split("=", 1)
+ if key in build_prop:
+ logging.info("Duplicated key found for {}".format(key))
+ if value != build_prop[key]:
+ logging.error("Key {} is defined twice with different values {} vs {}"
+ .format(key, value, build_prop[key]))
+ return key
+ build_prop[key] = value
def CheckBuildPropDuplicity(input_tmp):
diff --git a/tools/signapk/OWNERS b/tools/signapk/OWNERS
index 0b8d398..23cab0b 100644
--- a/tools/signapk/OWNERS
+++ b/tools/signapk/OWNERS
@@ -1,2 +1,2 @@
cbrubaker@google.com
-klyubin@google.com
+mpgroover@google.com
diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java
index 7e5c8fc..232e119 100644
--- a/tools/signapk/src/com/android/signapk/SignApk.java
+++ b/tools/signapk/src/com/android/signapk/SignApk.java
@@ -64,12 +64,19 @@
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStore.PrivateKeyEntry;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
+import java.security.UnrecoverableEntryException;
+import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
@@ -286,6 +293,32 @@
}
}
+ private static KeyStore createKeyStore(String keyStoreName, String keyStorePin) throws
+ CertificateException,
+ IOException,
+ KeyStoreException,
+ NoSuchAlgorithmException {
+ KeyStore keyStore = KeyStore.getInstance(keyStoreName);
+ keyStore.load(null, keyStorePin == null ? null : keyStorePin.toCharArray());
+ return keyStore;
+ }
+
+ /** Get a PKCS#11 private key from keyStore */
+ private static PrivateKey loadPrivateKeyFromKeyStore(
+ final KeyStore keyStore, final String keyName, final String password)
+ throws CertificateException, KeyStoreException, NoSuchAlgorithmException,
+ UnrecoverableKeyException, UnrecoverableEntryException {
+ final Key key = keyStore.getKey(keyName, password == null ? null : password.toCharArray());
+ final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(keyName, null);
+ if (privateKeyEntry == null) {
+ throw new Error(
+ "Key "
+ + keyName
+ + " not found in the token provided by PKCS11 library!");
+ }
+ return privateKeyEntry.getPrivateKey();
+ }
+
/**
* Add a copy of the public key to the archive; this should
* exactly match one of the files in
@@ -1020,7 +1053,10 @@
private static void usage() {
System.err.println("Usage: signapk [-w] " +
"[-a <alignment>] " +
+ "[--align-file-size] " +
"[-providerClass <className>] " +
+ "[-loadPrivateKeysFromKeyStore <keyStoreName>]" +
+ "[-keyStorePin <pin>]" +
"[--min-sdk-version <n>] " +
"[--disable-v2] " +
"[--enable-v4] " +
@@ -1043,7 +1079,10 @@
boolean signWholeFile = false;
String providerClass = null;
+ String keyStoreName = null;
+ String keyStorePin = null;
int alignment = 4;
+ boolean alignFileSize = false;
Integer minSdkVersionOverride = null;
boolean signUsingApkSignatureSchemeV2 = true;
boolean signUsingApkSignatureSchemeV4 = false;
@@ -1060,9 +1099,24 @@
}
providerClass = args[++argstart];
++argstart;
+ } else if ("-loadPrivateKeysFromKeyStore".equals(args[argstart])) {
+ if (argstart + 1 >= args.length) {
+ usage();
+ }
+ keyStoreName = args[++argstart];
+ ++argstart;
+ } else if ("-keyStorePin".equals(args[argstart])) {
+ if (argstart + 1 >= args.length) {
+ usage();
+ }
+ keyStorePin = args[++argstart];
+ ++argstart;
} else if ("-a".equals(args[argstart])) {
alignment = Integer.parseInt(args[++argstart]);
++argstart;
+ } else if ("--align-file-size".equals(args[argstart])) {
+ alignFileSize = true;
+ ++argstart;
} else if ("--min-sdk-version".equals(args[argstart])) {
String minSdkVersionString = args[++argstart];
try {
@@ -1137,11 +1191,21 @@
// timestamp using the current timezone. We thus adjust the milliseconds since epoch
// value to end up with MS-DOS timestamp of Jan 1 2009 00:00:00.
timestamp -= TimeZone.getDefault().getOffset(timestamp);
-
+ KeyStore keyStore = null;
+ if (keyStoreName != null) {
+ keyStore = createKeyStore(keyStoreName, keyStorePin);
+ }
PrivateKey[] privateKey = new PrivateKey[numKeys];
for (int i = 0; i < numKeys; ++i) {
int argNum = argstart + i*2 + 1;
- privateKey[i] = readPrivateKey(new File(args[argNum]));
+ if (keyStore == null) {
+ privateKey[i] = readPrivateKey(new File(args[argNum]));
+ } else {
+ String[] splits = args[argNum].split(":", 2);
+ final String keyAlias = splits[0];
+ final String password = splits.length > 1 ? splits[1] : null;
+ privateKey[i] = loadPrivateKeyFromKeyStore(keyStore, keyAlias, password);
+ }
}
inputJar = new JarFile(new File(inputFilename), false); // Don't verify.
@@ -1206,12 +1270,22 @@
ByteBuffer[] outputChunks = new ByteBuffer[] {v1SignedApk};
ZipSections zipSections = findMainZipSections(v1SignedApk);
- ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest =
- apkSigner.outputZipSections2(
- DataSources.asDataSource(zipSections.beforeCentralDir),
- DataSources.asDataSource(zipSections.centralDir),
- DataSources.asDataSource(zipSections.eocd));
- if (addV2SignatureRequest != null) {
+
+ ByteBuffer eocd = ByteBuffer.allocate(zipSections.eocd.remaining());
+ eocd.put(zipSections.eocd);
+ eocd.flip();
+ eocd.order(ByteOrder.LITTLE_ENDIAN);
+ // This loop is supposed to be iterated twice at most.
+ // The second pass is to align the file size after amending EOCD comments
+ // with assumption that re-generated signing block would be the same size.
+ while (true) {
+ ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest =
+ apkSigner.outputZipSections2(
+ DataSources.asDataSource(zipSections.beforeCentralDir),
+ DataSources.asDataSource(zipSections.centralDir),
+ DataSources.asDataSource(eocd));
+ if (addV2SignatureRequest == null) break;
+
// Need to insert the returned APK Signing Block before ZIP Central
// Directory.
int padding = addV2SignatureRequest.getPaddingSizeBeforeApkSigningBlock();
@@ -1219,8 +1293,8 @@
// Because the APK Signing Block is inserted before the Central Directory,
// we need to adjust accordingly the offset of Central Directory inside the
// ZIP End of Central Directory (EoCD) record.
- ByteBuffer modifiedEocd = ByteBuffer.allocate(zipSections.eocd.remaining());
- modifiedEocd.put(zipSections.eocd);
+ ByteBuffer modifiedEocd = ByteBuffer.allocate(eocd.remaining());
+ modifiedEocd.put(eocd);
modifiedEocd.flip();
modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
ApkUtils.setZipEocdCentralDirectoryOffset(
@@ -1235,6 +1309,32 @@
zipSections.centralDir,
modifiedEocd};
addV2SignatureRequest.done();
+
+ // Exit the loop if we don't need to align the file size
+ if (!alignFileSize || alignment < 2) {
+ break;
+ }
+
+ // Calculate the file size
+ eocd = modifiedEocd;
+ int fileSize = 0;
+ for (ByteBuffer buf : outputChunks) {
+ fileSize += buf.remaining();
+ }
+ // Exit the loop because the file size is aligned.
+ if (fileSize % alignment == 0) {
+ break;
+ }
+ // Pad EOCD comment to align the file size.
+ int commentLen = alignment - fileSize % alignment;
+ modifiedEocd = ByteBuffer.allocate(eocd.remaining() + commentLen);
+ modifiedEocd.put(eocd);
+ modifiedEocd.rewind();
+ modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
+ ApkUtils.updateZipEocdCommentLen(modifiedEocd);
+ // Since V2 signing block should cover modified EOCD,
+ // re-iterate the loop with modified EOCD.
+ eocd = modifiedEocd;
}
// This assumes outputChunks are array-backed. To avoid this assumption, the
diff --git a/tools/warn/cpp_warn_patterns.py b/tools/warn/cpp_warn_patterns.py
index 2fa9916..1e1aa43 100644
--- a/tools/warn/cpp_warn_patterns.py
+++ b/tools/warn/cpp_warn_patterns.py
@@ -91,6 +91,10 @@
[r".*: warning: incompatible redeclaration of library function .+"]),
high('Null passed as non-null argument',
[r".*: warning: Null passed to a callee that requires a non-null"]),
+ medium('Unused command line argument',
+ [r".*: warning: argument unused during compilation: .+"]),
+ medium('Set but not used',
+ [r".*: warning: .* set but not used.*Wunused-but-set"]),
medium('Unused parameter',
[r".*: warning: unused parameter '.*'"]),
medium('Unused function, variable, label, comparison, etc.',
@@ -166,6 +170,8 @@
[r".*: warning: '.+' declared with greater visibility than the type of its field '.+'"]),
medium('Shift count greater than width of type',
[r".*: warning: (left|right) shift count >= width of type"]),
+ medium('Shift operator precedence',
+ [r".*: warning: operator .* has lower precedence .+Wshift-op-parentheses.+"]),
medium('extern <foo> is initialized',
[r".*: warning: '.+' initialized and declared 'extern'",
r".*: warning: 'extern' variable has an initializer"]),
@@ -239,6 +245,8 @@
[r".*: warning: ignoring #pragma .+"]),
medium('Pragma warning messages',
[r".*: warning: .+W#pragma-messages"]),
+ medium('Pragma once in main file',
+ [r".*: warning: #pragma once in main file .+Wpragma-once-outside-header.*"]),
medium('Variable might be clobbered by longjmp or vfork',
[r".*: warning: variable '.+' might be clobbered by 'longjmp' or 'vfork'"]),
medium('Argument might be clobbered by longjmp or vfork',
@@ -289,6 +297,7 @@
[r".*: warning: declaration 'class .+' does not declare anything"]),
medium('Initialization order will be different',
[r".*: warning: '.+' will be initialized after",
+ r".*: warning: initializer order does not match the declaration order",
r".*: warning: field .+ will be initialized after .+Wreorder"]),
skip('skip, ....',
[r".*: warning: '.+'"]),
@@ -333,7 +342,7 @@
low('Deprecated register',
[r".*: warning: 'register' storage class specifier is deprecated"]),
low('Converts between pointers to integer types with different sign',
- [r".*: warning: .+ converts between pointers to integer types with different sign"]),
+ [r".*: warning: .+ converts between pointers to integer types .+Wpointer-sign\]"]),
harmless('Extra tokens after #endif',
[r".*: warning: extra tokens at end of #endif directive"]),
medium('Comparison between different enums',
@@ -410,10 +419,37 @@
[r".*: warning: missing .+Winvalid-pp-token"]),
low('need glibc to link',
[r".*: warning: .* requires at runtime .* glibc .* for linking"]),
+ low('Add braces to avoid dangling else',
+ [r".*: warning: add explicit braces to avoid dangling else"]),
+ low('Assigning value to self',
+ [r".*: warning: explicitly assigning value of .+ to itself"]),
+ low('Comparison of integers of different signs',
+ [r".*: warning: comparison of integers of different signs.+sign-compare"]),
+ low('Incompatible pointer types',
+ [r".*: warning: incompatible .*pointer types .*-Wincompatible-.*pointer-types"]),
+ low('Missing braces',
+ [r".*: warning: suggest braces around initialization of",
+ r".*: warning: too many braces around scalar initializer .+Wmany-braces-around-scalar-init",
+ r".*: warning: braces around scalar initializer"]),
+ low('Missing field initializers',
+ [r".*: warning: missing field '.+' initializer"]),
+ low('Typedef redefinition',
+ [r".*: warning: redefinition of typedef '.+' is a C11 feature"]),
+ low('GNU old-style field designator',
+ [r".*: warning: use of GNU old-style field designator extension"]),
+ low('Initializer overrides prior initialization',
+ [r".*: warning: initializer overrides prior initialization of this subobject"]),
+ low('GNU extension, variable sized type not at end',
+ [r".*: warning: field '.+' with variable sized type '.+' not at the end of a struct or class"]),
+ low('Comparison of constant is always false/true',
+ [r".*: comparison of .+ is always .+Wtautological-constant-out-of-range-compare"]),
+ low('Hides overloaded virtual function',
+ [r".*: '.+' hides overloaded virtual function"]),
medium('Operator new returns NULL',
[r".*: warning: 'operator new' must not return NULL unless it is declared 'throw\(\)' .+"]),
medium('NULL used in arithmetic',
[r".*: warning: NULL used in arithmetic",
+ r".*: warning: .* subtraction with a null pointer",
r".*: warning: comparison between NULL and non-pointer"]),
medium('Misspelled header guard',
[r".*: warning: '.+' is used as a header guard .+ followed by .+ different macro"]),
diff --git a/tools/warn/html_writer.py b/tools/warn/html_writer.py
index ac5d4b7..ef173bc 100644
--- a/tools/warn/html_writer.py
+++ b/tools/warn/html_writer.py
@@ -328,7 +328,8 @@
cur_row_class = 1 - cur_row_class
# remove last '\n'
out_text = text[:-1] if text[-1] == '\n' else text
- writer('<tr><td class="c' + str(cur_row_class) + '">' + out_text + '</td></tr>')
+ writer('<tr><td class="c' + str(cur_row_class) + '">'
+ + out_text + '</td></tr>')
writer('</table></div>')
writer('</blockquote>')
@@ -355,7 +356,8 @@
sort_warnings(warn_patterns)
total = 0
for severity in Severity.levels:
- total += write_severity(csvwriter, severity, severity.column_header, warn_patterns)
+ total += write_severity(
+ csvwriter, severity, severity.column_header, warn_patterns)
csvwriter.writerow([total, '', 'All warnings'])
diff --git a/tools/warn/java_warn_patterns.py b/tools/warn/java_warn_patterns.py
index 534f48d..3f5da9d 100644
--- a/tools/warn/java_warn_patterns.py
+++ b/tools/warn/java_warn_patterns.py
@@ -74,6 +74,8 @@
[r'.*\.class\): warning: Cannot find annotation method .+ in']),
java_medium('No class/method in SDK ...',
[r'.*\.java:.*: warning: No such (class|method) .* for SDK']),
+ java_medium('Unknown enum constant',
+ [r'unknown_source_file: warning: unknown enum constant .+']),
# Warnings generated by Error Prone
java_medium('Non-ascii characters used, but ascii encoding specified',
[r".*: warning: unmappable character for encoding ascii"]),
@@ -207,6 +209,8 @@
'Logging or rethrowing exceptions should usually be preferred to catching and calling printStackTrace'),
medium('CatchFail',
'Ignoring exceptions and calling fail() is unnecessary, and makes test output less useful'),
+ medium('ChangedAbstract',
+ 'Method has changed \'abstract\' qualifier'),
medium('ClassCanBeStatic',
'Inner class is non-static but does not reference enclosing class'),
medium('ClassNewInstance',
@@ -355,6 +359,8 @@
'equals method doesn\'t override Object.equals'),
medium('NotCloseable',
'Not closeable'),
+ medium('NullableCollection',
+ 'Method should not return a nullable collection'),
medium('NullableConstructor',
'Constructors should not be annotated with @Nullable since they cannot return null'),
medium('NullableDereference',
@@ -801,6 +807,8 @@
[r".*: warning: \[path\] bad path element .*\.jar"]),
java_medium('Supported version from annotation processor',
[r".*: warning: Supported source version .+ from annotation processor"]),
+ java_medium('Schema export directory is not provided',
+ [r".*\.(java|kt):.*: warning: Schema export directory is not provided"]),
]
compile_patterns(warn_patterns)
diff --git a/tools/warn/make_warn_patterns.py b/tools/warn/make_warn_patterns.py
index a54c502..11ad5cc 100644
--- a/tools/warn/make_warn_patterns.py
+++ b/tools/warn/make_warn_patterns.py
@@ -35,6 +35,9 @@
{'category': 'make', 'severity': Severity.HIGH,
'description': 'System module linking to a vendor module',
'patterns': [r".*: warning: .+ \(.+\) should not link to .+ \(partition:.+\)"]},
+ {'category': 'make', 'severity': Severity.HIGH,
+ 'description': 'make: lstat file does not exist',
+ 'patterns': [r".*: warning: lstat .+: file does not exist"]},
{'category': 'make', 'severity': Severity.MEDIUM,
'description': 'Invalid SDK/NDK linking',
'patterns': [r".*: warning: .+ \(.+\) should not link to .+ \(.+\)"]},
@@ -56,6 +59,9 @@
{'category': 'make', 'severity': Severity.MEDIUM,
'description': 'make: deprecated macros',
'patterns': [r".*\.mk:.* warning:.* [A-Z_]+ (is|has been) deprecated."]},
+ {'category': 'make', 'severity': Severity.MEDIUM,
+ 'description': 'make: other Android.mk warnings',
+ 'patterns': [r".*/Android.mk:.*: warning: .+"]},
]
diff --git a/tools/warn/other_warn_patterns.py b/tools/warn/other_warn_patterns.py
index d05c8e9..c95528c 100644
--- a/tools/warn/other_warn_patterns.py
+++ b/tools/warn/other_warn_patterns.py
@@ -75,37 +75,15 @@
# misc warnings
misc('Duplicate logtag',
[r".*: warning: tag \".+\" \(.+\) duplicated in .+"]),
- misc('Typedef redefinition',
- [r".*: warning: redefinition of typedef '.+' is a C11 feature"]),
- misc('GNU old-style field designator',
- [r".*: warning: use of GNU old-style field designator extension"]),
- misc('Missing field initializers',
- [r".*: warning: missing field '.+' initializer"]),
- misc('Missing braces',
- [r".*: warning: suggest braces around initialization of",
- r".*: warning: too many braces around scalar initializer .+Wmany-braces-around-scalar-init",
- r".*: warning: braces around scalar initializer"]),
- misc('Comparison of integers of different signs',
- [r".*: warning: comparison of integers of different signs.+sign-compare"]),
- misc('Add braces to avoid dangling else',
- [r".*: warning: add explicit braces to avoid dangling else"]),
- misc('Initializer overrides prior initialization',
- [r".*: warning: initializer overrides prior initialization of this subobject"]),
- misc('Assigning value to self',
- [r".*: warning: explicitly assigning value of .+ to itself"]),
- misc('GNU extension, variable sized type not at end',
- [r".*: warning: field '.+' with variable sized type '.+' not at the end of a struct or class"]),
- misc('Comparison of constant is always false/true',
- [r".*: comparison of .+ is always .+Wtautological-constant-out-of-range-compare"]),
- misc('Hides overloaded virtual function',
- [r".*: '.+' hides overloaded virtual function"]),
- misc('Incompatible pointer types',
- [r".*: warning: incompatible .*pointer types .*-Wincompatible-.*pointer-types"]),
# Assembler warnings
asm('ASM value size does not match register size',
[r".*: warning: value size does not match register size specified by the constraint and modifier"]),
asm('IT instruction is deprecated',
[r".*: warning: applying IT instruction .* is deprecated"]),
+ asm('section flags ignored',
+ [r".*: warning: section flags ignored on section redeclaration"]),
+ asm('setjmp/longjmp/vfork changed binding',
+ [r".*: warning: .*(setjmp|longjmp|vfork) changed binding to .*"]),
# NDK warnings
{'category': 'NDK', 'severity': Severity.HIGH,
'description': 'NDK: Generate guard with empty availability, obsoleted',
@@ -168,6 +146,9 @@
{'category': 'RenderScript', 'severity': Severity.LOW,
'description': 'RenderScript warnings',
'patterns': [r'.*\.rscript:.*: warning: ']},
+ {'category': 'RenderScript', 'severity': Severity.HIGH,
+ 'description': 'RenderScript is deprecated',
+ 'patterns': [r'.*: warning: Renderscript is deprecated:.+']},
# Broken/partial warning messages will be skipped.
{'category': 'Misc', 'severity': Severity.SKIP,
'description': 'skip, ,',
diff --git a/tools/warn/tidy_warn_patterns.py b/tools/warn/tidy_warn_patterns.py
index 7018d10..a5842ea 100644
--- a/tools/warn/tidy_warn_patterns.py
+++ b/tools/warn/tidy_warn_patterns.py
@@ -23,15 +23,19 @@
from .severity import Severity
-def tidy_warn_pattern(description, pattern):
+def tidy_warn(description, patterns):
return {
'category': 'C/C++',
'severity': Severity.TIDY,
'description': 'clang-tidy ' + description,
- 'patterns': [r'.*: .+\[' + pattern + r'\]$']
+ 'patterns': patterns,
}
+def tidy_warn_pattern(description, pattern):
+ return tidy_warn(description, [r'.*: .+\[' + pattern + r'\]$'])
+
+
def simple_tidy_warn_pattern(description):
return tidy_warn_pattern(description, description)
@@ -77,6 +81,7 @@
warn_patterns = [
# pylint does not recognize g-inconsistent-quotes
# pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes
+ group_tidy_warn_pattern('altera'),
group_tidy_warn_pattern('android'),
simple_tidy_warn_pattern('abseil-string-find-startswith'),
simple_tidy_warn_pattern('bugprone-argument-comment'),
@@ -123,8 +128,9 @@
simple_tidy_warn_pattern('cert-oop54-cpp'),
group_tidy_warn_pattern('cert'),
group_tidy_warn_pattern('clang-diagnostic'),
+ group_tidy_warn_pattern('concurrency'),
group_tidy_warn_pattern('cppcoreguidelines'),
- group_tidy_warn_pattern('llvm'),
+ group_tidy_warn_pattern('fuchsia'),
simple_tidy_warn_pattern('google-default-arguments'),
simple_tidy_warn_pattern('google-runtime-int'),
simple_tidy_warn_pattern('google-runtime-operator'),
@@ -148,8 +154,10 @@
simple_tidy_warn_pattern('hicpp-noexcept-move'),
simple_tidy_warn_pattern('hicpp-use-override'),
group_tidy_warn_pattern('hicpp'),
- group_tidy_warn_pattern('modernize'),
+ group_tidy_warn_pattern('llvm'),
+ group_tidy_warn_pattern('llvmlibc'),
group_tidy_warn_pattern('misc'),
+ group_tidy_warn_pattern('modernize'),
simple_tidy_warn_pattern('performance-faster-string-find'),
simple_tidy_warn_pattern('performance-for-range-copy'),
simple_tidy_warn_pattern('performance-implicit-cast-in-loop'),
@@ -168,6 +176,9 @@
simple_tidy_warn_pattern('portability-simd-intrinsics'),
group_tidy_warn_pattern('portability'),
+ tidy_warn('TIMEOUT', [r".*: warning: clang-tidy aborted "]),
+ tidy_warn('Long Runs', [r".*: warning: clang-tidy used "]),
+
# warnings from clang-tidy's clang-analyzer checks
analyzer_high('clang-analyzer-core, null pointer',
[r".*: warning: .+ pointer is null .*\[clang-analyzer-core"]),
diff --git a/tools/warn/warn_common.py b/tools/warn/warn_common.py
index 844f629..61c8676 100755
--- a/tools/warn/warn_common.py
+++ b/tools/warn/warn_common.py
@@ -236,7 +236,11 @@
warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
count = 0
for line in buildlog:
- if warning_pattern.match(line):
+ # We want to find android_root of a local build machine.
+ # Do not use RBE warning lines, which has '/b/f/w/' path prefix.
+ # Do not use /tmp/ file warnings.
+ if warning_pattern.match(line) and (
+ '/b/f/w' not in line and not line.startswith('/tmp/')):
warning_lines.append(line)
count += 1
if count > 9999:
@@ -300,6 +304,7 @@
architecture = 'unknown'
# only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
+ # Bug: http://198657613, This might need change to handle RBE output.
chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
warning_pattern = re.compile(chrome_warning_pattern)
@@ -347,6 +352,8 @@
platform_version = 'unknown'
target_product = 'unknown'
target_variant = 'unknown'
+ build_id = 'unknown'
+ use_rbe = False
android_root = find_android_root(infile)
infile.seek(0)
@@ -363,6 +370,14 @@
warning_without_file = re.compile('^warning: .*')
rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
+ # If RBE was used, try to reclaim some warning lines mixed with some
+ # leading chars from other concurrent job's stderr output .
+ # The leading characters can be any character, including digits and spaces.
+ # It's impossible to correctly identify the starting point of the source
+ # file path without the file directory name knowledge.
+ # Here we can only be sure to recover lines containing "/b/f/w/".
+ rbe_warning_pattern = re.compile('.*/b/f/w/[^ ]*: warning: .*')
+
# Collect all unique warning lines
# Remove the duplicated warnings save ~8% of time when parsing
# one typical build log than before
@@ -384,6 +399,12 @@
prev_warning, flags, android_root, unique_warnings)
prev_warning = ''
+ if use_rbe and rbe_warning_pattern.match(line):
+ cleaned_up_line = re.sub('.*/b/f/w/', '', line)
+ unique_warnings = add_normalized_line_to_warnings(
+ cleaned_up_line, flags, android_root, unique_warnings)
+ continue
+
if warning_pattern.match(line):
if warning_without_file.match(line):
# save this line and combine it with the next line
@@ -399,15 +420,26 @@
result = re.search('(?<=^PLATFORM_VERSION=).*', line)
if result is not None:
platform_version = result.group(0)
+ continue
result = re.search('(?<=^TARGET_PRODUCT=).*', line)
if result is not None:
target_product = result.group(0)
+ continue
result = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
if result is not None:
target_variant = result.group(0)
+ continue
+ result = re.search('(?<=^BUILD_ID=).*', line)
+ if result is not None:
+ build_id = result.group(0)
+ continue
result = re.search('(?<=^TOP=).*', line)
if result is not None:
android_root = result.group(1)
+ continue
+ if re.search('USE_RBE=', line) is not None:
+ use_rbe = True
+ continue
if android_root:
new_unique_warnings = dict()
@@ -418,8 +450,8 @@
warning_line, flags, android_root)
unique_warnings = new_unique_warnings
- header_str = '%s - %s - %s' % (platform_version, target_product,
- target_variant)
+ header_str = '%s - %s - %s (%s)' % (
+ platform_version, target_product, target_variant, build_id)
return unique_warnings, header_str
diff --git a/tools/zipalign/ZipAlignMain.cpp b/tools/zipalign/ZipAlignMain.cpp
index 47ebd12..53fc8d4 100644
--- a/tools/zipalign/ZipAlignMain.cpp
+++ b/tools/zipalign/ZipAlignMain.cpp
@@ -20,6 +20,7 @@
#include "ZipAlign.h"
+#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
@@ -60,69 +61,53 @@
int alignment;
char* endp;
- if (argc < 4) {
- wantUsage = true;
- goto bail;
- }
-
- argc--;
- argv++;
-
- while (argc && argv[0][0] == '-') {
- const char* cp = argv[0] +1;
-
- while (*cp != '\0') {
- switch (*cp) {
- case 'c':
- check = true;
- break;
- case 'f':
- force = true;
- break;
- case 'v':
- verbose = true;
- break;
- case 'z':
- zopfli = true;
- break;
- case 'p':
- pageAlignSharedLibs = true;
- break;
- default:
- fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
- wantUsage = true;
- goto bail;
- }
-
- cp++;
+ int opt;
+ while ((opt = getopt(argc, argv, "fcpvz")) != -1) {
+ switch (opt) {
+ case 'c':
+ check = true;
+ break;
+ case 'f':
+ force = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'z':
+ zopfli = true;
+ break;
+ case 'p':
+ pageAlignSharedLibs = true;
+ break;
+ default:
+ fprintf(stderr, "ERROR: unknown flag -%c\n", opt);
+ wantUsage = true;
+ goto bail;
}
-
- argc--;
- argv++;
}
- if (!((check && argc == 2) || (!check && argc == 3))) {
+ if (!((check && (argc - optind) == 2) || (!check && (argc - optind) == 3))) {
wantUsage = true;
goto bail;
}
- alignment = strtol(argv[0], &endp, 10);
+ alignment = strtol(argv[optind], &endp, 10);
if (*endp != '\0' || alignment <= 0) {
- fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
+ fprintf(stderr, "Invalid value for alignment: %s\n", argv[optind]);
wantUsage = true;
goto bail;
}
if (check) {
/* check existing archive for correct alignment */
- result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
+ result = verify(argv[optind + 1], alignment, verbose, pageAlignSharedLibs);
} else {
/* create the new archive */
- result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
+ result = process(argv[optind + 1], argv[optind + 2], alignment, force, zopfli, pageAlignSharedLibs);
/* trust, but verify */
if (result == 0) {
- result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
+ result = verify(argv[optind + 2], alignment, verbose, pageAlignSharedLibs);
}
}
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
index 5233f0a..fcad96c 100644
--- a/tools/zipalign/ZipEntry.cpp
+++ b/tools/zipalign/ZipEntry.cpp
@@ -87,7 +87,7 @@
}
/*
- * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
+ * Check the LFH. Note that this will fail if the "kUsesDataDescr"
* flag is set, because the LFH is incomplete. (Not a problem, since we
* prefer the CDE values.)
*/
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
index 1e3c413..f2f65a6 100644
--- a/tools/zipalign/ZipFile.cpp
+++ b/tools/zipalign/ZipFile.cpp
@@ -530,7 +530,7 @@
// If the alignment is not what was requested, add some padding in the extra
// so the payload ends up where is requested.
uint64_t alignDiff = alignTo - (expectedPayloadOffset % alignTo);
- if (alignDiff == 0)
+ if (alignDiff == alignTo)
return OK;
return pEntry->addPadding(alignDiff);
@@ -654,7 +654,7 @@
{
ZipEntry* pEntry = NULL;
status_t result;
- long lfhPosn, startPosn, endPosn, uncompressedLen;
+ long lfhPosn, uncompressedLen;
if (mReadOnly)
return INVALID_OPERATION;
@@ -690,7 +690,6 @@
*/
lfhPosn = ftell(mZipFp);
pEntry->mLFH.write(mZipFp);
- startPosn = ftell(mZipFp);
/*
* Copy the data over.
@@ -741,18 +740,13 @@
}
/*
- * Update file offsets.
- */
- endPosn = ftell(mZipFp);
-
- /*
* Success! Fill out new values.
*/
pEntry->setLFHOffset(lfhPosn);
mEOCD.mNumEntries++;
mEOCD.mTotalNumEntries++;
mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
+ mEOCD.mCentralDirOffset = ftell(mZipFp);
/*
* Go back and write the LFH.
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
index c79e791..96d4f73 100644
--- a/tools/zipalign/tests/src/align_test.cpp
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -9,6 +9,7 @@
#include <android-base/file.h>
using namespace android;
+using namespace base;
static std::string GetTestPath(const std::string& filename) {
static std::string test_data_dir = android::base::GetExecutableDirectory() + "/tests/data/";
@@ -26,6 +27,34 @@
ASSERT_EQ(0, verified);
}
+TEST(Align, DoubleAligment) {
+ const std::string src = GetTestPath("unaligned.zip");
+ const std::string tmp = GetTestPath("da_aligned.zip");
+ const std::string dst = GetTestPath("da_d_aligner.zip");
+
+ int processed = process(src.c_str(), tmp.c_str(), 4, true, false, 4096);
+ ASSERT_EQ(0, processed);
+
+ int verified = verify(tmp.c_str(), 4, true, false);
+ ASSERT_EQ(0, verified);
+
+ // Align the result of the previous run. Essentially double aligning.
+ processed = process(tmp.c_str(), dst.c_str(), 4, true, false, 4096);
+ ASSERT_EQ(0, processed);
+
+ verified = verify(dst.c_str(), 4, true, false);
+ ASSERT_EQ(0, verified);
+
+ // Nothing should have changed between tmp and dst.
+ std::string tmp_content;
+ ASSERT_EQ(true, ReadFileToString(tmp, &tmp_content));
+
+ std::string dst_content;
+ ASSERT_EQ(true, ReadFileToString(dst, &dst_content));
+
+ ASSERT_EQ(tmp_content, dst_content);
+}
+
// Align a zip featuring a hole at the beginning. The
// hole in the archive is a delete entry in the Central
// Directory.