Merge "Remove references to device/generic/goldfish/arm32-vendor.mk"
diff --git a/Changes.md b/Changes.md
index 3ad2641..8979e30 100644
--- a/Changes.md
+++ b/Changes.md
@@ -860,6 +860,39 @@
the makefile system. If you need one of them, you'll have to set up your own
version.
+## Soong config variables
+
+### Soong config string variables must list all values they can be set to
+
+In order to facilitate the transition to bazel, all soong_config_string_variables
+must only be set to a value listed in their `values` property, or an empty string.
+It is a build error otherwise.
+
+Example Android.bp:
+```
+soong_config_string_variable {
+ name: "my_string_variable",
+ values: [
+ "foo",
+ "bar",
+ ],
+}
+
+soong_config_module_type {
+ name: "my_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "my_namespace",
+ variables: ["my_string_variable"],
+ properties: [
+ "shared_libs",
+ "static_libs",
+ ],
+}
+```
+Product config:
+```
+$(call soong_config_set,my_namespace,my_string_variable,baz) # Will be an error as baz is not listed in my_string_variable's values.
+```
[build/soong/Changes.md]: https://android.googlesource.com/platform/build/soong/+/master/Changes.md
[build/soong/docs/best_practices.md#headers]: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers
diff --git a/core/BUILD b/core/BUILD
new file mode 100644
index 0000000..3e69e62
--- /dev/null
+++ b/core/BUILD
@@ -0,0 +1,4 @@
+# Export tradefed templates for tests.
+exports_files(
+ glob(["*.xml"]),
+)
diff --git a/core/Makefile b/core/Makefile
index e0b1287..955b360 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -2987,12 +2987,10 @@
$$(fsverity-metadata-targets-$(2))
rm -rf $$(PRIVATE_ASSETS)
mkdir -p $$(PRIVATE_ASSETS)
-ifdef fsverity-metadata-targets-$(2)
$$< --fsverity-path $$(PRIVATE_FSVERITY) \
--base-dir $$(PRODUCT_OUT) \
--output $$(PRIVATE_ASSETS)/build_manifest.pb \
$$(PRIVATE_INPUTS)
-endif # fsverity-metadata-targets-$(2)
$$(PRIVATE_AAPT2) link -o $$@ \
-A $$(PRIVATE_ASSETS) \
-I $$(PRIVATE_FRAMEWORK_RES) \
@@ -3933,7 +3931,7 @@
INSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img
INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET := $(PRODUCT_OUT)/pvmfw_embedded.avbpubkey
INTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,pvmfw_img)
-INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_sign_key)
+INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_embedded_key)
$(call declare-1p-container,$(INSTALLED_PVMFWIMAGE_TARGET),)
$(call declare-container-license-deps,$(INSTALLED_PVMFWIMAGE_TARGET),$(INTERNAL_PVMFWIMAGE_FILES),$(PRODUCT_OUT)/:/)
@@ -5013,9 +5011,11 @@
INTERNAL_OTATOOLS_MODULES += \
apexer \
apex_compression_tool \
+ blkid \
deapexer \
debugfs_static \
dump_apex_info \
+ fsck.erofs \
merge_zips \
resize2fs \
soong_zip \
@@ -5045,6 +5045,12 @@
$(sort $(shell find build/make/target/product/security -type f -name "*.x509.pem" -o \
-name "*.pk8"))
+ifneq (,$(wildcard packages/modules))
+INTERNAL_OTATOOLS_PACKAGE_FILES += \
+ $(sort $(shell find packages/modules -type f -name "*.x509.pem" -o -name "*.pk8" -o -name \
+ "key.pem"))
+endif
+
ifneq (,$(wildcard device))
INTERNAL_OTATOOLS_PACKAGE_FILES += \
$(sort $(shell find device $(wildcard vendor) -type f -name "*.pk8" -o -name "verifiedboot*" -o \
@@ -6824,16 +6830,26 @@
$(HOST_OUT_EXECUTABLES)/atree \
$(HOST_OUT_EXECUTABLES)/line_endings
+# The name of the subdir within the platforms dir of the sdk. One of:
+# - android-<SDK_INT> (stable base dessert SDKs)
+# - android-<CODENAME> (stable extension SDKs)
+# - android-<SDK_INT>-ext<EXT_INT> (codename SDKs)
+sdk_platform_dir_name := $(strip \
+ $(if $(filter REL,$(PLATFORM_VERSION_CODENAME)), \
+ $(if $(filter $(PLATFORM_SDK_EXTENSION_VERSION),$(PLATFORM_BASE_SDK_EXTENSION_VERSION)), \
+ android-$(PLATFORM_SDK_VERSION), \
+ android-$(PLATFORM_SDK_VERSION)-ext$(PLATFORM_SDK_EXTENSION_VERSION) \
+ ), \
+ android-$(PLATFORM_VERSION_CODENAME) \
+ ) \
+)
+
INTERNAL_SDK_TARGET := $(sdk_dir)/$(sdk_name).zip
$(INTERNAL_SDK_TARGET): PRIVATE_NAME := $(sdk_name)
$(INTERNAL_SDK_TARGET): PRIVATE_DIR := $(sdk_dir)/$(sdk_name)
$(INTERNAL_SDK_TARGET): PRIVATE_DEP_FILE := $(sdk_dep_file)
$(INTERNAL_SDK_TARGET): PRIVATE_INPUT_FILES := $(sdk_atree_files)
-$(INTERNAL_SDK_TARGET): PRIVATE_PLATFORM_NAME := \
- $(strip $(if $(filter $(PLATFORM_SDK_EXTENSION_VERSION),$(PLATFORM_BASE_SDK_EXTENSION_VERSION)),\
- android-$(PLATFORM_SDK_VERSION),\
- android-$(PLATFORM_SDK_VERSION)-ext$(PLATFORM_SDK_EXTENSION_VERSION)) \
-)
+$(INTERNAL_SDK_TARGET): PRIVATE_PLATFORM_NAME := $(sdk_platform_dir_name)
# Set SDK_GNU_ERROR to non-empty to fail when a GNU target is built.
#
#SDK_GNU_ERROR := true
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 8fe5214..e325760 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -153,7 +153,6 @@
LOCAL_JAR_PROCESSOR_ARGS:=
LOCAL_JAVACFLAGS:=
LOCAL_JAVA_LANGUAGE_VERSION:=
-LOCAL_JAVA_LAYERS_FILE:=
LOCAL_JAVA_LIBRARIES:=
LOCAL_JAVA_RESOURCE_DIRS:=
LOCAL_JAVA_RESOURCE_FILES:=
diff --git a/core/config.mk b/core/config.mk
index e8b984d..631ba34 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -166,6 +166,8 @@
$(KATI_obsolete_var PRODUCT_SUPPORTS_BOOT_SIGNER,VB 1.0 and related variables are no longer supported)
$(KATI_obsolete_var PRODUCT_VERITY_SIGNING_KEY,VB 1.0 and related variables are no longer supported)
$(KATI_obsolete_var BOARD_PREBUILT_PVMFWIMAGE,pvmfw.bin is now built in AOSP and custom versions are no longer supported)
+$(KATI_obsolete_var BOARD_BUILD_SYSTEM_ROOT_IMAGE)
+
# Used to force goals to build. Only use for conditionally defined goals.
.PHONY: FORCE
FORCE:
@@ -612,7 +614,7 @@
JARJAR := $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar
DATA_BINDING_COMPILER := $(HOST_OUT_JAVA_LIBRARIES)/databinding-compiler.jar
FAT16COPY := build/make/tools/fat16copy.py
-CHECK_ELF_FILE := build/make/tools/check_elf_file.py
+CHECK_ELF_FILE := $(HOST_OUT_EXECUTABLES)/check_elf_file$(HOST_EXECUTABLE_SUFFIX)
LPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX)
ADD_IMG_TO_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/add_img_to_target_files$(HOST_EXECUTABLE_SUFFIX)
BUILD_IMAGE := $(HOST_OUT_EXECUTABLES)/build_image$(HOST_EXECUTABLE_SUFFIX)
@@ -866,11 +868,6 @@
endif
endif
-# TODO(b/241346584): Mark BOARD_BUILD_SYSTEM_ROOT_IMAGE as KATI_obsolete_var after all users are removed
-ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
- $(error BOARD_BUILD_SYSTEM_ROOT_IMAGE is deprecated)
-endif
-
ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
ifneq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true)
$(error PRODUCT_USE_DYNAMIC_PARTITION_SIZE must be true for devices with dynamic partitions)
diff --git a/core/definitions.mk b/core/definitions.mk
index 98fdd6c..afa7f7b 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2604,8 +2604,6 @@
$(if $(PRIVATE_SRCJARS),\@$(PRIVATE_SRCJAR_LIST_FILE)) \
|| ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 ) \
fi
-$(if $(PRIVATE_JAVA_LAYERS_FILE), $(hide) build/make/tools/java-layers.py \
- $(PRIVATE_JAVA_LAYERS_FILE) @$(PRIVATE_JAVA_SOURCE_LIST),)
$(if $(PRIVATE_JAR_EXCLUDE_FILES), $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
-name $(word 1, $(PRIVATE_JAR_EXCLUDE_FILES)) \
$(addprefix -o -name , $(wordlist 2, 999, $(PRIVATE_JAR_EXCLUDE_FILES))) \
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 593ad66..d7a00d0 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -62,6 +62,8 @@
boot_zip := $(PRODUCT_OUT)/boot.zip
bootclasspath_jars := $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)
+
+# TODO remove system_server_jars usages from boot.zip and depend directly on system_server.zip file.
system_server_jars := \
$(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),\
$(PRODUCT_OUT)/system/framework/$(call word-colon,2,$(m)).jar)
@@ -79,5 +81,34 @@
$(call dist-for-goals, droidcore, $(boot_zip))
+# Build the system_server.zip which contains the Apex system server jars and standalone system server jars
+system_server_zip := $(PRODUCT_OUT)/system_server.zip
+apex_system_server_jars := \
+ $(foreach m,$(PRODUCT_APEX_SYSTEM_SERVER_JARS),\
+ $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+
+apex_standalone_system_server_jars := \
+ $(foreach m,$(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS),\
+ $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+
+standalone_system_server_jars := \
+ $(foreach m,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS),\
+ $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+
+$(system_server_zip): PRIVATE_SYSTEM_SERVER_JARS := $(system_server_jars)
+$(system_server_zip): PRIVATE_APEX_SYSTEM_SERVER_JARS := $(apex_system_server_jars)
+$(system_server_zip): PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS := $(apex_standalone_system_server_jars)
+$(system_server_zip): PRIVATE_STANDALONE_SYSTEM_SERVER_JARS := $(standalone_system_server_jars)
+$(system_server_zip): $(system_server_jars) $(apex_system_server_jars) $(apex_standalone_system_server_jars) $(standalone_system_server_jars) $(SOONG_ZIP)
+ @echo "Create system server package: $@"
+ rm -f $@
+ $(SOONG_ZIP) -o $@ \
+ -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) \
+ -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_APEX_SYSTEM_SERVER_JARS)) \
+ -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS)) \
+ -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_STANDALONE_SYSTEM_SERVER_JARS))
+
+$(call dist-for-goals, droidcore, $(system_server_zip))
+
endif #PRODUCT_USES_DEFAULT_ART_CONFIG
endif #WITH_DEXPREOPT
diff --git a/core/envsetup.mk b/core/envsetup.mk
index fc4afd9..7dd9b12 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -135,15 +135,17 @@
HOST_OS := darwin
endif
-HOST_OS_EXTRA := $(shell uname -rsm)
-ifeq ($(HOST_OS),linux)
- ifneq ($(wildcard /etc/os-release),)
- HOST_OS_EXTRA += $(shell source /etc/os-release; echo $$PRETTY_NAME)
+ifeq ($(CALLED_FROM_SETUP),true)
+ HOST_OS_EXTRA := $(shell uname -rsm)
+ ifeq ($(HOST_OS),linux)
+ ifneq ($(wildcard /etc/os-release),)
+ HOST_OS_EXTRA += $(shell source /etc/os-release; echo $$PRETTY_NAME)
+ endif
+ else ifeq ($(HOST_OS),darwin)
+ HOST_OS_EXTRA += $(shell sw_vers -productVersion)
endif
-else ifeq ($(HOST_OS),darwin)
- HOST_OS_EXTRA += $(shell sw_vers -productVersion)
+ HOST_OS_EXTRA := $(subst $(space),-,$(HOST_OS_EXTRA))
endif
-HOST_OS_EXTRA := $(subst $(space),-,$(HOST_OS_EXTRA))
# BUILD_OS is the real host doing the build.
BUILD_OS := $(HOST_OS)
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index 0f95202..89aa53c 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -56,10 +56,6 @@
include $(BUILD_SYSTEM)/java_common.mk
-# The layers file allows you to enforce a layering between java packages.
-# Run build/make/tools/java-layers.py for more details.
-layers_file := $(addprefix $(LOCAL_PATH)/, $(LOCAL_JAVA_LAYERS_FILE))
-
# List of dependencies for anything that needs all java sources in place
java_sources_deps := \
$(java_sources) \
@@ -72,7 +68,6 @@
# TODO(b/143658984): goma can't handle the --system argument to javac.
#$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL)
-$(full_classes_compiled_jar): PRIVATE_JAVA_LAYERS_FILE := $(layers_file)
$(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags)
$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES :=
$(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES :=
diff --git a/core/java.mk b/core/java.mk
index 01951c0..b13ef4d 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -200,10 +200,6 @@
$(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_stubs_jar)))
ALL_MODULES.$(my_register_name).STUBS := $(full_classes_stubs_jar)
-# The layers file allows you to enforce a layering between java packages.
-# Run build/make/tools/java-layers.py for more details.
-layers_file := $(addprefix $(LOCAL_PATH)/, $(LOCAL_JAVA_LAYERS_FILE))
-$(full_classes_compiled_jar): PRIVATE_JAVA_LAYERS_FILE := $(layers_file)
$(full_classes_compiled_jar): PRIVATE_WARNINGS_ENABLE := $(LOCAL_WARNINGS_ENABLE)
# Compile the java files to a .jar file.
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index dbd1e84..e83d408 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -41,3 +41,9 @@
$(call dist-for-goals, general-tests, $(MODULE_INFO_JSON))
$(call dist-for-goals, droidcore-unbundled, $(MODULE_INFO_JSON))
+
+# On every build, generate an all_modules.txt file to be used for autocompleting
+# the m command. After timing this using $(shell date +"%s.%3N"), it only adds
+# 0.01 seconds to the internal master build, and will only rerun on builds that
+# rerun kati.
+$(file >$(PRODUCT_OUT)/all_modules.txt,$(subst $(space),$(newline),$(ALL_MODULES)))
diff --git a/envsetup.sh b/envsetup.sh
index bc206c4..4c1aeaa 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -253,6 +253,9 @@
local ATEST_PATH="$T/prebuilts/asuite/atest/$os_arch"
ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ACLOUD_PATH:$AIDEGEN_PATH:$ATEST_PATH
+ # Build system
+ ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$T/build/bazel/bin
+
export ANDROID_BUILD_PATHS=$(tr -s : <<<"${ANDROID_BUILD_PATHS}:")
export PATH=$ANDROID_BUILD_PATHS$PATH
@@ -294,22 +297,6 @@
#export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
}
-function bazel()
-{
- 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
-
- 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" "$@"
-}
-
function printconfig()
{
local T=$(gettop)
@@ -1564,12 +1551,10 @@
fi
}
-# List all modules for the current device, as cached in module-info.json. If any build change is
-# made and it should be reflected in the output, you should run 'refreshmod' first.
+# List all modules for the current device, as cached in all_modules.txt. If any build change is
+# made and it should be reflected in the output, you should run `m nothing` first.
function allmod() {
- verifymodinfo || return 1
-
- python3 -c "import json; print('\n'.join(sorted(json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')).keys())))"
+ cat $ANDROID_PRODUCT_OUT/all_modules.txt 2>/dev/null
}
# Return the Bazel label of a Soong module if it is converted with bp2build.
@@ -1586,9 +1571,9 @@
#
# For a snappy result, use the latest generated version in soong_injection,
# and ask users to run m bp2build if it doesn't exist.
- converted_json="out/soong/soong_injection/metrics/converted_modules_path_map.json"
+ converted_json="$(get_abs_build_var OUT_DIR)/soong/soong_injection/metrics/converted_modules_path_map.json"
- if [ ! -f $(gettop)/${converted_json} ]; then
+ if [ ! -f ${converted_json} ]; then
echo "bp2build files not found. Have you ran 'm bp2build'?" >&2
return 1
fi
@@ -1748,7 +1733,7 @@
function _complete_android_module_names() {
local word=${COMP_WORDS[COMP_CWORD]}
- COMPREPLY=( $(QUIET_VERIFYMODINFO=true allmod | grep -E "^$word") )
+ COMPREPLY=( $(allmod | grep -E "^$word") )
}
# Print colored exit condition
@@ -1842,59 +1827,6 @@
fi
)
-# Convenience entry point (like m) to use Bazel in AOSP.
-function b()
-(
- # zsh breaks posix by not doing string-splitting on unquoted args by default.
- # See https://zsh.sourceforge.io/Guide/zshguide05.html section 5.4.4.
- # Tell it to emulate Bourne shell for this function.
- if [ -n "$ZSH_VERSION" ]; then emulate -L sh; fi
-
- # Look for the --run-soong-tests flag and skip passing --skip-soong-tests to Soong if present
- local bazel_args=""
- local skip_tests="--skip-soong-tests"
- for i in $@; do
- if [[ $i != "--run-soong-tests" ]]; then
- bazel_args+="$i "
- else
- skip_tests=""
- fi
- done
-
- # Generate BUILD, bzl files into the synthetic Bazel workspace (out/soong/workspace).
- # RBE is disabled because it's not used with b builds and adds overhead: b/251441524
- USE_RBE=false _trigger_build "all-modules" bp2build $skip_tests USE_BAZEL_ANALYSIS= || return 1
- # Then, run Bazel using the synthetic workspace as the --package_path.
- if [[ -z "$bazel_args" ]]; then
- # If there are no args, show help and exit.
- bazel help
- else
- # Else, always run with the bp2build configuration, which sets Bazel's package path to the synthetic workspace.
- # Add the --config=bp2build after the first argument that doesn't start with a dash. That should be the bazel
- # command. (build, test, run, ect) If the --config was added at the end, it wouldn't work with commands like:
- # b run //foo -- --args-for-foo
- local config_set=0
-
- # Represent the args as an array, not a string.
- local bazel_args_with_config=()
- for arg in $bazel_args; do
- if [[ $arg == "--" && $config_set -ne 1 ]]; # if we find --, insert config argument here
- then
- bazel_args_with_config+=("--config=bp2build -- ")
- config_set=1
- else
- bazel_args_with_config+=("$arg ")
- fi
- done
- if [[ $config_set -ne 1 ]]; then
- bazel_args_with_config+=("--config=bp2build ")
- fi
-
- # Call Bazel.
- bazel ${bazel_args_with_config[@]}
- fi
-)
-
function m()
(
_trigger_build "all-modules" "$@"
@@ -2046,13 +1978,7 @@
return
;;
esac
- if [[ -z "$OUT_DIR" ]]; then
- if [[ -z "$OUT_DIR_COMMON_BASE" ]]; then
- OUT_DIR=out
- else
- OUT_DIR=${OUT_DIR_COMMON_BASE}/${PWD##*/}
- fi
- fi
+ OUT_DIR="$(get_abs_build_var OUT_DIR)"
if [[ "$1" == "--regenerate" ]]; then
shift 1
NINJA_ARGS="-t commands $@" m
diff --git a/finalize-aidl-vndk-sdk-resources.sh b/finalize-aidl-vndk-sdk-resources.sh
index e74ba71..8e12c49 100755
--- a/finalize-aidl-vndk-sdk-resources.sh
+++ b/finalize-aidl-vndk-sdk-resources.sh
@@ -23,9 +23,11 @@
out/host/linux-x86/bin/create_reference_dumps \
-p aosp_arm64 --build-variant user
+ echo "NOTE: THIS INTENTIONALLY MAY FAIL AND REPAIR ITSELF (until 'DONE')"
# Update new versions of files. See update-vndk-list.sh (which requires envsetup.sh)
$m check-vndk-list || \
{ cp $top/out/soong/vndk/vndk.libraries.txt $top/build/make/target/product/gsi/current.txt; }
+ echo "DONE: THIS INTENTIONALLY MAY FAIL AND REPAIR ITSELF"
# Finalize resources
"$top/frameworks/base/tools/aapt2/tools/finalize_res.py" \
diff --git a/finalize-cleanup.sh b/finalize-cleanup.sh
index efa2707..c62a97c 100755
--- a/finalize-cleanup.sh
+++ b/finalize-cleanup.sh
@@ -9,8 +9,8 @@
repo selfupdate
repo forall -c '\
- git checkout . ; git clean -fdx ;\
- git checkout @ ; git b fina-step1 -D ; git reset --hard; \
+ git checkout . ; git revert --abort ; git clean -fdx ;\
+ git checkout @ ; git branch fina-step1 -D ; git reset --hard; \
repo start fina-step1 ; git checkout @ ; git b fina-step1 -D ;'
}
diff --git a/finalize-step-1.sh b/finalize-step-1.sh
index 9f87b6c..1ca98d4 100755
--- a/finalize-step-1.sh
+++ b/finalize-step-1.sh
@@ -6,13 +6,18 @@
# set -ex
-function revert_local_changes() {
+function revert_to_unfinalized_state() {
repo forall -c '\
- git checkout . ; git clean -fdx ;\
- git checkout @ ; git b fina-step1 -D ; git reset --hard; \
+ git checkout . ; git revert --abort ; git clean -fdx ;\
+ git checkout @ ; git branch fina-step1 -D ; git reset --hard; \
repo start fina-step1 ; git checkout @ ; git b fina-step1 -D ;\
- previousHash="$(git log --format=%H --no-merges --max-count=100 --grep ^FINALIZATION_STEP_1_SCRIPT_COMMIT)" ;\
- if [[ $previousHash ]]; then git revert --no-commit $previousHash ; fi ;'
+ baselineHash="$(git log --format=%H --no-merges --max-count=1 --grep ^FINALIZATION_STEP_1_BASELINE_COMMIT)" ;\
+ if [[ $baselineHash ]]; then
+ previousHash="$(git log --format=%H --no-merges --max-count=100 --grep ^FINALIZATION_STEP_1_SCRIPT_COMMIT $baselineHash..HEAD | tr \n \040)" ;\
+ else
+ previousHash="$(git log --format=%H --no-merges --max-count=100 --grep ^FINALIZATION_STEP_1_SCRIPT_COMMIT | tr \n \040)" ;\
+ fi ; \
+ if [[ $previousHash ]]; then git revert --no-commit --strategy=ort --strategy-option=ours $previousHash ; fi ;'
}
function commit_changes() {
@@ -31,7 +36,7 @@
repo selfupdate
- revert_local_changes
+ revert_to_unfinalized_state
# vndk etc finalization
source $top/build/make/finalize-aidl-vndk-sdk-resources.sh
diff --git a/finalize_branch_for_release.sh b/finalize_branch_for_release.sh
index b46390d..9e9d6a1 100755
--- a/finalize_branch_for_release.sh
+++ b/finalize_branch_for_release.sh
@@ -17,7 +17,7 @@
# Resource/SDK finalization.
# In the future, we would want to actually turn the branch into the REL
# state and test with that.
- AIDL_FROZEN_REL=true $m droidcore
+ AIDL_FROZEN_REL=true $m
# Build SDK (TODO)
# lunch sdk...
diff --git a/target/board/Android.mk b/target/board/Android.mk
index baa3d3a..21c0c10 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -19,8 +19,11 @@
ifndef board_info_txt
board_info_txt := $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt)
endif
-$(INSTALLED_ANDROID_INFO_TXT_TARGET): $(board_info_txt) build/make/tools/check_radio_versions.py
- $(hide) build/make/tools/check_radio_versions.py $< $(BOARD_INFO_CHECK)
+CHECK_RADIO_VERSIONS := $(HOST_OUT_EXECUTABLES)/check_radio_versions$(HOST_EXECUTABLE_SUFFIX)
+$(INSTALLED_ANDROID_INFO_TXT_TARGET): $(board_info_txt) $(CHECK_RADIO_VERSIONS)
+ $(hide) $(CHECK_RADIO_VERSIONS) \
+ --board_info_txt $(board_info_txt) \
+ --board_info_check $(BOARD_INFO_CHECK)
$(call pretty,"Generated: ($@)")
ifdef board_info_txt
$(hide) grep -v '#' $< > $@
diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk
index 8c634f6..4d95b33 100644
--- a/target/board/BoardConfigGsiCommon.mk
+++ b/target/board/BoardConfigGsiCommon.mk
@@ -3,6 +3,8 @@
# Common compile-time definitions for GSI
# Builds upon the mainline config.
#
+# See device/generic/common/README.md for more details.
+#
include build/make/target/board/BoardConfigMainlineCommon.mk
@@ -17,6 +19,12 @@
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
TARGET_USERIMAGES_SPARSE_EROFS_DISABLED := true
+# Enable system_dlkm image for creating a symlink in GSI to support
+# the devices with system_dlkm partition
+BOARD_USES_SYSTEM_DLKMIMAGE := true
+BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE := ext4
+TARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm
+
# GSI also includes make_f2fs to support userdata parition in f2fs
# for some devices
TARGET_USERIMAGES_USE_F2FS := true
diff --git a/target/product/aosp_riscv64.mk b/target/product/aosp_riscv64.mk
index 518f8b1..023317b 100644
--- a/target/product/aosp_riscv64.mk
+++ b/target/product/aosp_riscv64.mk
@@ -30,7 +30,8 @@
# GSI for system/product & support 64-bit apps only
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk)
+#$(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk)
+TARGET_FLATTEN_APEX := false
#
# All components inherited here go to system_ext image
@@ -46,7 +47,7 @@
#
# All components inherited here go to vendor image
#
-#$(call inherit-product-if-exists, device/generic/goldfish/riscv64-vendor.mk)
+$(call inherit-product-if-exists, device/generic/goldfish/riscv64-vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_riscv64/device.mk)
@@ -57,6 +58,17 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
endif
+# TODO: this list should come via mainline_system.mk, but for now list
+# just the modules that work for riscv64.
+PRODUCT_PACKAGES := \
+ init.environ.rc \
+ init_system \
+ linker \
+ shell_and_utilities \
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk)
+PRODUCT_USES_DEFAULT_ART_CONFIG := false
+
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
root/init.zygote64.rc
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 94892dc..56a9d8a 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -7,6 +7,7 @@
LLNDK: libbinder_ndk.so
LLNDK: libc.so
LLNDK: libcgrouprc.so
+LLNDK: libcom.android.tethering.connectivity_native.so
LLNDK: libdl.so
LLNDK: libft2.so
LLNDK: liblog.so
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index 20493be..09d4bc9 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -23,6 +23,8 @@
# - Released GSI contains more VNDK packages to support old version vendors
# - etc.
#
+# See device/generic/common/README.md for more details.
+#
BUILDING_GSI := true
@@ -83,6 +85,7 @@
PRODUCT_BUILD_VENDOR_IMAGE := false
PRODUCT_BUILD_SUPER_PARTITION := false
PRODUCT_BUILD_SUPER_EMPTY_IMAGE := false
+PRODUCT_BUILD_SYSTEM_DLKM_IMAGE := false
PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true
# Always build modules from source
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index 1ebd4ab..a62cda7 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -165,3 +165,13 @@
dalvik.vm.usap_pool_size_max?=3 \
dalvik.vm.usap_pool_size_min?=1 \
dalvik.vm.usap_pool_refill_delay_ms?=3000
+
+# Allow dexopt files that are side-effects of already allowlisted files.
+# This is only necessary when ART is prebuilt.
+ifeq (false,$(ART_MODULE_BUILD_FROM_SOURCE))
+ PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
+ system/framework/%.art \
+ system/framework/%.oat \
+ system/framework/%.odex \
+ system/framework/%.vdex
+endif
diff --git a/tests/b_tests.sh b/tests/b_tests.sh
index f4e043c..45cb4f7 100755
--- a/tests/b_tests.sh
+++ b/tests/b_tests.sh
@@ -18,6 +18,9 @@
source $(dirname $0)/../envsetup.sh
+# lunch required to set up PATH to use b
+lunch aosp_arm64
+
test_target=//build/bazel/scripts/difftool:difftool
b build "$test_target"
diff --git a/tools/Android.bp b/tools/Android.bp
index bd326f1..1f0d406 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -54,3 +54,13 @@
name: "build-runfiles",
srcs: ["build-runfiles.cc"],
}
+
+python_binary_host {
+ name: "check_radio_versions",
+ srcs: ["check_radio_versions.py"],
+}
+
+python_binary_host {
+ name: "check_elf_file",
+ srcs: ["check_elf_file.py"],
+}
diff --git a/tools/check_elf_file.py b/tools/check_elf_file.py
index 0b80226..eaa1854 100755
--- a/tools/check_elf_file.py
+++ b/tools/check_elf_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2019 The Android Open Source Project
#
@@ -196,11 +196,7 @@
def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):
"""Run llvm-readobj and parse the output."""
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)
+ out = subprocess.check_output(cmd, text=True)
lines = out.splitlines()
return cls._parse_llvm_readobj(elf_file_path, header, lines)
@@ -467,7 +463,7 @@
"""Check whether all undefined symbols are resolved to a definition."""
all_elf_files = [self._file_under_test] + self._shared_libs
missing_symbols = []
- for sym, imported_vers in self._file_under_test.imported.iteritems():
+ for sym, imported_vers in self._file_under_test.imported.items():
for imported_ver in imported_vers:
lib = self._find_symbol_from_libs(all_elf_files, sym, imported_ver)
if not lib:
diff --git a/tools/check_radio_versions.py b/tools/check_radio_versions.py
index ebe621f..d1d50e6 100755
--- a/tools/check_radio_versions.py
+++ b/tools/check_radio_versions.py
@@ -22,11 +22,18 @@
except ImportError:
from sha import sha as sha1
-if len(sys.argv) < 2:
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--board_info_txt", nargs="?", required=True)
+parser.add_argument("--board_info_check", nargs="*", required=True)
+args = parser.parse_args()
+
+if not args.board_info_txt:
sys.exit(0)
build_info = {}
-f = open(sys.argv[1])
+f = open(args.board_info_txt)
for line in f:
line = line.strip()
if line.startswith("require"):
@@ -36,7 +43,7 @@
bad = False
-for item in sys.argv[2:]:
+for item in args.board_info_check:
key, fn = item.split(":", 1)
values = build_info.get(key, None)
@@ -52,8 +59,8 @@
try:
f = open(fn + ".sha1")
except IOError:
- if not bad: print
- print "*** Error opening \"%s.sha1\"; can't verify %s" % (fn, key)
+ if not bad: print()
+ print("*** Error opening \"%s.sha1\"; can't verify %s" % (fn, key))
bad = True
continue
for line in f:
@@ -63,17 +70,17 @@
versions[h] = v
if digest not in versions:
- if not bad: print
- print "*** SHA-1 hash of \"%s\" doesn't appear in \"%s.sha1\"" % (fn, fn)
+ if not bad: print()
+ print("*** SHA-1 hash of \"%s\" doesn't appear in \"%s.sha1\"" % (fn, fn))
bad = True
continue
if versions[digest] not in values:
- if not bad: print
- print "*** \"%s\" is version %s; not any %s allowed by \"%s\"." % (
- fn, versions[digest], key, sys.argv[1])
+ if not bad: print()
+ print("*** \"%s\" is version %s; not any %s allowed by \"%s\"." % (
+ fn, versions[digest], key, args.board_info_txt))
bad = True
if bad:
- print
+ print()
sys.exit(1)
diff --git a/tools/compare_fileslist.py b/tools/compare_fileslist.py
deleted file mode 100755
index 1f507d8..0000000
--- a/tools/compare_fileslist.py
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import cgi, os, string, sys
-
-def IsDifferent(row):
- val = None
- for v in row:
- if v:
- if not val:
- val = v
- else:
- if val != v:
- return True
- return False
-
-def main(argv):
- inputs = argv[1:]
- data = {}
- index = 0
- for input in inputs:
- f = file(input, "r")
- lines = f.readlines()
- f.close()
- lines = map(string.split, lines)
- lines = map(lambda (x,y): (y,int(x)), lines)
- for fn,sz in lines:
- if not data.has_key(fn):
- data[fn] = {}
- data[fn][index] = sz
- index = index + 1
- rows = []
- for fn,sizes in data.iteritems():
- row = [fn]
- for i in range(0,index):
- if sizes.has_key(i):
- row.append(sizes[i])
- else:
- row.append(None)
- rows.append(row)
- rows = sorted(rows, key=lambda x: x[0])
- print """<html>
- <head>
- <style type="text/css">
- .fn, .sz, .z, .d {
- padding-left: 10px;
- padding-right: 10px;
- }
- .sz, .z, .d {
- text-align: right;
- }
- .fn {
- background-color: #ffffdd;
- }
- .sz {
- background-color: #ffffcc;
- }
- .z {
- background-color: #ffcccc;
- }
- .d {
- background-color: #99ccff;
- }
- </style>
- </head>
- <body>
- """
- print "<table>"
- print "<tr>"
- for input in inputs:
- combo = input.split(os.path.sep)[1]
- print " <td class='fn'>%s</td>" % cgi.escape(combo)
- print "</tr>"
-
- for row in rows:
- print "<tr>"
- for sz in row[1:]:
- if not sz:
- print " <td class='z'> </td>"
- elif IsDifferent(row[1:]):
- print " <td class='d'>%d</td>" % sz
- else:
- print " <td class='sz'>%d</td>" % sz
- print " <td class='fn'>%s</td>" % cgi.escape(row[0])
- print "</tr>"
- print "</table>"
- print "</body></html>"
-
-if __name__ == '__main__':
- main(sys.argv)
-
-
diff --git a/tools/compliance/Android.bp b/tools/compliance/Android.bp
index 2527df7..8e13f2f 100644
--- a/tools/compliance/Android.bp
+++ b/tools/compliance/Android.bp
@@ -131,6 +131,17 @@
testSrcs: ["cmd/xmlnotice/xmlnotice_test.go"],
}
+blueprint_go_binary {
+ name: "compliance_sbom",
+ srcs: ["cmd/sbom/sbom.go"],
+ deps: [
+ "compliance-module",
+ "blueprint-deptools",
+ "soong-response",
+ ],
+ testSrcs: ["cmd/sbom/sbom_test.go"],
+}
+
bootstrap_go_package {
name: "compliance-module",
srcs: [
diff --git a/tools/compliance/README.md b/tools/compliance/README.md
new file mode 100644
index 0000000..995d9ca
--- /dev/null
+++ b/tools/compliance/README.md
@@ -0,0 +1,101 @@
+# Compliance
+
+<!-- Much of this content appears too in doc.go
+When changing this file consider whether the change also applies to doc.go -->
+
+Package compliance provides an approved means for reading, consuming, and
+analyzing license metadata graphs.
+
+Assuming the license metadata and dependencies are fully and accurately
+recorded in the build system, any discrepancy between the official policy for
+open source license compliance and this code is **a bug in this code.**
+
+## Naming
+
+All of the code that directly reflects a policy decision belongs in a file with
+a name begninning `policy_`. Changes to these files need to be authored or
+reviewed by someone in OSPO or whichever successor group governs policy.
+
+The files with names not beginning `policy_` describe data types, and general,
+reusable algorithms.
+
+The source code for binary tools and utilities appears under the `cmd/`
+subdirectory. Other subdirectories contain reusable components that are not
+`compliance` per se.
+
+## Data Types
+
+A few principal types to understand are LicenseGraph, LicenseCondition, and
+ResolutionSet.
+
+### LicenseGraph
+
+A LicenseGraph is an immutable graph of the targets and dependencies reachable
+from a specific set of root targets. In general, the root targets will be the
+artifacts in a release or distribution. While conceptually immutable, parts of
+the graph may be loaded or evaluated lazily.
+
+Conceptually, the graph itself will always be a directed acyclic graph. One
+representation is a set of directed edges. Another is a set of nodes with
+directed edges to their dependencies.
+
+The edges have annotations, which can distinguish between build tools, runtime
+dependencies, and dependencies like 'contains' that make a derivative work.
+
+### LicenseCondition
+
+A LicenseCondition is an immutable tuple pairing a condition name with an
+originating target. e.g. Per current policy, a static library licensed under an
+MIT license would pair a "notice" condition with the static library target, and
+a dynamic license licensed under GPL would pair a "restricted" condition with
+the dynamic library target.
+
+### ResolutionSet
+
+A ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`
+tuples describing how license conditions apply to targets.
+
+`AttachesTo` is the trigger for acting. Distribution of the target invokes
+the policy.
+
+`ActsOn` is the target to share, give notice for, hide etc.
+
+`Resolves` is the set of conditions that the action resolves.
+
+For most condition types, `ActsOn` will be the target where the condition
+originated. For example, a notice condition policy means attribution or notice
+must be given for the target where the condition originates. Likewise, a
+proprietary condition policy means the privacy of the target where the
+condition originates must be respected. i.e. The thing acted on is the origin.
+
+Restricted conditions are different. The infectious nature of restricted often
+means sharing code that is not the target where the restricted condition
+originates. Linking an MIT library to a GPL library implies a policy to share
+the MIT library despite the MIT license having no source sharing requirement.
+
+In this case, one or more resolution tuples will have the MIT license module in
+`ActsOn` and the restricted condition originating at the GPL library module in
+`Resolves`. These tuples will `AttachTo` every target that depends on the GPL
+library because shipping any of those targets trigger the policy to share the
+code.
+
+## Processes
+
+### ReadLicenseGraph
+
+The principal means to ingest license metadata. Given the distribution targets,
+ReadLicenseGraph populates the LicenseGraph for those root targets.
+
+### NoticeIndex.IndexLicenseTexts
+
+IndexLicenseTexts reads, deduplicates and caches license texts for notice
+files. Also reads and caches project metadata for deriving library names.
+
+The algorithm for deriving library names has not been dictated by OSPO policy,
+but reflects a pragmatic attempt to comply with Android policy regarding
+unreleased product names, proprietary partner names etc.
+
+### projectmetadata.Index.MetadataForProjects
+
+MetadataForProjects reads, deduplicates and caches project METADATA files used
+for notice library names, and various properties appearing in SBOMs.
diff --git a/tools/compliance/cmd/htmlnotice/htmlnotice.go b/tools/compliance/cmd/htmlnotice/htmlnotice.go
index 1a49610..78371ee 100644
--- a/tools/compliance/cmd/htmlnotice/htmlnotice.go
+++ b/tools/compliance/cmd/htmlnotice/htmlnotice.go
@@ -24,6 +24,7 @@
"io/fs"
"os"
"path/filepath"
+ "sort"
"strings"
"android/soong/response"
@@ -275,7 +276,8 @@
}
fmt.Fprintln(ctx.stdout, "</body></html>")
- *ctx.deps = ni.InputNoticeFiles()
+ *ctx.deps = ni.InputFiles()
+ sort.Strings(*ctx.deps)
return nil
}
diff --git a/tools/compliance/cmd/htmlnotice/htmlnotice_test.go b/tools/compliance/cmd/htmlnotice/htmlnotice_test.go
index b927018..8dc1197 100644
--- a/tools/compliance/cmd/htmlnotice/htmlnotice_test.go
+++ b/tools/compliance/cmd/htmlnotice/htmlnotice_test.go
@@ -78,7 +78,16 @@
usedBy{"highest.apex/lib/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -106,7 +115,16 @@
usedBy{"highest.apex/lib/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -124,7 +142,16 @@
usedBy{"highest.apex/lib/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -154,7 +181,16 @@
usedBy{"highest.apex/lib/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -170,7 +206,16 @@
usedBy{"container.zip/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -182,7 +227,13 @@
usedBy{"application"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/application.meta_lic",
+ "testdata/firstparty/bin/bin3.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -194,7 +245,12 @@
usedBy{"bin/bin1"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -206,7 +262,10 @@
usedBy{"lib/libd.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "notice",
@@ -231,6 +290,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
},
},
{
@@ -256,6 +322,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
},
},
{
@@ -275,6 +348,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/application.meta_lic",
+ "testdata/notice/bin/bin3.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
},
},
{
@@ -296,6 +373,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
},
},
{
@@ -308,7 +388,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/lib/libd.so.meta_lic",
+ },
},
{
condition: "reciprocal",
@@ -333,6 +416,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -358,6 +448,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -377,6 +474,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/application.meta_lic",
+ "testdata/reciprocal/bin/bin3.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
},
},
{
@@ -398,6 +499,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
},
},
{
@@ -410,7 +514,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ },
},
{
condition: "restricted",
@@ -440,6 +547,13 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
},
},
{
@@ -470,6 +584,13 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
},
},
{
@@ -489,6 +610,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/bin/bin3.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
},
},
{
@@ -513,6 +638,9 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
},
},
{
@@ -525,7 +653,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ },
},
{
condition: "proprietary",
@@ -555,6 +686,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
"testdata/restricted/RESTRICTED_LICENSE",
},
},
@@ -586,6 +724,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
"testdata/restricted/RESTRICTED_LICENSE",
},
},
@@ -606,6 +751,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/bin/bin3.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
},
},
{
@@ -627,6 +776,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
},
},
{
@@ -639,7 +791,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ },
},
}
for _, tt := range tests {
diff --git a/tools/compliance/cmd/sbom/sbom.go b/tools/compliance/cmd/sbom/sbom.go
new file mode 100644
index 0000000..afb377e
--- /dev/null
+++ b/tools/compliance/cmd/sbom/sbom.go
@@ -0,0 +1,399 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+
+ "android/soong/response"
+ "android/soong/tools/compliance"
+ "android/soong/tools/compliance/projectmetadata"
+
+ "github.com/google/blueprint/deptools"
+)
+
+var (
+ failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
+ failNoLicenses = fmt.Errorf("No licenses found")
+)
+
+type context struct {
+ stdout io.Writer
+ stderr io.Writer
+ rootFS fs.FS
+ product string
+ stripPrefix []string
+ creationTime creationTimeGetter
+}
+
+func (ctx context) strip(installPath string) string {
+ for _, prefix := range ctx.stripPrefix {
+ if strings.HasPrefix(installPath, prefix) {
+ p := strings.TrimPrefix(installPath, prefix)
+ if 0 == len(p) {
+ p = ctx.product
+ }
+ if 0 == len(p) {
+ continue
+ }
+ return p
+ }
+ }
+ return installPath
+}
+
+// newMultiString creates a flag that allows multiple values in an array.
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
+ var f multiString
+ flags.Var(&f, name, usage)
+ return &f
+}
+
+// multiString implements the flag `Value` interface for multiple strings.
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ var expandedArgs []string
+ for _, arg := range os.Args[1:] {
+ if strings.HasPrefix(arg, "@") {
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ expandedArgs = append(expandedArgs, respArgs...)
+ } else {
+ expandedArgs = append(expandedArgs, arg)
+ }
+ }
+
+ flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+ flags.Usage = func() {
+ fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
+
+Outputs an SBOM.spdx.
+
+Options:
+`, filepath.Base(os.Args[0]))
+ flags.PrintDefaults()
+ }
+
+ outputFile := flags.String("o", "-", "Where to write the SBOM spdx file. (default stdout)")
+ depsFile := flags.String("d", "", "Where to write the deps file")
+ product := flags.String("product", "", "The name of the product for which the notice is generated.")
+ stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
+
+ flags.Parse(expandedArgs)
+
+ // Must specify at least one root target.
+ if flags.NArg() == 0 {
+ flags.Usage()
+ os.Exit(2)
+ }
+
+ if len(*outputFile) == 0 {
+ flags.Usage()
+ fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
+ os.Exit(2)
+ } else {
+ dir, err := filepath.Abs(filepath.Dir(*outputFile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
+ os.Exit(1)
+ }
+ fi, err := os.Stat(dir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
+ os.Exit(1)
+ }
+ if !fi.IsDir() {
+ fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
+ os.Exit(1)
+ }
+ }
+
+ var ofile io.Writer
+ ofile = os.Stdout
+ var obuf *bytes.Buffer
+ if *outputFile != "-" {
+ obuf = &bytes.Buffer{}
+ ofile = obuf
+ }
+
+ ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, actualTime}
+
+ deps, err := sbomGenerator(ctx, flags.Args()...)
+ if err != nil {
+ if err == failNoneRequested {
+ flags.Usage()
+ }
+ fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+ os.Exit(1)
+ }
+
+ if *outputFile != "-" {
+ err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
+ os.Exit(1)
+ }
+ }
+
+ if *depsFile != "" {
+ err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
+ os.Exit(1)
+ }
+ }
+ os.Exit(0)
+}
+
+type creationTimeGetter func() time.Time
+
+// actualTime returns current time in UTC
+func actualTime() time.Time {
+ return time.Now().UTC()
+}
+
+// replaceSlashes replaces "/" by "-" for the library path to be used for packages & files SPDXID
+func replaceSlashes(x string) string {
+ return strings.ReplaceAll(x, "/", "-")
+}
+
+// getPackageName returns a package name of a target Node
+func getPackageName(_ *context, tn *compliance.TargetNode) string {
+ return replaceSlashes(tn.Name())
+}
+
+// getDocumentName returns a package name of a target Node
+func getDocumentName(ctx *context, tn *compliance.TargetNode, pm *projectmetadata.ProjectMetadata) string {
+ if len(ctx.product) > 0 {
+ return replaceSlashes(ctx.product)
+ }
+ if len(tn.ModuleName()) > 0 {
+ if pm != nil {
+ return replaceSlashes(pm.Name() + ":" + tn.ModuleName())
+ }
+ return replaceSlashes(tn.ModuleName())
+ }
+
+ // TO DO: Replace tn.Name() with pm.Name() + parts of the target name
+ return replaceSlashes(tn.Name())
+}
+
+// getDownloadUrl returns the download URL if available (GIT, SVN, etc..),
+// or NOASSERTION if not available, none determined or ambiguous
+func getDownloadUrl(_ *context, pm *projectmetadata.ProjectMetadata) string {
+ if pm == nil {
+ return "NOASSERTION"
+ }
+
+ urlsByTypeName := pm.UrlsByTypeName()
+ if urlsByTypeName == nil {
+ return "NOASSERTION"
+ }
+
+ url := urlsByTypeName.DownloadUrl()
+ if url == "" {
+ return "NOASSERTION"
+ }
+ return url
+}
+
+// getProjectMetadata returns the project metadata for the target node
+func getProjectMetadata(_ *context, pmix *projectmetadata.Index,
+ tn *compliance.TargetNode) (*projectmetadata.ProjectMetadata, error) {
+ pms, err := pmix.MetadataForProjects(tn.Projects()...)
+ if err != nil {
+ return nil, fmt.Errorf("Unable to read projects for %q: %w\n", tn, err)
+ }
+ if len(pms) == 0 {
+ return nil, nil
+ }
+
+ // TO DO: skip first element if it doesn't have one of the three info needed
+ return pms[0], nil
+}
+
+// sbomGenerator implements the spdx bom utility
+
+// SBOM is part of the new government regulation issued to improve national cyber security
+// and enhance software supply chain and transparency, see https://www.cisa.gov/sbom
+
+// sbomGenerator uses the SPDX standard, see the SPDX specification (https://spdx.github.io/spdx-spec/)
+// sbomGenerator is also following the internal google SBOM styleguide (http://goto.google.com/spdx-style-guide)
+func sbomGenerator(ctx *context, files ...string) ([]string, error) {
+ // Must be at least one root file.
+ if len(files) < 1 {
+ return nil, failNoneRequested
+ }
+
+ pmix := projectmetadata.NewIndex(ctx.rootFS)
+
+ lg, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
+
+ if err != nil {
+ return nil, fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
+ }
+
+ // implementing the licenses references for the packages
+ licenses := make(map[string]string)
+ concludedLicenses := func(licenseTexts []string) string {
+ licenseRefs := make([]string, 0, len(licenseTexts))
+ for _, licenseText := range licenseTexts {
+ license := strings.SplitN(licenseText, ":", 2)[0]
+ if _, ok := licenses[license]; !ok {
+ licenseRef := "LicenseRef-" + replaceSlashes(license)
+ licenses[license] = licenseRef
+ }
+
+ licenseRefs = append(licenseRefs, licenses[license])
+ }
+ if len(licenseRefs) > 1 {
+ return "(" + strings.Join(licenseRefs, " AND ") + ")"
+ } else if len(licenseRefs) == 1 {
+ return licenseRefs[0]
+ }
+ return "NONE"
+ }
+
+ isMainPackage := true
+ var mainPackage string
+ visitedNodes := make(map[*compliance.TargetNode]struct{})
+
+ // performing a Breadth-first top down walk of licensegraph and building package information
+ compliance.WalkTopDownBreadthFirst(nil, lg,
+ func(lg *compliance.LicenseGraph, tn *compliance.TargetNode, path compliance.TargetEdgePath) bool {
+ if err != nil {
+ return false
+ }
+ var pm *projectmetadata.ProjectMetadata
+ pm, err = getProjectMetadata(ctx, pmix, tn)
+ if err != nil {
+ return false
+ }
+
+ if isMainPackage {
+ mainPackage = getDocumentName(ctx, tn, pm)
+ fmt.Fprintf(ctx.stdout, "SPDXVersion: SPDX-2.2\n")
+ fmt.Fprintf(ctx.stdout, "DataLicense: CC-1.0\n")
+ fmt.Fprintf(ctx.stdout, "DocumentName: %s\n", mainPackage)
+ fmt.Fprintf(ctx.stdout, "SPDXID: SPDXRef-DOCUMENT-%s\n", mainPackage)
+ fmt.Fprintf(ctx.stdout, "DocumentNamespace: Android\n")
+ fmt.Fprintf(ctx.stdout, "Creator: Organization: Google LLC\n")
+ fmt.Fprintf(ctx.stdout, "Created: %s\n", ctx.creationTime().Format("2006-01-02T15:04:05Z"))
+ isMainPackage = false
+ }
+
+ relationships := make([]string, 0, 1)
+ defer func() {
+ if r := recover(); r != nil {
+ panic(r)
+ }
+ for _, relationship := range relationships {
+ fmt.Fprintln(ctx.stdout, relationship)
+ }
+ }()
+ if len(path) == 0 {
+ relationships = append(relationships,
+ fmt.Sprintf("Relationship: SPDXRef-DOCUMENT-%s DESCRIBES SPDXRef-Package-%s",
+ mainPackage, getPackageName(ctx, tn)))
+ } else {
+ // Check parent and identify annotation
+ parent := path[len(path)-1]
+ targetEdge := parent.Edge()
+ if targetEdge.IsRuntimeDependency() {
+ // Adding the dynamic link annotation RUNTIME_DEPENDENCY_OF relationship
+ relationships = append(relationships, fmt.Sprintf("Relationship: SPDXRef-Package-%s RUNTIME_DEPENDENCY_OF SPDXRef-Package-%s", getPackageName(ctx, tn), getPackageName(ctx, targetEdge.Target())))
+
+ } else if targetEdge.IsDerivation() {
+ // Adding the derivation annotation as a CONTAINS relationship
+ relationships = append(relationships, fmt.Sprintf("Relationship: SPDXRef-Package-%s CONTAINS SPDXRef-Package-%s", getPackageName(ctx, targetEdge.Target()), getPackageName(ctx, tn)))
+
+ } else if targetEdge.IsBuildTool() {
+ // Adding the toolchain annotation as a BUILD_TOOL_OF relationship
+ relationships = append(relationships, fmt.Sprintf("Relationship: SPDXRef-Package-%s BUILD_TOOL_OF SPDXRef-Package-%s", getPackageName(ctx, tn), getPackageName(ctx, targetEdge.Target())))
+ } else {
+ panic(fmt.Errorf("Unknown dependency type: %v", targetEdge.Annotations()))
+ }
+ }
+
+ if _, alreadyVisited := visitedNodes[tn]; alreadyVisited {
+ return false
+ }
+ visitedNodes[tn] = struct{}{}
+ pkgName := getPackageName(ctx, tn)
+ fmt.Fprintf(ctx.stdout, "##### Package: %s\n", strings.Replace(pkgName, "-", "/", -2))
+ fmt.Fprintf(ctx.stdout, "PackageName: %s\n", pkgName)
+ if pm != nil && pm.Version() != "" {
+ fmt.Fprintf(ctx.stdout, "PackageVersion: %s\n", pm.Version())
+ }
+ fmt.Fprintf(ctx.stdout, "SPDXID: SPDXRef-Package-%s\n", pkgName)
+ fmt.Fprintf(ctx.stdout, "PackageDownloadLocation: %s\n", getDownloadUrl(ctx, pm))
+ fmt.Fprintf(ctx.stdout, "PackageLicenseConcluded: %s\n", concludedLicenses(tn.LicenseTexts()))
+ return true
+ })
+
+ fmt.Fprintf(ctx.stdout, "##### Non-standard license:\n")
+
+ licenseTexts := make([]string, 0, len(licenses))
+
+ for licenseText := range licenses {
+ licenseTexts = append(licenseTexts, licenseText)
+ }
+
+ sort.Strings(licenseTexts)
+
+ for _, licenseText := range licenseTexts {
+ fmt.Fprintf(ctx.stdout, "LicenseID: %s\n", licenses[licenseText])
+ // open the file
+ f, err := ctx.rootFS.Open(filepath.Clean(licenseText))
+ if err != nil {
+ return nil, fmt.Errorf("error opening license text file %q: %w", licenseText, err)
+ }
+
+ // read the file
+ text, err := io.ReadAll(f)
+ if err != nil {
+ return nil, fmt.Errorf("error reading license text file %q: %w", licenseText, err)
+ }
+ // adding the extracted license text
+ fmt.Fprintf(ctx.stdout, "ExtractedText: <text>%v</text>\n", string(text))
+ }
+
+ deps := licenseTexts
+ return deps, nil
+}
diff --git a/tools/compliance/cmd/sbom/sbom_test.go b/tools/compliance/cmd/sbom/sbom_test.go
new file mode 100644
index 0000000..6b40a27
--- /dev/null
+++ b/tools/compliance/cmd/sbom/sbom_test.go
@@ -0,0 +1,1629 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+ "reflect"
+ "regexp"
+ "strings"
+ "testing"
+ "time"
+
+ "android/soong/tools/compliance"
+)
+
+var (
+ spdxVersionTag = regexp.MustCompile(`^\s*SPDXVersion: SPDX-2.2\s*$`)
+ spdxDataLicenseTag = regexp.MustCompile(`^\s*DataLicense: CC-1.0\s*$`)
+ spdxDocumentNameTag = regexp.MustCompile(`^\s*DocumentName:\s*Android*\s*$`)
+ spdxIDTag = regexp.MustCompile(`^\s*SPDXID:\s*SPDXRef-DOCUMENT-(.*)\s*$`)
+ spdxDocumentNameSpaceTag = regexp.MustCompile(`^\s*DocumentNamespace:\s*Android\s*$`)
+ spdxCreatorOrganizationTag = regexp.MustCompile(`^\s*Creator:\s*Organization:\s*Google LLC\s*$`)
+ spdxCreatedTimeTag = regexp.MustCompile(`^\s*Created: 1970-01-01T00:00:00Z\s*$`)
+ spdxPackageTag = regexp.MustCompile(`^\s*#####\s*Package:\s*(.*)\s*$`)
+ spdxPackageNameTag = regexp.MustCompile(`^\s*PackageName:\s*(.*)\s*$`)
+ spdxPkgIDTag = regexp.MustCompile(`^\s*SPDXID:\s*SPDXRef-Package-(.*)\s*$`)
+ spdxPkgDownloadLocationTag = regexp.MustCompile(`^\s*PackageDownloadLocation:\s*NOASSERTION\s*$`)
+ spdxPkgLicenseDeclaredTag = regexp.MustCompile(`^\s*PackageLicenseConcluded:\s*LicenseRef-(.*)\s*$`)
+ spdxRelationshipTag = regexp.MustCompile(`^\s*Relationship:\s*SPDXRef-(.*)\s*(DESCRIBES|CONTAINS|BUILD_TOOL_OF|RUNTIME_DEPENDENCY_OF)\s*SPDXRef-Package-(.*)\s*$`)
+ spdxLicenseTag = regexp.MustCompile(`^\s*##### Non-standard license:\s*$`)
+ spdxLicenseIDTag = regexp.MustCompile(`^\s*LicenseID: LicenseRef-(.*)\s*$`)
+ spdxExtractedTextTag = regexp.MustCompile(`^\s*ExtractedText:\s*<text>(.*)\s*$`)
+ spdxExtractedClosingTextTag = regexp.MustCompile(`^\s*</text>\s*$`)
+)
+
+func TestMain(m *testing.M) {
+ // Change into the parent directory before running the tests
+ // so they can find the testdata directory.
+ if err := os.Chdir(".."); err != nil {
+ fmt.Printf("failed to change to testdata directory: %s\n", err)
+ os.Exit(1)
+ }
+ os.Exit(m.Run())
+}
+
+func Test(t *testing.T) {
+ tests := []struct {
+ condition string
+ name string
+ outDir string
+ roots []string
+ stripPrefix string
+ expectedOut []matcher
+ expectedDeps []string
+ }{
+ {
+ condition: "firstparty",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/firstparty/highest.apex.meta_lic"},
+ packageName{"testdata/firstparty/highest.apex.meta_lic"},
+ spdxPkgID{"testdata/firstparty/highest.apex.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata-firstparty-highest.apex.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/firstparty/bin/bin1.meta_lic"},
+ packageName{"testdata/firstparty/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/firstparty/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/highest.apex.meta_lic ", "testdata/firstparty/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/bin/bin2.meta_lic"},
+ packageName{"testdata/firstparty/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/firstparty/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/highest.apex.meta_lic ", "testdata-firstparty-bin-bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/liba.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/highest.apex.meta_lic ", "testdata/firstparty/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/libb.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/highest.apex.meta_lic ", "testdata/firstparty/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/firstparty/bin/bin1.meta_lic ", "testdata/firstparty/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/libc.a.meta_lic"},
+ packageName{"testdata/firstparty/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata-firstparty-bin-bin1.meta_lic ", "testdata/firstparty/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/firstparty/lib/libb.so.meta_lic ", "testdata/firstparty/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/firstparty/lib/libd.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/lib/libd.so.meta_lic ", "testdata/firstparty/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ },
+ {
+ condition: "firstparty",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/firstparty/application.meta_lic"},
+ packageName{"testdata/firstparty/application.meta_lic"},
+ spdxPkgID{"testdata/firstparty/application.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/firstparty/application.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/firstparty/bin/bin3.meta_lic"},
+ packageName{"testdata/firstparty/bin/bin3.meta_lic"},
+ spdxPkgID{"testdata/firstparty/bin/bin3.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/bin/bin3.meta_lic ", "testdata-firstparty-application.meta_lic", "BUILD_TOOL_OF"},
+ packageTag{"testdata/firstparty/lib/liba.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/application.meta_lic ", "testdata/firstparty/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/libb.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/lib/libb.so.meta_lic ", "testdata-firstparty-application.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ },
+ {
+ condition: "firstparty",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/firstparty/container.zip.meta_lic"},
+ packageName{"testdata/firstparty/container.zip.meta_lic"},
+ spdxPkgID{"testdata/firstparty/container.zip.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/firstparty/container.zip.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/firstparty/bin/bin1.meta_lic"},
+ packageName{"testdata/firstparty/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/firstparty/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/container.zip.meta_lic ", "testdata/firstparty/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/bin/bin2.meta_lic"},
+ packageName{"testdata/firstparty/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/firstparty/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/container.zip.meta_lic ", "testdata/firstparty/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/liba.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/container.zip.meta_lic ", "testdata/firstparty/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/libb.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/container.zip.meta_lic ", "testdata/firstparty/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/firstparty/bin/bin1.meta_lic ", "testdata/firstparty/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/libc.a.meta_lic"},
+ packageName{"testdata/firstparty/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/bin/bin1.meta_lic ", "testdata/firstparty/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/firstparty/lib/libb.so.meta_lic ", "testdata/firstparty/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/firstparty/lib/libd.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/lib/libd.so.meta_lic ", "testdata/firstparty/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ },
+ {
+ condition: "firstparty",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/firstparty/bin/bin1.meta_lic"},
+ packageName{"testdata/firstparty/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/firstparty/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/firstparty/bin/bin1.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/firstparty/lib/liba.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/bin/bin1.meta_lic ", "testdata/firstparty/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/firstparty/lib/libc.a.meta_lic"},
+ packageName{"testdata/firstparty/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/firstparty/bin/bin1.meta_lic ", "testdata/firstparty/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ },
+ {
+ condition: "firstparty",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/firstparty/lib/libd.so.meta_lic"},
+ packageName{"testdata/firstparty/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/firstparty/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/firstparty/lib/libd.so.meta_lic", "DESCRIBES"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ },
+ {
+ condition: "notice",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/notice/highest.apex.meta_lic"},
+ packageName{"testdata/notice/highest.apex.meta_lic"},
+ spdxPkgID{"testdata/notice/highest.apex.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/notice/highest.apex.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/notice/bin/bin1.meta_lic"},
+ packageName{"testdata/notice/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/notice/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/highest.apex.meta_lic ", "testdata/notice/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/bin/bin2.meta_lic"},
+ packageName{"testdata/notice/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/notice/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/highest.apex.meta_lic ", "testdata/notice/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/liba.so.meta_lic"},
+ packageName{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/highest.apex.meta_lic ", "testdata/notice/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/libb.so.meta_lic"},
+ packageName{"testdata/notice/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/highest.apex.meta_lic ", "testdata/notice/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/notice/bin/bin1.meta_lic ", "testdata/notice/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/libc.a.meta_lic"},
+ packageName{"testdata/notice/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/bin/bin1.meta_lic ", "testdata/notice/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/notice/lib/libb.so.meta_lic ", "testdata/notice/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/notice/lib/libd.so.meta_lic"},
+ packageName{"testdata/notice/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/lib/libd.so.meta_lic ", "testdata/notice/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ },
+ },
+ {
+ condition: "notice",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/notice/container.zip.meta_lic"},
+ packageName{"testdata/notice/container.zip.meta_lic"},
+ spdxPkgID{"testdata/notice/container.zip.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/notice/container.zip.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/notice/bin/bin1.meta_lic"},
+ packageName{"testdata/notice/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/notice/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/container.zip.meta_lic ", "testdata/notice/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/bin/bin2.meta_lic"},
+ packageName{"testdata/notice/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/notice/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/container.zip.meta_lic ", "testdata/notice/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/liba.so.meta_lic"},
+ packageName{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/container.zip.meta_lic ", "testdata/notice/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/libb.so.meta_lic"},
+ packageName{"testdata/notice/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/container.zip.meta_lic ", "testdata/notice/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/notice/bin/bin1.meta_lic ", "testdata/notice/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/libc.a.meta_lic"},
+ packageName{"testdata/notice/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/bin/bin1.meta_lic ", "testdata/notice/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/notice/lib/libb.so.meta_lic ", "testdata/notice/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/notice/lib/libd.so.meta_lic"},
+ packageName{"testdata/notice/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/lib/libd.so.meta_lic ", "testdata/notice/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ },
+ },
+ {
+ condition: "notice",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/notice/application.meta_lic"},
+ packageName{"testdata/notice/application.meta_lic"},
+ spdxPkgID{"testdata/notice/application.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata-notice-application.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/notice/bin/bin3.meta_lic"},
+ packageName{"testdata/notice/bin/bin3.meta_lic"},
+ spdxPkgID{"testdata/notice/bin/bin3.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata-notice-bin-bin3.meta_lic ", "testdata/notice/application.meta_lic", "BUILD_TOOL_OF"},
+ packageTag{"testdata/notice/lib/liba.so.meta_lic"},
+ packageName{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/application.meta_lic ", "testdata-notice-lib-liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/libb.so.meta_lic"},
+ packageName{"testdata/notice/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata-notice-lib-libb.so.meta_lic ", "testdata/notice/application.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ },
+ },
+ {
+ condition: "notice",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/notice/bin/bin1.meta_lic"},
+ packageName{"testdata/notice/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/notice/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/notice/bin/bin1.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/notice/lib/liba.so.meta_lic"},
+ packageName{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/bin/bin1.meta_lic ", "testdata/notice/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/notice/lib/libc.a.meta_lic"},
+ packageName{"testdata/notice/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/notice/bin/bin1.meta_lic ", "testdata/notice/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ },
+ },
+ {
+ condition: "notice",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/notice/lib/libd.so.meta_lic"},
+ packageName{"testdata/notice/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/notice/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/notice/lib/libd.so.meta_lic", "DESCRIBES"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ },
+ {
+ condition: "reciprocal",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/reciprocal/highest.apex.meta_lic"},
+ packageName{"testdata/reciprocal/highest.apex.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/highest.apex.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/reciprocal/highest.apex.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/reciprocal/bin/bin1.meta_lic"},
+ packageName{"testdata/reciprocal/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/highest.apex.meta_lic ", "testdata-reciprocal-bin-bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/bin/bin2.meta_lic"},
+ packageName{"testdata/reciprocal/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/highest.apex.meta_lic ", "testdata-reciprocal-bin-bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/highest.apex.meta_lic ", "testdata/reciprocal/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/highest.apex.meta_lic ", "testdata/reciprocal/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/reciprocal/bin/bin1.meta_lic ", "testdata/reciprocal/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/bin/bin1.meta_lic ", "testdata/reciprocal/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/reciprocal/lib/libb.so.meta_lic ", "testdata/reciprocal/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/lib/libd.so.meta_lic ", "testdata/reciprocal/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/reciprocal/container.zip.meta_lic"},
+ packageName{"testdata/reciprocal/container.zip.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/container.zip.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/reciprocal/container.zip.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/reciprocal/bin/bin1.meta_lic"},
+ packageName{"testdata/reciprocal/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/container.zip.meta_lic ", "testdata-reciprocal-bin-bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/bin/bin2.meta_lic"},
+ packageName{"testdata/reciprocal/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/container.zip.meta_lic ", "testdata-reciprocal-bin-bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/container.zip.meta_lic ", "testdata/reciprocal/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/container.zip.meta_lic ", "testdata/reciprocal/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/reciprocal/bin/bin1.meta_lic ", "testdata/reciprocal/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/bin/bin1.meta_lic ", "testdata/reciprocal/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/reciprocal/lib/libb.so.meta_lic ", "testdata/reciprocal/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/lib/libd.so.meta_lic ", "testdata/reciprocal/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/reciprocal/application.meta_lic"},
+ packageName{"testdata/reciprocal/application.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/application.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/reciprocal/application.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/reciprocal/bin/bin3.meta_lic"},
+ packageName{"testdata/reciprocal/bin/bin3.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/bin/bin3.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata-reciprocal-bin-bin3.meta_lic ", "testdata/reciprocal/application.meta_lic", "BUILD_TOOL_OF"},
+ packageTag{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/application.meta_lic ", "testdata/reciprocal/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/lib/libb.so.meta_lic ", "testdata/reciprocal/application.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/reciprocal/bin/bin1.meta_lic"},
+ packageName{"testdata/reciprocal/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/reciprocal/bin/bin1.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/bin/bin1.meta_lic ", "testdata/reciprocal/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/reciprocal/bin/bin1.meta_lic ", "testdata/reciprocal/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ },
+ },
+ {
+ condition: "reciprocal",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ packageName{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/reciprocal/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/reciprocal/lib/libd.so.meta_lic", "DESCRIBES"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ stripPrefix: "out/target/product/fictional/system/apex/",
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/restricted/highest.apex.meta_lic"},
+ packageName{"testdata/restricted/highest.apex.meta_lic"},
+ spdxPkgID{"testdata/restricted/highest.apex.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/restricted/highest.apex.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/restricted/bin/bin1.meta_lic"},
+ packageName{"testdata/restricted/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/restricted/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/highest.apex.meta_lic ", "testdata/restricted/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/bin/bin2.meta_lic"},
+ packageName{"testdata/restricted/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/restricted/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/highest.apex.meta_lic ", "testdata/restricted/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/liba.so.meta_lic"},
+ packageName{"testdata/restricted/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/highest.apex.meta_lic ", "testdata/restricted/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/libb.so.meta_lic"},
+ packageName{"testdata/restricted/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/highest.apex.meta_lic ", "testdata/restricted/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/restricted/bin/bin1.meta_lic ", "testdata/restricted/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/libc.a.meta_lic"},
+ packageName{"testdata/restricted/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/bin/bin1.meta_lic ", "testdata/restricted/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/restricted/lib/libb.so.meta_lic ", "testdata/restricted/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/restricted/lib/libd.so.meta_lic"},
+ packageName{"testdata/restricted/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/lib/libd.so.meta_lic ", "testdata/restricted/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxExtractedText{"###Restricted License###"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/restricted/RESTRICTED_LICENSE",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ stripPrefix: "out/target/product/fictional/system/apex/",
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/restricted/container.zip.meta_lic"},
+ packageName{"testdata/restricted/container.zip.meta_lic"},
+ spdxPkgID{"testdata/restricted/container.zip.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/restricted/container.zip.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/restricted/bin/bin1.meta_lic"},
+ packageName{"testdata/restricted/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/restricted/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/container.zip.meta_lic ", "testdata/restricted/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/bin/bin2.meta_lic"},
+ packageName{"testdata/restricted/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/restricted/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/container.zip.meta_lic ", "testdata/restricted/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/liba.so.meta_lic"},
+ packageName{"testdata/restricted/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/container.zip.meta_lic ", "testdata/restricted/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/libb.so.meta_lic"},
+ packageName{"testdata/restricted/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/container.zip.meta_lic ", "testdata/restricted/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/restricted/bin/bin1.meta_lic ", "testdata/restricted/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/libc.a.meta_lic"},
+ packageName{"testdata/restricted/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/bin/bin1.meta_lic ", "testdata/restricted/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/restricted/lib/libb.so.meta_lic ", "testdata/restricted/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/restricted/lib/libd.so.meta_lic"},
+ packageName{"testdata/restricted/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/lib/libd.so.meta_lic ", "testdata/restricted/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxExtractedText{"###Restricted License###"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/restricted/RESTRICTED_LICENSE",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/restricted/bin/bin1.meta_lic"},
+ packageName{"testdata/restricted/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/restricted/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/restricted/bin/bin1.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/restricted/lib/liba.so.meta_lic"},
+ packageName{"testdata/restricted/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/bin/bin1.meta_lic ", "testdata/restricted/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/restricted/lib/libc.a.meta_lic"},
+ packageName{"testdata/restricted/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxRelationship{"Package-testdata/restricted/bin/bin1.meta_lic ", "testdata/restricted/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-reciprocal-RECIPROCAL_LICENSE"},
+ spdxExtractedText{"$$$Reciprocal License$$$"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxExtractedText{"###Restricted License###"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/restricted/RESTRICTED_LICENSE",
+ },
+ },
+ {
+ condition: "restricted",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/restricted/lib/libd.so.meta_lic"},
+ packageName{"testdata/restricted/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/restricted/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/restricted/lib/libd.so.meta_lic", "DESCRIBES"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ },
+ {
+ condition: "proprietary",
+ name: "apex",
+ roots: []string{"highest.apex.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/proprietary/highest.apex.meta_lic"},
+ packageName{"testdata/proprietary/highest.apex.meta_lic"},
+ spdxPkgID{"testdata/proprietary/highest.apex.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/proprietary/highest.apex.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/proprietary/bin/bin1.meta_lic"},
+ packageName{"testdata/proprietary/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/proprietary/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/highest.apex.meta_lic ", "testdata/proprietary/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/bin/bin2.meta_lic"},
+ packageName{"testdata/proprietary/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/proprietary/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/highest.apex.meta_lic ", "testdata/proprietary/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/liba.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/highest.apex.meta_lic ", "testdata/proprietary/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/libb.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/highest.apex.meta_lic ", "testdata/proprietary/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin1.meta_lic ", "testdata/proprietary/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/libc.a.meta_lic"},
+ packageName{"testdata/proprietary/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin1.meta_lic ", "testdata/proprietary/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata-proprietary-lib-libb.so.meta_lic ", "testdata/proprietary/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/proprietary/lib/libd.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata-proprietary-lib-libd.so.meta_lic ", "testdata/proprietary/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxExtractedText{"@@@Proprietary License@@@"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxExtractedText{"###Restricted License###"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/restricted/RESTRICTED_LICENSE",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "container",
+ roots: []string{"container.zip.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/proprietary/container.zip.meta_lic"},
+ packageName{"testdata/proprietary/container.zip.meta_lic"},
+ spdxPkgID{"testdata/proprietary/container.zip.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/proprietary/container.zip.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/proprietary/bin/bin1.meta_lic"},
+ packageName{"testdata/proprietary/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/proprietary/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/container.zip.meta_lic ", "testdata/proprietary/bin/bin1.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/bin/bin2.meta_lic"},
+ packageName{"testdata/proprietary/bin/bin2.meta_lic"},
+ spdxPkgID{"testdata/proprietary/bin/bin2.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/container.zip.meta_lic ", "testdata/proprietary/bin/bin2.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/liba.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/container.zip.meta_lic ", "testdata/proprietary/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/libb.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/container.zip.meta_lic ", "testdata/proprietary/lib/libb.so.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin1.meta_lic ", "testdata/proprietary/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/libc.a.meta_lic"},
+ packageName{"testdata/proprietary/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin1.meta_lic ", "testdata/proprietary/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxRelationship{"Package-testdata-proprietary-lib-libb.so.meta_lic ", "testdata/proprietary/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ packageTag{"testdata/proprietary/lib/libd.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"Package-testdata-proprietary-lib-libd.so.meta_lic ", "testdata/proprietary/bin/bin2.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxExtractedText{"@@@Proprietary License@@@"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxExtractedText{"###Restricted License###"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/restricted/RESTRICTED_LICENSE",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "application",
+ roots: []string{"application.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/proprietary/application.meta_lic"},
+ packageName{"testdata/proprietary/application.meta_lic"},
+ spdxPkgID{"testdata/proprietary/application.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/proprietary/application.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/proprietary/bin/bin3.meta_lic"},
+ packageName{"testdata/proprietary/bin/bin3.meta_lic"},
+ spdxPkgID{"testdata/proprietary/bin/bin3.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin3.meta_lic ", "testdata/proprietary/application.meta_lic", "BUILD_TOOL_OF"},
+ packageTag{"testdata/proprietary/lib/liba.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/application.meta_lic ", "testdata/proprietary/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/libb.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/libb.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libb.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/lib/libb.so.meta_lic ", "testdata/proprietary/application.meta_lic", "RUNTIME_DEPENDENCY_OF"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxExtractedText{"@@@Proprietary License@@@"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-restricted-RESTRICTED_LICENSE"},
+ spdxExtractedText{"###Restricted License###"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/restricted/RESTRICTED_LICENSE",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "binary",
+ roots: []string{"bin/bin1.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/proprietary/bin/bin1.meta_lic"},
+ packageName{"testdata/proprietary/bin/bin1.meta_lic"},
+ spdxPkgID{"testdata/proprietary/bin/bin1.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/proprietary/bin/bin1.meta_lic", "DESCRIBES"},
+ packageTag{"testdata/proprietary/lib/liba.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/liba.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin1.meta_lic ", "testdata/proprietary/lib/liba.so.meta_lic", "CONTAINS"},
+ packageTag{"testdata/proprietary/lib/libc.a.meta_lic"},
+ packageName{"testdata/proprietary/lib/libc.a.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libc.a.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxRelationship{"Package-testdata/proprietary/bin/bin1.meta_lic ", "testdata/proprietary/lib/libc.a.meta_lic", "CONTAINS"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-firstparty-FIRST_PARTY_LICENSE"},
+ spdxExtractedText{"&&&First Party License&&&"},
+ spdxExtractedClosingText{},
+ spdxLicenseID{"testdata-proprietary-PROPRIETARY_LICENSE"},
+ spdxExtractedText{"@@@Proprietary License@@@"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/proprietary/PROPRIETARY_LICENSE",
+ },
+ },
+ {
+ condition: "proprietary",
+ name: "library",
+ roots: []string{"lib/libd.so.meta_lic"},
+ expectedOut: []matcher{
+ spdxVersion{},
+ spdxDataLicense{},
+ spdxDocumentName{"Android"},
+ spdxID{"Android"},
+ spdxDocumentNameSpace{},
+ spdxCreatorOrganization{},
+ spdxCreatedTime{},
+ packageTag{"testdata/proprietary/lib/libd.so.meta_lic"},
+ packageName{"testdata/proprietary/lib/libd.so.meta_lic"},
+ spdxPkgID{"testdata/proprietary/lib/libd.so.meta_lic"},
+ spdxPkgDownloadLocation{"NOASSERTION"},
+ spdxPkgLicenseDeclared{"testdata-notice-NOTICE_LICENSE"},
+ spdxRelationship{"DOCUMENT-Android ", "testdata/proprietary/lib/libd.so.meta_lic", "DESCRIBES"},
+ spdxLicense{},
+ spdxLicenseID{"testdata-notice-NOTICE_LICENSE"},
+ spdxExtractedText{"%%%Notice License%%%"},
+ spdxExtractedClosingText{},
+ },
+ expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ rootFiles := make([]string, 0, len(tt.roots))
+ for _, r := range tt.roots {
+ rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
+ }
+
+ ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "Android", []string{tt.stripPrefix}, fakeTime}
+
+ deps, err := sbomGenerator(&ctx, rootFiles...)
+ if err != nil {
+ t.Fatalf("sbom: error = %v, stderr = %v", err, stderr)
+ return
+ }
+ if stderr.Len() > 0 {
+ t.Errorf("sbom: gotStderr = %v, want none", stderr)
+ }
+
+ t.Logf("got stdout: %s", stdout.String())
+
+ t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
+
+ out := bufio.NewScanner(stdout)
+ lineno := 0
+ for out.Scan() {
+ line := out.Text()
+ if strings.TrimLeft(line, " ") == "" {
+ continue
+ }
+ if len(tt.expectedOut) <= lineno {
+ t.Errorf("sbom: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
+ } else if !tt.expectedOut[lineno].isMatch(line) {
+ t.Errorf("sbom: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno])
+ }
+ lineno++
+ }
+ for ; lineno < len(tt.expectedOut); lineno++ {
+ t.Errorf("bom: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno])
+ }
+
+ t.Logf("got deps: %q", deps)
+
+ t.Logf("want deps: %q", tt.expectedDeps)
+
+ if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
+ t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
+ strings.Join(w, "\n"), strings.Join(g, "\n"))
+ }
+ })
+ }
+}
+
+type matcher interface {
+ isMatch(line string) bool
+ String() string
+}
+
+type packageTag struct {
+ name string
+}
+
+func (m packageTag) isMatch(line string) bool {
+ groups := spdxPackageTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == m.name
+}
+
+func (m packageTag) String() string {
+ return "##### Package: " + m.name
+}
+
+type packageName struct {
+ name string
+}
+
+func (m packageName) isMatch(line string) bool {
+ groups := spdxPackageNameTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.name)
+}
+
+func (m packageName) String() string {
+ return "PackageName: " + replaceSlashes(m.name)
+}
+
+type spdxID struct {
+ name string
+}
+
+func (m spdxID) isMatch(line string) bool {
+ groups := spdxIDTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.name)
+}
+
+func (m spdxID) String() string {
+ return "SPDXID: SPDXRef-DOCUMENT-" + replaceSlashes(m.name)
+}
+
+type spdxPkgID struct {
+ name string
+}
+
+func (m spdxPkgID) isMatch(line string) bool {
+ groups := spdxPkgIDTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.name)
+}
+
+func (m spdxPkgID) String() string {
+ return "SPDXID: SPDXRef-Package-" + replaceSlashes(m.name)
+}
+
+type spdxVersion struct{}
+
+func (m spdxVersion) isMatch(line string) bool {
+ return spdxVersionTag.MatchString(line)
+}
+
+func (m spdxVersion) String() string {
+ return "SPDXVersion: SPDX-2.2"
+}
+
+type spdxDataLicense struct{}
+
+func (m spdxDataLicense) isMatch(line string) bool {
+ return spdxDataLicenseTag.MatchString(line)
+}
+
+func (m spdxDataLicense) String() string {
+ return "DataLicense: CC-1.0"
+}
+
+type spdxDocumentName struct {
+ name string
+}
+
+func (m spdxDocumentName) isMatch(line string) bool {
+ return spdxDocumentNameTag.MatchString(line)
+}
+
+func (m spdxDocumentName) String() string {
+ return "DocumentName: " + m.name
+}
+
+type spdxDocumentNameSpace struct {
+ name string
+}
+
+func (m spdxDocumentNameSpace) isMatch(line string) bool {
+ return spdxDocumentNameSpaceTag.MatchString(line)
+}
+
+func (m spdxDocumentNameSpace) String() string {
+ return "DocumentNameSpace: Android"
+}
+
+type spdxCreatorOrganization struct{}
+
+func (m spdxCreatorOrganization) isMatch(line string) bool {
+ return spdxCreatorOrganizationTag.MatchString(line)
+}
+
+func (m spdxCreatorOrganization) String() string {
+ return "Creator: Organization: Google LLC"
+}
+
+func fakeTime() time.Time {
+ return time.UnixMicro(0).UTC()
+}
+
+type spdxCreatedTime struct{}
+
+func (m spdxCreatedTime) isMatch(line string) bool {
+ return spdxCreatedTimeTag.MatchString(line)
+}
+
+func (m spdxCreatedTime) String() string {
+ return "Created: 1970-01-01T00:00:00Z"
+}
+
+type spdxPkgDownloadLocation struct {
+ name string
+}
+
+func (m spdxPkgDownloadLocation) isMatch(line string) bool {
+ return spdxPkgDownloadLocationTag.MatchString(line)
+}
+
+func (m spdxPkgDownloadLocation) String() string {
+ return "PackageDownloadLocation: " + m.name
+}
+
+type spdxPkgLicenseDeclared struct {
+ name string
+}
+
+func (m spdxPkgLicenseDeclared) isMatch(line string) bool {
+ groups := spdxPkgLicenseDeclaredTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.name)
+}
+
+func (m spdxPkgLicenseDeclared) String() string {
+ return "PackageLicenseConcluded: LicenseRef-" + m.name
+}
+
+type spdxRelationship struct {
+ pkg1 string
+ pkg2 string
+ relation string
+}
+
+func (m spdxRelationship) isMatch(line string) bool {
+ groups := spdxRelationshipTag.FindStringSubmatch(line)
+ if len(groups) != 4 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.pkg1) && groups[2] == m.relation && groups[3] == replaceSlashes(m.pkg2)
+}
+
+func (m spdxRelationship) String() string {
+ return "Relationship: SPDXRef-" + replaceSlashes(m.pkg1) + " " + m.relation + " SPDXRef-Package-" + replaceSlashes(m.pkg2)
+}
+
+type spdxLicense struct{}
+
+func (m spdxLicense) isMatch(line string) bool {
+ return spdxLicenseTag.MatchString(line)
+}
+
+func (m spdxLicense) String() string {
+ return "##### Non-standard license:"
+}
+
+type spdxLicenseID struct {
+ name string
+}
+
+func (m spdxLicenseID) isMatch(line string) bool {
+ groups := spdxLicenseIDTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.name)
+}
+
+func (m spdxLicenseID) String() string {
+ return "LicenseID: LicenseRef-" + m.name
+}
+
+type spdxExtractedText struct {
+ name string
+}
+
+func (m spdxExtractedText) isMatch(line string) bool {
+ groups := spdxExtractedTextTag.FindStringSubmatch(line)
+ if len(groups) != 2 {
+ return false
+ }
+ return groups[1] == replaceSlashes(m.name)
+}
+
+func (m spdxExtractedText) String() string {
+ return "ExtractedText: <text>" + m.name
+}
+
+type spdxExtractedClosingText struct{}
+
+func (m spdxExtractedClosingText) isMatch(line string) bool {
+ return spdxExtractedClosingTextTag.MatchString(line)
+}
+
+func (m spdxExtractedClosingText) String() string {
+ return "</text>"
+}
+
+type matcherList []matcher
+
+func (l matcherList) String() string {
+ var sb strings.Builder
+ for _, m := range l {
+ s := m.String()
+ fmt.Fprintf(&sb, "%s\n", s)
+ }
+ return sb.String()
+}
diff --git a/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
index 7ef14e9..a7c3d01 100644
--- a/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
+++ b/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
@@ -2,7 +2,7 @@
module_classes: "EXECUTABLES"
projects: "standalone/binary"
license_kinds: "SPDX-license-identifier-LGPL-2.0"
-license_conditions: "restricted"
+license_conditions: "restricted_allows_dynamic_linking"
license_texts: "testdata/restricted/RESTRICTED_LICENSE"
is_container: false
built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
diff --git a/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
index 7ef14e9..a7c3d01 100644
--- a/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
+++ b/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
@@ -2,7 +2,7 @@
module_classes: "EXECUTABLES"
projects: "standalone/binary"
license_kinds: "SPDX-license-identifier-LGPL-2.0"
-license_conditions: "restricted"
+license_conditions: "restricted_allows_dynamic_linking"
license_texts: "testdata/restricted/RESTRICTED_LICENSE"
is_container: false
built: "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
diff --git a/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic b/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
index a505d4a..101ca19 100644
--- a/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
+++ b/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
@@ -1,7 +1,7 @@
package_name: "Device"
projects: "device/library"
license_kinds: "SPDX-license-identifier-LGPL-2.0"
-license_conditions: "restricted"
+license_conditions: "restricted_allows_dynamic_linking"
license_texts: "testdata/restricted/RESTRICTED_LICENSE"
is_container: false
built: "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
diff --git a/tools/compliance/cmd/textnotice/textnotice.go b/tools/compliance/cmd/textnotice/textnotice.go
index 9beaf58..450290c 100644
--- a/tools/compliance/cmd/textnotice/textnotice.go
+++ b/tools/compliance/cmd/textnotice/textnotice.go
@@ -23,6 +23,7 @@
"io/fs"
"os"
"path/filepath"
+ "sort"
"strings"
"android/soong/response"
@@ -230,7 +231,8 @@
fmt.Fprintln(ctx.stdout)
}
- *ctx.deps = ni.InputNoticeFiles()
+ *ctx.deps = ni.InputFiles()
+ sort.Strings(*ctx.deps)
return nil
}
diff --git a/tools/compliance/cmd/textnotice/textnotice_test.go b/tools/compliance/cmd/textnotice/textnotice_test.go
index e661a44..a902313 100644
--- a/tools/compliance/cmd/textnotice/textnotice_test.go
+++ b/tools/compliance/cmd/textnotice/textnotice_test.go
@@ -65,7 +65,16 @@
usedBy{"highest.apex/lib/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -81,7 +90,16 @@
usedBy{"container.zip/libb.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -93,7 +111,13 @@
usedBy{"application"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/application.meta_lic",
+ "testdata/firstparty/bin/bin3.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -105,7 +129,12 @@
usedBy{"bin/bin1"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -117,7 +146,10 @@
usedBy{"lib/libd.so"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "notice",
@@ -142,6 +174,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
},
},
{
@@ -167,6 +206,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
},
},
{
@@ -186,6 +232,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/application.meta_lic",
+ "testdata/notice/bin/bin3.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
},
},
{
@@ -207,6 +257,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
},
},
{
@@ -219,7 +272,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/lib/libd.so.meta_lic",
+ },
},
{
condition: "reciprocal",
@@ -244,6 +300,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -269,6 +332,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -288,6 +358,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/application.meta_lic",
+ "testdata/reciprocal/bin/bin3.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
},
},
{
@@ -309,6 +383,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
},
},
{
@@ -323,6 +400,7 @@
},
expectedDeps: []string{
"testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -353,6 +431,13 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
},
},
{
@@ -383,6 +468,13 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
},
},
{
@@ -402,6 +494,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/bin/bin3.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
},
},
{
@@ -426,6 +522,9 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
},
},
{
@@ -438,7 +537,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ },
},
{
condition: "proprietary",
@@ -468,6 +570,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
"testdata/restricted/RESTRICTED_LICENSE",
},
},
@@ -499,6 +608,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
"testdata/restricted/RESTRICTED_LICENSE",
},
},
@@ -519,6 +635,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/bin/bin3.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
},
},
{
@@ -540,6 +660,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
},
},
{
@@ -552,7 +675,10 @@
usedBy{"lib/libd.so"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ },
},
}
for _, tt := range tests {
diff --git a/tools/compliance/cmd/xmlnotice/xmlnotice.go b/tools/compliance/cmd/xmlnotice/xmlnotice.go
index 2097b7c..c3f8e4c 100644
--- a/tools/compliance/cmd/xmlnotice/xmlnotice.go
+++ b/tools/compliance/cmd/xmlnotice/xmlnotice.go
@@ -24,6 +24,7 @@
"io/fs"
"os"
"path/filepath"
+ "sort"
"strings"
"android/soong/response"
@@ -238,7 +239,8 @@
}
fmt.Fprintln(ctx.stdout, "</licenses>")
- *ctx.deps = ni.InputNoticeFiles()
+ *ctx.deps = ni.InputFiles()
+ sort.Strings(*ctx.deps)
return nil
}
diff --git a/tools/compliance/cmd/xmlnotice/xmlnotice_test.go b/tools/compliance/cmd/xmlnotice/xmlnotice_test.go
index 731e783..551006f 100644
--- a/tools/compliance/cmd/xmlnotice/xmlnotice_test.go
+++ b/tools/compliance/cmd/xmlnotice/xmlnotice_test.go
@@ -65,7 +65,16 @@
target{"highest.apex/lib/libb.so", "Android"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/highest.apex.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -79,7 +88,16 @@
target{"container.zip/libb.so", "Android"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/bin/bin2.meta_lic",
+ "testdata/firstparty/container.zip.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -89,7 +107,13 @@
target{"application", "Android"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/application.meta_lic",
+ "testdata/firstparty/bin/bin3.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libb.so.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -99,7 +123,12 @@
target{"bin/bin1", "Android"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/bin/bin1.meta_lic",
+ "testdata/firstparty/lib/liba.so.meta_lic",
+ "testdata/firstparty/lib/libc.a.meta_lic",
+ },
},
{
condition: "firstparty",
@@ -109,7 +138,10 @@
target{"lib/libd.so", "Android"},
firstParty{},
},
- expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
+ expectedDeps: []string{
+ "testdata/firstparty/FIRST_PARTY_LICENSE",
+ "testdata/firstparty/lib/libd.so.meta_lic",
+ },
},
{
condition: "notice",
@@ -129,6 +161,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/highest.apex.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
},
},
{
@@ -149,6 +188,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/bin/bin2.meta_lic",
+ "testdata/notice/container.zip.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
+ "testdata/notice/lib/libd.so.meta_lic",
},
},
{
@@ -164,6 +210,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/application.meta_lic",
+ "testdata/notice/bin/bin3.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libb.so.meta_lic",
},
},
{
@@ -180,6 +230,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/bin/bin1.meta_lic",
+ "testdata/notice/lib/liba.so.meta_lic",
+ "testdata/notice/lib/libc.a.meta_lic",
},
},
{
@@ -190,7 +243,10 @@
target{"lib/libd.so", "External"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/notice/lib/libd.so.meta_lic",
+ },
},
{
condition: "reciprocal",
@@ -210,6 +266,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/highest.apex.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -230,6 +293,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/bin/bin2.meta_lic",
+ "testdata/reciprocal/container.zip.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
},
},
{
@@ -245,6 +315,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/application.meta_lic",
+ "testdata/reciprocal/bin/bin3.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libb.so.meta_lic",
},
},
{
@@ -261,6 +335,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
+ "testdata/reciprocal/bin/bin1.meta_lic",
+ "testdata/reciprocal/lib/liba.so.meta_lic",
+ "testdata/reciprocal/lib/libc.a.meta_lic",
},
},
{
@@ -271,7 +348,10 @@
target{"lib/libd.so", "External"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/reciprocal/lib/libd.so.meta_lic",
+ },
},
{
condition: "restricted",
@@ -294,6 +374,13 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/highest.apex.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
},
},
{
@@ -317,6 +404,13 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/bin/bin2.meta_lic",
+ "testdata/restricted/container.zip.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
+ "testdata/restricted/lib/libd.so.meta_lic",
},
},
{
@@ -332,6 +426,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/application.meta_lic",
+ "testdata/restricted/bin/bin3.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libb.so.meta_lic",
},
},
{
@@ -350,6 +448,9 @@
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/reciprocal/RECIPROCAL_LICENSE",
"testdata/restricted/RESTRICTED_LICENSE",
+ "testdata/restricted/bin/bin1.meta_lic",
+ "testdata/restricted/lib/liba.so.meta_lic",
+ "testdata/restricted/lib/libc.a.meta_lic",
},
},
{
@@ -360,7 +461,10 @@
target{"lib/libd.so", "External"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/restricted/lib/libd.so.meta_lic",
+ },
},
{
condition: "proprietary",
@@ -382,6 +486,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/highest.apex.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
"testdata/restricted/RESTRICTED_LICENSE",
},
},
@@ -405,6 +516,13 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/bin/bin2.meta_lic",
+ "testdata/proprietary/container.zip.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
+ "testdata/proprietary/lib/libd.so.meta_lic",
"testdata/restricted/RESTRICTED_LICENSE",
},
},
@@ -421,6 +539,10 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/application.meta_lic",
+ "testdata/proprietary/bin/bin3.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libb.so.meta_lic",
},
},
{
@@ -437,6 +559,9 @@
expectedDeps: []string{
"testdata/firstparty/FIRST_PARTY_LICENSE",
"testdata/proprietary/PROPRIETARY_LICENSE",
+ "testdata/proprietary/bin/bin1.meta_lic",
+ "testdata/proprietary/lib/liba.so.meta_lic",
+ "testdata/proprietary/lib/libc.a.meta_lic",
},
},
{
@@ -447,7 +572,10 @@
target{"lib/libd.so", "External"},
notice{},
},
- expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
+ expectedDeps: []string{
+ "testdata/notice/NOTICE_LICENSE",
+ "testdata/proprietary/lib/libd.so.meta_lic",
+ },
},
}
for _, tt := range tests {
diff --git a/tools/compliance/doc.go b/tools/compliance/doc.go
index a47c1cf..5ced9ee 100644
--- a/tools/compliance/doc.go
+++ b/tools/compliance/doc.go
@@ -11,6 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+
+// Much of this content appears too in README.md
+// When changing this file consider whether the change also applies to README.md
+
/*
Package compliance provides an approved means for reading, consuming, and
@@ -31,6 +35,13 @@
artifacts in a release or distribution. While conceptually immutable, parts of
the graph may be loaded or evaluated lazily.
+Conceptually, the graph itself will always be a directed acyclic graph. One
+representation is a set of directed edges. Another is a set of nodes with
+directed edges to their dependencies.
+
+The edges have annotations, which can distinguish between build tools, runtime
+dependencies, and dependencies like 'contains' that make a derivative work.
+
LicenseCondition
----------------
@@ -51,17 +62,13 @@
`ActsOn` is the target to share, give notice for, hide etc.
-`Resolves` is the license condition that the action resolves.
+`Resolves` is the set of condition types 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.
+For most condition types, `ActsOn` will be the target where the condition
+originated. For example, a notice condition policy means attribution or notice
+must be given for the target where the condition originates. Likewise, a
+proprietary condition policy means the privacy of the target where the
+condition originates must be respected. i.e. The thing acted on is the origin.
Restricted conditions are different. The infectious nature of restricted often
means sharing code that is not the target where the restricted condition
diff --git a/tools/compliance/graph.go b/tools/compliance/graph.go
index e73ab46..80a2f47 100644
--- a/tools/compliance/graph.go
+++ b/tools/compliance/graph.go
@@ -58,13 +58,11 @@
/// (guarded by mu)
targets map[string]*TargetNode
- // wgBU becomes non-nil when the bottom-up resolve begins and reaches 0
- // (i.e. Wait() proceeds) when the bottom-up resolve completes. (guarded by mu)
- wgBU *sync.WaitGroup
+ // onceBottomUp makes sure the bottom-up resolve walk only happens one time.
+ onceBottomUp sync.Once
- // wgTD becomes non-nil when the top-down resolve begins and reaches 0 (i.e. Wait()
- // proceeds) when the top-down resolve completes. (guarded by mu)
- wgTD *sync.WaitGroup
+ // onceTopDown makes sure the top-down resolve walk only happens one time.
+ onceTopDown sync.Once
// shippedNodes caches the results of a full walk of nodes identifying targets
// distributed either directly or as derivative works. (creation guarded by mu)
@@ -139,6 +137,24 @@
return e.annotations
}
+// IsRuntimeDependency returns true for edges representing shared libraries
+// linked dynamically at runtime.
+func (e *TargetEdge) IsRuntimeDependency() bool {
+ return edgeIsDynamicLink(e)
+}
+
+// IsDerivation returns true for edges where the target is a derivative
+// work of dependency.
+func (e *TargetEdge) IsDerivation() bool {
+ return edgeIsDerivation(e)
+}
+
+// IsBuildTool returns true for edges where the target is built
+// by dependency.
+func (e *TargetEdge) IsBuildTool() bool {
+ return !edgeIsDerivation(e) && !edgeIsDynamicLink(e)
+}
+
// String returns a human-readable string representation of the edge.
func (e *TargetEdge) String() string {
return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
@@ -188,6 +204,11 @@
return s.edge.dependency
}
+// Edge describes the target edge.
+func (s TargetEdgePathSegment) Edge() *TargetEdge {
+ return s.edge
+}
+
// Annotations describes the type of edge by the set of annotations attached to
// it.
//
@@ -300,21 +321,9 @@
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...)
+// ModuleName returns the module name of the target.
+func (tn *TargetNode) ModuleName() string {
+ return tn.proto.GetModuleName()
}
// Projects returns the projects defining the target node. (unordered)
@@ -326,14 +335,6 @@
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)
diff --git a/tools/compliance/noticeindex.go b/tools/compliance/noticeindex.go
index 86d42ac..dbfede6 100644
--- a/tools/compliance/noticeindex.go
+++ b/tools/compliance/noticeindex.go
@@ -15,7 +15,6 @@
package compliance
import (
- "bufio"
"crypto/md5"
"fmt"
"io"
@@ -25,16 +24,11 @@
"regexp"
"sort"
"strings"
-)
-const (
- noProjectName = "\u2205"
+ "android/soong/tools/compliance/projectmetadata"
)
var (
- nameRegexp = regexp.MustCompile(`^\s*name\s*:\s*"(.*)"\s*$`)
- descRegexp = regexp.MustCompile(`^\s*description\s*:\s*"(.*)"\s*$`)
- versionRegexp = regexp.MustCompile(`^\s*version\s*:\s*"(.*)"\s*$`)
licensesPathRegexp = regexp.MustCompile(`licen[cs]es?/`)
)
@@ -43,6 +37,8 @@
type NoticeIndex struct {
// lg identifies the license graph to which the index applies.
lg *LicenseGraph
+ // pmix indexes project metadata
+ pmix *projectmetadata.Index
// rs identifies the set of resolutions upon which the index is based.
rs ResolutionSet
// shipped identifies the set of target nodes shipped directly or as derivative works.
@@ -75,6 +71,7 @@
}
ni := &NoticeIndex{
lg: lg,
+ pmix: projectmetadata.NewIndex(rootFS),
rs: rs,
shipped: ShippedNodes(lg),
rootFS: rootFS,
@@ -110,9 +107,12 @@
return hashes, nil
}
- link := func(tn *TargetNode, hashes map[hash]struct{}, installPaths []string) {
+ link := func(tn *TargetNode, hashes map[hash]struct{}, installPaths []string) error {
for h := range hashes {
- libName := ni.getLibName(tn, h)
+ libName, err := ni.getLibName(tn, h)
+ if err != nil {
+ return err
+ }
if _, ok := ni.libHash[libName]; !ok {
ni.libHash[libName] = make(map[hash]struct{})
}
@@ -145,6 +145,11 @@
}
}
}
+ return nil
+ }
+
+ cacheMetadata := func(tn *TargetNode) {
+ ni.pmix.MetadataForProjects(tn.Projects()...)
}
// returns error from walk below.
@@ -157,13 +162,17 @@
if !ni.shipped.Contains(tn) {
return false
}
+ go cacheMetadata(tn)
installPaths := getInstallPaths(tn, path)
var hashes map[hash]struct{}
hashes, err = index(tn)
if err != nil {
return false
}
- link(tn, hashes, installPaths)
+ err = link(tn, hashes, installPaths)
+ if err != nil {
+ return false
+ }
if tn.IsContainer() {
return true
}
@@ -173,7 +182,10 @@
if err != nil {
return false
}
- link(r.actsOn, hashes, installPaths)
+ err = link(r.actsOn, hashes, installPaths)
+ if err != nil {
+ return false
+ }
}
return false
})
@@ -214,12 +226,18 @@
close(c)
}()
return c
+
}
-// InputNoticeFiles returns the list of files that were hashed during IndexLicenseTexts.
-func (ni *NoticeIndex) InputNoticeFiles() []string {
- files := append([]string(nil), ni.files...)
- sort.Strings(files)
+// InputFiles returns the complete list of files read during indexing.
+func (ni *NoticeIndex) InputFiles() []string {
+ projectMeta := ni.pmix.AllMetadataFiles()
+ files := make([]string, 0, len(ni.files) + len(ni.lg.targets) + len(projectMeta))
+ files = append(files, ni.files...)
+ for f := range ni.lg.targets {
+ files = append(files, f)
+ }
+ files = append(files, projectMeta...)
return files
}
@@ -308,15 +326,18 @@
}
// getLibName returns the name of the library associated with `noticeFor`.
-func (ni *NoticeIndex) getLibName(noticeFor *TargetNode, h hash) string {
+func (ni *NoticeIndex) getLibName(noticeFor *TargetNode, h hash) (string, error) {
for _, text := range noticeFor.LicenseTexts() {
if !strings.Contains(text, ":") {
if ni.hash[text].key != h.key {
continue
}
- ln := ni.checkMetadataForLicenseText(noticeFor, text)
+ ln, err := ni.checkMetadataForLicenseText(noticeFor, text)
+ if err != nil {
+ return "", err
+ }
if len(ln) > 0 {
- return ln
+ return ln, nil
}
continue
}
@@ -331,17 +352,20 @@
if err != nil {
continue
}
- return ln
+ return ln, nil
}
// use name from METADATA if available
- ln := ni.checkMetadata(noticeFor)
+ ln, err := ni.checkMetadata(noticeFor)
+ if err != nil {
+ return "", err
+ }
if len(ln) > 0 {
- return ln
+ return ln, nil
}
// use package_name: from license{} module if available
pn := noticeFor.PackageName()
if len(pn) > 0 {
- return pn
+ return pn, nil
}
for _, p := range noticeFor.Projects() {
if strings.HasPrefix(p, "prebuilts/") {
@@ -385,7 +409,7 @@
match = match[:li]
}
}
- return match
+ return match, nil
}
break
}
@@ -393,9 +417,9 @@
for _, safePathPrefix := range safePathPrefixes {
if strings.HasPrefix(p, safePathPrefix.prefix) {
if safePathPrefix.strip {
- return p[len(safePathPrefix.prefix):]
+ return p[len(safePathPrefix.prefix):], nil
} else {
- return p
+ return p, nil
}
}
}
@@ -410,35 +434,26 @@
if fi > 0 {
n = n[:fi]
}
- return n
+ return n, nil
}
// checkMetadata tries to look up a library name from a METADATA file associated with `noticeFor`.
-func (ni *NoticeIndex) checkMetadata(noticeFor *TargetNode) string {
- for _, p := range noticeFor.Projects() {
- if name, ok := ni.projectName[p]; ok {
- if name == noProjectName {
- continue
- }
- return name
- }
- name, err := ni.checkMetadataFile(filepath.Join(p, "METADATA"))
- if err != nil {
- ni.projectName[p] = noProjectName
- continue
- }
- if len(name) == 0 {
- ni.projectName[p] = noProjectName
- continue
- }
- ni.projectName[p] = name
- return name
+func (ni *NoticeIndex) checkMetadata(noticeFor *TargetNode) (string, error) {
+ pms, err := ni.pmix.MetadataForProjects(noticeFor.Projects()...)
+ if err != nil {
+ return "", err
}
- return ""
+ for _, pm := range pms {
+ name := pm.VersionedName()
+ if name != "" {
+ return name, nil
+ }
+ }
+ return "", nil
}
// checkMetadataForLicenseText
-func (ni *NoticeIndex) checkMetadataForLicenseText(noticeFor *TargetNode, licenseText string) string {
+func (ni *NoticeIndex) checkMetadataForLicenseText(noticeFor *TargetNode, licenseText string) (string, error) {
p := ""
for _, proj := range noticeFor.Projects() {
if strings.HasPrefix(licenseText, proj) {
@@ -456,79 +471,17 @@
p = filepath.Dir(p)
continue
}
- return ""
+ return "", nil
}
}
- if name, ok := ni.projectName[p]; ok {
- if name == noProjectName {
- return ""
- }
- return name
- }
- name, err := ni.checkMetadataFile(filepath.Join(p, "METADATA"))
- if err == nil && len(name) > 0 {
- ni.projectName[p] = name
- return name
- }
- ni.projectName[p] = noProjectName
- return ""
-}
-
-// checkMetadataFile tries to look up a library name from a METADATA file at `path`.
-func (ni *NoticeIndex) checkMetadataFile(path string) (string, error) {
- f, err := ni.rootFS.Open(path)
+ pms, err := ni.pmix.MetadataForProjects(p)
if err != nil {
return "", err
}
- name := ""
- description := ""
- version := ""
- s := bufio.NewScanner(f)
- for s.Scan() {
- line := s.Text()
- m := nameRegexp.FindStringSubmatch(line)
- if m != nil {
- if 1 < len(m) && m[1] != "" {
- name = m[1]
- }
- if version != "" {
- break
- }
- continue
- }
- m = versionRegexp.FindStringSubmatch(line)
- if m != nil {
- if 1 < len(m) && m[1] != "" {
- version = m[1]
- }
- if name != "" {
- break
- }
- continue
- }
- m = descRegexp.FindStringSubmatch(line)
- if m != nil {
- if 1 < len(m) && m[1] != "" {
- description = m[1]
- }
- }
+ if pms == nil {
+ return "", nil
}
- _ = s.Err()
- _ = f.Close()
- if name != "" {
- if version != "" {
- if version[0] == 'v' || version[0] == 'V' {
- return name + "_" + version, nil
- } else {
- return name + "_v_" + version, nil
- }
- }
- return name, nil
- }
- if description != "" {
- return description, nil
- }
- return "", nil
+ return pms[0].VersionedName(), nil
}
// addText reads and indexes the content of a license text file.
diff --git a/tools/compliance/policy_policy.go b/tools/compliance/policy_policy.go
index 02d1d96..23e25c6 100644
--- a/tools/compliance/policy_policy.go
+++ b/tools/compliance/policy_policy.go
@@ -117,32 +117,6 @@
func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
cs := NewLicenseConditionSet()
for _, name := range names {
- if name == "restricted" {
- if 0 == len(tn.LicenseKinds()) {
- cs = cs.Plus(RestrictedCondition)
- continue
- }
- hasLgpl := false
- hasGeneric := false
- for _, kind := range tn.LicenseKinds() {
- if anyLgpl.MatchString(kind) {
- cs = cs.Plus(WeaklyRestrictedCondition)
- hasLgpl = true
- } else if versionedGpl.MatchString(kind) {
- cs = cs.Plus(RestrictedCondition)
- } else if genericGpl.MatchString(kind) {
- hasGeneric = true
- } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
- cs = cs.Plus(RestrictedCondition)
- } else {
- cs = cs.Plus(RestrictedCondition)
- }
- }
- if hasGeneric && !hasLgpl {
- cs = cs.Plus(RestrictedCondition)
- }
- continue
- }
if lc, ok := RecognizedConditionNames[name]; ok {
cs |= LicenseConditionSet(lc)
}
diff --git a/tools/compliance/policy_resolve.go b/tools/compliance/policy_resolve.go
index 93335a9..fc8ed4c 100644
--- a/tools/compliance/policy_resolve.go
+++ b/tools/compliance/policy_resolve.go
@@ -49,84 +49,71 @@
func TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
// short-cut if already walked and cached
- lg.mu.Lock()
- wg := lg.wgBU
+ lg.onceBottomUp.Do(func() {
+ // amap identifes targets previously walked. (guarded by mu)
+ amap := make(map[*TargetNode]struct{})
- if wg != nil {
- lg.mu.Unlock()
- wg.Wait()
- return
- }
- wg = &sync.WaitGroup{}
- wg.Add(1)
- lg.wgBU = wg
- lg.mu.Unlock()
+ // mu guards concurrent access to amap
+ var mu sync.Mutex
- // amap identifes targets previously walked. (guarded by mu)
- amap := make(map[*TargetNode]struct{})
+ var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
- // mu guards concurrent access to amap
- var mu sync.Mutex
+ walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
+ priorWalkResults := func() (LicenseConditionSet, bool) {
+ mu.Lock()
+ defer mu.Unlock()
- var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
-
- walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
- priorWalkResults := func() (LicenseConditionSet, bool) {
- mu.Lock()
- defer mu.Unlock()
-
- if _, alreadyWalked := amap[target]; alreadyWalked {
- if treatAsAggregate {
- return target.resolution, true
+ if _, alreadyWalked := amap[target]; alreadyWalked {
+ if treatAsAggregate {
+ return target.resolution, true
+ }
+ if !target.pure {
+ return target.resolution, true
+ }
+ // previously walked in a pure aggregate context,
+ // needs to walk again in non-aggregate context
+ } else {
+ target.resolution |= conditionsFn(target)
+ amap[target] = struct{}{}
}
- if !target.pure {
- return target.resolution, true
- }
- // previously walked in a pure aggregate context,
- // needs to walk again in non-aggregate context
- } else {
- target.resolution |= conditionsFn(target)
- amap[target] = struct{}{}
+ target.pure = treatAsAggregate
+ return target.resolution, false
}
- target.pure = treatAsAggregate
- return target.resolution, false
- }
- cs, alreadyWalked := priorWalkResults()
- if alreadyWalked {
+ cs, alreadyWalked := priorWalkResults()
+ if alreadyWalked {
+ return cs
+ }
+
+ c := make(chan LicenseConditionSet, len(target.edges))
+ // add all the conditions from all the dependencies
+ for _, edge := range target.edges {
+ go func(edge *TargetEdge) {
+ // walk dependency to get its conditions
+ cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
+
+ // turn those into the conditions that apply to the target
+ cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
+
+ c <- cs
+ }(edge)
+ }
+ for i := 0; i < len(target.edges); i++ {
+ cs |= <-c
+ }
+ mu.Lock()
+ target.resolution |= cs
+ mu.Unlock()
+
+ // return conditions up the tree
return cs
}
- c := make(chan LicenseConditionSet, len(target.edges))
- // add all the conditions from all the dependencies
- for _, edge := range target.edges {
- go func(edge *TargetEdge) {
- // walk dependency to get its conditions
- cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
-
- // turn those into the conditions that apply to the target
- cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
-
- c <- cs
- }(edge)
+ // walk each of the roots
+ for _, rname := range lg.rootFiles {
+ rnode := lg.targets[rname]
+ _ = walk(rnode, rnode.IsContainer())
}
- for i := 0; i < len(target.edges); i++ {
- cs |= <-c
- }
- mu.Lock()
- target.resolution |= cs
- mu.Unlock()
-
- // return conditions up the tree
- return cs
- }
-
- // walk each of the roots
- for _, rname := range lg.rootFiles {
- rnode := lg.targets[rname]
- _ = walk(rnode, rnode.IsContainer())
- }
-
- wg.Done()
+ })
}
// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
@@ -145,78 +132,76 @@
func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
// short-cut if already walked and cached
- lg.mu.Lock()
- wg := lg.wgTD
+ lg.onceTopDown.Do(func() {
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
- if wg != nil {
- lg.mu.Unlock()
- wg.Wait()
- return
- }
- wg = &sync.WaitGroup{}
- wg.Add(1)
- lg.wgTD = wg
- lg.mu.Unlock()
+ // start with the conditions propagated up the graph
+ TraceBottomUpConditions(lg, conditionsFn)
- // start with the conditions propagated up the graph
- TraceBottomUpConditions(lg, conditionsFn)
+ // amap contains the set of targets already walked. (guarded by mu)
+ amap := make(map[*TargetNode]struct{})
- // amap contains the set of targets already walked. (guarded by mu)
- amap := make(map[*TargetNode]struct{})
+ // mu guards concurrent access to amap
+ var mu sync.Mutex
- // mu guards concurrent access to amap
- var mu sync.Mutex
+ var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
- var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
-
- walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
- defer wg.Done()
- mu.Lock()
- fnode.resolution |= conditionsFn(fnode)
- fnode.resolution |= cs
- fnode.pure = treatAsAggregate
- amap[fnode] = struct{}{}
- cs = fnode.resolution
- mu.Unlock()
- // for each dependency
- for _, edge := range fnode.edges {
- func(edge *TargetEdge) {
- // dcs holds the dpendency conditions inherited from the target
- dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
- dnode := edge.dependency
+ walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
+ defer wg.Done()
+ continueWalk := func() bool {
mu.Lock()
defer mu.Unlock()
- depcs := dnode.resolution
- _, alreadyWalked := amap[dnode]
- if !dcs.IsEmpty() && alreadyWalked {
- if dcs.Difference(depcs).IsEmpty() {
+
+ depcs := fnode.resolution
+ _, alreadyWalked := amap[fnode]
+ if alreadyWalked {
+ if cs.IsEmpty() {
+ return false
+ }
+ if cs.Difference(depcs).IsEmpty() {
// no new conditions
// pure aggregates never need walking a 2nd time with same conditions
if treatAsAggregate {
- return
+ return false
}
// non-aggregates don't need walking as non-aggregate a 2nd time
- if !dnode.pure {
- return
+ if !fnode.pure {
+ return false
}
// previously walked as pure aggregate; need to re-walk as non-aggregate
}
}
+ fnode.resolution |= conditionsFn(fnode)
+ fnode.resolution |= cs
+ fnode.pure = treatAsAggregate
+ amap[fnode] = struct{}{}
+ cs = fnode.resolution
+ return true
+ }()
+ if !continueWalk {
+ return
+ }
+ // for each dependency
+ for _, edge := range fnode.edges {
+ // dcs holds the dpendency conditions inherited from the target
+ dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
+ dnode := edge.dependency
// add the conditions to the dependency
wg.Add(1)
go walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
- }(edge)
+ }
}
- }
- // walk each of the roots
- for _, rname := range lg.rootFiles {
- rnode := lg.targets[rname]
- wg.Add(1)
- // add the conditions to the root and its transitive closure
- go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
- }
- wg.Done()
- wg.Wait()
+ // walk each of the roots
+ for _, rname := range lg.rootFiles {
+ rnode := lg.targets[rname]
+ wg.Add(1)
+ // add the conditions to the root and its transitive closure
+ go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
+ }
+ wg.Done()
+ wg.Wait()
+ })
}
diff --git a/tools/compliance/policy_resolvenotices_test.go b/tools/compliance/policy_resolvenotices_test.go
index ee5e3b8..92b0ce3 100644
--- a/tools/compliance/policy_resolvenotices_test.go
+++ b/tools/compliance/policy_resolvenotices_test.go
@@ -217,10 +217,10 @@
},
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", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
- {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -245,7 +245,7 @@
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"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -258,17 +258,17 @@
},
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", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"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", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
- {"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"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", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
- {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -280,11 +280,11 @@
},
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", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
- {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
- {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -309,7 +309,7 @@
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"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -336,7 +336,7 @@
{"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"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -363,7 +363,7 @@
{"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"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
diff --git a/tools/compliance/policy_resolveshare_test.go b/tools/compliance/policy_resolveshare_test.go
index d49dfa8..cf88058 100644
--- a/tools/compliance/policy_resolveshare_test.go
+++ b/tools/compliance/policy_resolveshare_test.go
@@ -73,8 +73,8 @@
{"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"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -84,7 +84,7 @@
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
- {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
@@ -94,7 +94,7 @@
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
- {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
},
},
{
diff --git a/tools/compliance/projectmetadata/projectmetadata.go b/tools/compliance/projectmetadata/projectmetadata.go
index b31413d..f0ad7b5 100644
--- a/tools/compliance/projectmetadata/projectmetadata.go
+++ b/tools/compliance/projectmetadata/projectmetadata.go
@@ -40,11 +40,39 @@
project string
}
+// ProjectUrlMap maps url type name to url value
+type ProjectUrlMap map[string]string
+
+// DownloadUrl returns the address of a download location
+func (m ProjectUrlMap) DownloadUrl() string {
+ for _, urlType := range []string{"GIT", "SVN", "HG", "DARCS"} {
+ if url, ok := m[urlType]; ok {
+ return url
+ }
+ }
+ return ""
+}
+
// String returns a string representation of the metadata for error messages.
func (pm *ProjectMetadata) String() string {
return fmt.Sprintf("project: %q\n%s", pm.project, pm.proto.String())
}
+// ProjectName returns the name of the project.
+func (pm *ProjectMetadata) Name() string {
+ return pm.proto.GetName()
+}
+
+// ProjectVersion returns the version of the project if available.
+func (pm *ProjectMetadata) Version() string {
+ tp := pm.proto.GetThirdParty()
+ if tp != nil {
+ version := tp.GetVersion()
+ return version
+ }
+ return ""
+}
+
// VersionedName returns the name of the project including the version if any.
func (pm *ProjectMetadata) VersionedName() string {
name := pm.proto.GetName()
@@ -65,13 +93,35 @@
return pm.proto.GetDescription()
}
+// UrlsByTypeName returns a map of URLs by Type Name
+func (pm *ProjectMetadata) UrlsByTypeName() ProjectUrlMap {
+ tp := pm.proto.GetThirdParty()
+ if tp == nil {
+ return nil
+ }
+ if len(tp.Url) == 0 {
+ return nil
+ }
+ urls := make(ProjectUrlMap)
+
+ for _, url := range tp.Url {
+ uri := url.GetValue()
+ if uri == "" {
+ continue
+ }
+ urls[project_metadata_proto.URL_Type_name[int32(url.GetType())]] = uri
+ }
+ return urls
+}
+
// projectIndex describes a project to be read; after `wg.Wait()`, will contain either
// a `ProjectMetadata`, pm (can be nil even without error), or a non-nil `err`.
type projectIndex struct {
project string
- pm *ProjectMetadata
- err error
- done chan struct{}
+ path string
+ pm *ProjectMetadata
+ err error
+ done chan struct{}
}
// finish marks the task to read the `projectIndex` completed.
@@ -181,6 +231,19 @@
return result, nil
}
+// AllMetadataFiles returns the sorted list of all METADATA files read thus far.
+func (ix *Index) AllMetadataFiles() []string {
+ var files []string
+ ix.projects.Range(func(key, value any) bool {
+ pi := value.(*projectIndex)
+ if pi.path != "" {
+ files = append(files, pi.path)
+ }
+ return true
+ })
+ return files
+}
+
// readMetadataFile tries to read and parse a METADATA file at `path` for `project`.
func (ix *Index) readMetadataFile(pi *projectIndex, path string) {
f, err := ix.rootFS.Open(path)
@@ -201,9 +264,24 @@
pm := &ProjectMetadata{project: pi.project}
err = uo.Unmarshal(data, &pm.proto)
if err != nil {
- pi.err = fmt.Errorf("error in project %q metadata %q: %w", pi.project, path, err)
+ pi.err = fmt.Errorf(`error in project %q METADATA %q: %v
+
+METADATA and METADATA.android files must parse as text protobufs
+defined by
+ build/soong/compliance/project_metadata_proto/project_metadata.proto
+
+* unknown fields don't matter
+* check invalid ENUM names
+* check quoting
+* check unescaped nested quotes
+* check the comment marker for protobuf is '#' not '//'
+
+if importing a library that uses a different sort of METADATA file, add
+a METADATA.android file beside it to parse instead
+`, pi.project, path, err)
return
}
+ pi.path = path
pi.pm = pm
}
diff --git a/tools/compliance/projectmetadata/projectmetadata_test.go b/tools/compliance/projectmetadata/projectmetadata_test.go
index 1e4256f..0af0cd7 100644
--- a/tools/compliance/projectmetadata/projectmetadata_test.go
+++ b/tools/compliance/projectmetadata/projectmetadata_test.go
@@ -19,6 +19,7 @@
"strings"
"testing"
+ "android/soong/compliance/project_metadata_proto"
"android/soong/tools/compliance/testfs"
)
@@ -40,8 +41,79 @@
// NO_NAME_0_1 represents a METADATA file with a description but no name
NO_NAME_0_1 = `description: "my library" third_party { version: "0.1" }`
+
+ // URL values per type
+ GIT_URL = "http://example.github.com/my_lib"
+ SVN_URL = "http://example.svn.com/my_lib"
+ HG_URL = "http://example.hg.com/my_lib"
+ DARCS_URL = "http://example.darcs.com/my_lib"
+ PIPER_URL = "http://google3/third_party/my/package"
+ HOMEPAGE_URL = "http://example.com/homepage"
+ OTHER_URL = "http://google.com/"
+ ARCHIVE_URL = "http://ftp.example.com/"
+ LOCAL_SOURCE_URL = "https://android.googlesource.com/platform/external/apache-http/"
)
+// libWithUrl returns a METADATA file with the right download url
+func libWithUrl(urlTypes ...string) string {
+ var sb strings.Builder
+
+ fmt.Fprintln(&sb, `name: "mylib" description: "my library"
+ third_party {
+ version: "1.0"`)
+
+ for _, urltype := range urlTypes {
+ var urlValue string
+ switch urltype {
+ case "GIT":
+ urlValue = GIT_URL
+ case "SVN":
+ urlValue = SVN_URL
+ case "HG":
+ urlValue = HG_URL
+ case "DARCS":
+ urlValue = DARCS_URL
+ case "PIPER":
+ urlValue = PIPER_URL
+ case "HOMEPAGE":
+ urlValue = HOMEPAGE_URL
+ case "OTHER":
+ urlValue = OTHER_URL
+ case "ARCHIVE":
+ urlValue = ARCHIVE_URL
+ case "LOCAL_SOURCE":
+ urlValue = LOCAL_SOURCE_URL
+ default:
+ panic(fmt.Errorf("unknown url type: %q. Please update libWithUrl() in build/make/tools/compliance/projectmetadata/projectmetadata_test.go", urltype))
+ }
+ fmt.Fprintf(&sb, " url { type: %s value: %q }\n", urltype, urlValue)
+ }
+ fmt.Fprintln(&sb, `}`)
+
+ return sb.String()
+}
+
+func TestVerifyAllUrlTypes(t *testing.T) {
+ t.Run("verifyAllUrlTypes", func(t *testing.T) {
+ types := make([]string, 0, len(project_metadata_proto.URL_Type_value))
+ for t := range project_metadata_proto.URL_Type_value {
+ types = append(types, t)
+ }
+ libWithUrl(types...)
+ })
+}
+
+func TestUnknownPanics(t *testing.T) {
+ t.Run("Unknown panics", func(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("unexpected success: got no error, want panic")
+ }
+ }()
+ libWithUrl("SOME WILD VALUE THAT DOES NOT EXIST")
+ })
+}
+
func TestReadMetadataForProjects(t *testing.T) {
tests := []struct {
name string
@@ -56,7 +128,13 @@
"/a/METADATA": []byte("name: \"Android\"\n"),
},
projects: []string{"/a"},
- expected: []pmeta{{project: "/a", versionedName: "Android"}},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "Android",
+ name: "Android",
+ version: "",
+ downloadUrl: "",
+ }},
},
{
name: "versioned",
@@ -64,7 +142,237 @@
"/a/METADATA": []byte(MY_LIB_1_0),
},
projects: []string{"/a"},
- expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_homepage",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("HOMEPAGE")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_git",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("GIT")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: GIT_URL,
+ }},
+ },
+ {
+ name: "lib_with_svn",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("SVN")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: SVN_URL,
+ }},
+ },
+ {
+ name: "lib_with_hg",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("HG")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: HG_URL,
+ }},
+ },
+ {
+ name: "lib_with_darcs",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("DARCS")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: DARCS_URL,
+ }},
+ },
+ {
+ name: "lib_with_piper",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("PIPER")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_other",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("OTHER")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_local_source",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("LOCAL_SOURCE")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_archive",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("ARCHIVE")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_all_downloads",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("DARCS", "HG", "SVN", "GIT")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: GIT_URL,
+ }},
+ },
+ {
+ name: "lib_with_all_downloads_in_different_order",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("DARCS", "GIT", "SVN", "HG")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: GIT_URL,
+ }},
+ },
+ {
+ name: "lib_with_all_but_git",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("DARCS", "HG", "SVN")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: SVN_URL,
+ }},
+ },
+ {
+ name: "lib_with_all_but_git_and_svn",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("DARCS", "HG")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: HG_URL,
+ }},
+ },
+ {
+ name: "lib_with_all_nondownloads_and_git",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("HOMEPAGE", "LOCAL_SOURCE", "PIPER", "ARCHIVE", "GIT")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: GIT_URL,
+ }},
+ },
+ {
+ name: "lib_with_all_nondownloads",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl("HOMEPAGE", "LOCAL_SOURCE", "PIPER", "ARCHIVE")),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
+ },
+ {
+ name: "lib_with_all_nondownloads",
+ fs: &testfs.TestFS{
+ "/a/METADATA": []byte(libWithUrl()),
+ },
+ projects: []string{"/a"},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
},
{
name: "versioneddesc",
@@ -72,7 +380,13 @@
"/a/METADATA": []byte(NO_NAME_0_1),
},
projects: []string{"/a"},
- expected: []pmeta{{project: "/a", versionedName: "my library"}},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "my library",
+ name: "",
+ version: "0.1",
+ downloadUrl: "",
+ }},
},
{
name: "unterminated",
@@ -91,9 +405,27 @@
},
projects: []string{"/a", "/b", "/c"},
expected: []pmeta{
- {project: "/a", versionedName: ""},
- {project: "/b", versionedName: "mylib_v_1.0"},
- {project: "/c", versionedName: "my library"},
+ {
+ project: "/a",
+ versionedName: "",
+ name: "",
+ version: "",
+ downloadUrl: "",
+ },
+ {
+ project: "/b",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ },
+ {
+ project: "/c",
+ versionedName: "my library",
+ name: "",
+ version: "0.1",
+ downloadUrl: "",
+ },
},
},
{
@@ -104,8 +436,20 @@
},
projects: []string{"/a", "/b", "/c"},
expected: []pmeta{
- {project: "/a", versionedName: ""},
- {project: "/b", versionedName: "mylib_v_1.0"},
+ {
+ project: "/a",
+ versionedName: "",
+ name: "",
+ version: "",
+ downloadUrl: "",
+ },
+ {
+ project: "/b",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ },
},
},
{
@@ -116,8 +460,20 @@
},
projects: []string{"/a", "/b", "/c"},
expected: []pmeta{
- {project: "/a", versionedName: ""},
- {project: "/c", versionedName: "my library"},
+ {
+ project: "/a",
+ versionedName: "",
+ name: "",
+ version: "",
+ downloadUrl: "",
+ },
+ {
+ project: "/c",
+ versionedName: "my library",
+ name: "",
+ version: "0.1",
+ downloadUrl: "",
+ },
},
},
{
@@ -128,8 +484,20 @@
},
projects: []string{"/a", "/b", "/c"},
expected: []pmeta{
- {project: "/b", versionedName: "mylib_v_1.0"},
- {project: "/c", versionedName: "my library"},
+ {
+ project: "/b",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ },
+ {
+ project: "/c",
+ versionedName: "my library",
+ name: "",
+ version: "0.1",
+ downloadUrl: "",
+ },
},
},
{
@@ -170,7 +538,13 @@
"/a/METADATA": []byte(EMPTY),
},
projects: []string{"/a"},
- expected: []pmeta{{project: "/a", versionedName: ""}},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "",
+ name: "",
+ version: "",
+ downloadUrl: "",
+ }},
},
{
name: "emptyother",
@@ -191,7 +565,13 @@
"/a/METADATA.android": []byte(MY_LIB_1_0),
},
projects: []string{"/a"},
- expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}},
+ expected: []pmeta{{
+ project: "/a",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ }},
},
{
name: "enchilada",
@@ -203,9 +583,27 @@
},
projects: []string{"/a", "/b", "/c"},
expected: []pmeta{
- {project: "/a", versionedName: ""},
- {project: "/b", versionedName: "mylib_v_1.0"},
- {project: "/c", versionedName: "my library"},
+ {
+ project: "/a",
+ versionedName: "",
+ name: "",
+ version: "",
+ downloadUrl: "",
+ },
+ {
+ project: "/b",
+ versionedName: "mylib_v_1.0",
+ name: "mylib",
+ version: "1.0",
+ downloadUrl: "",
+ },
+ {
+ project: "/c",
+ versionedName: "my library",
+ name: "",
+ version: "0.1",
+ downloadUrl: "",
+ },
},
},
}
@@ -255,10 +653,13 @@
type pmeta struct {
project string
versionedName string
+ name string
+ version string
+ downloadUrl string
}
func (pm pmeta) String() string {
- return fmt.Sprintf("project: %q versionedName: %q\n", pm.project, pm.versionedName)
+ return fmt.Sprintf("project: %q versionedName: %q name: %q version: %q downloadUrl: %q\n", pm.project, pm.versionedName, pm.name, pm.version, pm.downloadUrl)
}
func (pm pmeta) equals(other *ProjectMetadata) bool {
@@ -268,6 +669,15 @@
if pm.versionedName != other.VersionedName() {
return false
}
+ if pm.name != other.Name() {
+ return false
+ }
+ if pm.version != other.Version() {
+ return false
+ }
+ if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {
+ return false
+ }
return true
}
@@ -283,6 +693,15 @@
if pm.versionedName != other.VersionedName() {
fmt.Fprintf(&sb, " versionedName: %q", other.VersionedName())
}
+ if pm.name != other.Name() {
+ fmt.Fprintf(&sb, " name: %q", other.Name())
+ }
+ if pm.version != other.Version() {
+ fmt.Fprintf(&sb, " version: %q", other.Version())
+ }
+ if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {
+ fmt.Fprintf(&sb, " downloadUrl: %q", other.UrlsByTypeName().DownloadUrl())
+ }
fmt.Fprintf(&sb, ", want")
if pm.project != other.project {
fmt.Fprintf(&sb, " project: %q", pm.project)
@@ -290,5 +709,14 @@
if pm.versionedName != other.VersionedName() {
fmt.Fprintf(&sb, " versionedName: %q", pm.versionedName)
}
+ if pm.name != other.Name() {
+ fmt.Fprintf(&sb, " name: %q", pm.name)
+ }
+ if pm.version != other.Version() {
+ fmt.Fprintf(&sb, " version: %q", pm.version)
+ }
+ if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {
+ fmt.Fprintf(&sb, " downloadUrl: %q", pm.downloadUrl)
+ }
return sb.String()
}
diff --git a/tools/compliance/test_util.go b/tools/compliance/test_util.go
index 6c50d3e..db711a7 100644
--- a/tools/compliance/test_util.go
+++ b/tools/compliance/test_util.go
@@ -57,7 +57,7 @@
LGPL = `` +
`package_name: "Free Library"
license_kinds: "SPDX-license-identifier-LGPL-2.0"
-license_conditions: "restricted"
+license_conditions: "restricted_allows_dynamic_linking"
`
// MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.
diff --git a/tools/event_log_tags.bzl b/tools/event_log_tags.bzl
deleted file mode 100644
index 35305ae..0000000
--- a/tools/event_log_tags.bzl
+++ /dev/null
@@ -1,35 +0,0 @@
-"""Event log tags generation rule"""
-
-load("@bazel_skylib//lib:paths.bzl", "paths")
-
-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"),
- ),
- },
-)
diff --git a/tools/fileslist_util.py b/tools/fileslist_util.py
index ff40d51..a1b1197 100755
--- a/tools/fileslist_util.py
+++ b/tools/fileslist_util.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -15,7 +15,9 @@
# limitations under the License.
#
-import getopt, json, sys
+import argparse
+import json
+import sys
def PrintFileNames(path):
with open(path) as jf:
@@ -27,42 +29,25 @@
with open(path) as jf:
data = json.load(jf)
for line in data:
- print "{0:12d} {1}".format(line["Size"], line["Name"])
+ print(f"{line['Size']:12d} {line['Name']}")
-def PrintUsage(name):
- print("""
-Usage: %s -[nc] json_files_list
- -n produces list of files only
- -c produces classic installed-files.txt
-""" % (name))
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-n", action="store_true",
+ help="produces list of files only")
+ parser.add_argument("-c", action="store_true",
+ help="produces classic installed-files.txt")
+ parser.add_argument("json_files_list")
+ args = parser.parse_args()
-def main(argv):
- try:
- opts, args = getopt.getopt(argv[1:], "nc", "")
- except getopt.GetoptError, err:
- print(err)
- PrintUsage(argv[0])
- sys.exit(2)
-
- if len(opts) == 0:
- print("No conversion option specified")
- PrintUsage(argv[0])
- sys.exit(2)
-
- if len(args) == 0:
- print("No input file specified")
- PrintUsage(argv[0])
- sys.exit(2)
-
- for o, a in opts:
- if o == ("-n"):
- PrintFileNames(args[0])
- sys.exit()
- elif o == ("-c"):
- PrintCanonicalList(args[0])
- sys.exit()
- else:
- assert False, "Unsupported option"
+ if args.n and args.c:
+ sys.exit("Cannot specify both -n and -c")
+ elif args.n:
+ PrintFileNames(args.json_files_list)
+ elif args.c:
+ PrintCanonicalList(args.json_files_list)
+ else:
+ sys.exit("No conversion option specified")
if __name__ == '__main__':
- main(sys.argv)
+ main()
diff --git a/tools/findleaves.py b/tools/findleaves.py
index 97302e9..86f3f3a 100755
--- a/tools/findleaves.py
+++ b/tools/findleaves.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2009 The Android Open Source Project
#
@@ -121,7 +121,7 @@
results = list(set(perform_find(mindepth, prune, dirlist, filenames)))
results.sort()
for r in results:
- print r
+ print(r)
if __name__ == "__main__":
main(sys.argv)
diff --git a/tools/java-layers.py b/tools/java-layers.py
deleted file mode 100755
index b3aec2b..0000000
--- a/tools/java-layers.py
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import re
-import sys
-
-def fail_with_usage():
- sys.stderr.write("usage: java-layers.py DEPENDENCY_FILE SOURCE_DIRECTORIES...\n")
- sys.stderr.write("\n")
- sys.stderr.write("Enforces layering between java packages. Scans\n")
- sys.stderr.write("DIRECTORY and prints errors when the packages violate\n")
- sys.stderr.write("the rules defined in the DEPENDENCY_FILE.\n")
- sys.stderr.write("\n")
- sys.stderr.write("Prints a warning when an unknown package is encountered\n")
- sys.stderr.write("on the assumption that it should fit somewhere into the\n")
- sys.stderr.write("layering.\n")
- sys.stderr.write("\n")
- sys.stderr.write("DEPENDENCY_FILE format\n")
- sys.stderr.write(" - # starts comment\n")
- sys.stderr.write(" - Lines consisting of two java package names: The\n")
- sys.stderr.write(" first package listed must not contain any references\n")
- sys.stderr.write(" to any classes present in the second package, or any\n")
- sys.stderr.write(" of its dependencies.\n")
- sys.stderr.write(" - Lines consisting of one java package name: The\n")
- sys.stderr.write(" packge is assumed to be a high level package and\n")
- sys.stderr.write(" nothing may depend on it.\n")
- sys.stderr.write(" - Lines consisting of a dash (+) followed by one java\n")
- sys.stderr.write(" package name: The package is considered a low level\n")
- sys.stderr.write(" package and may not import any of the other packages\n")
- sys.stderr.write(" listed in the dependency file.\n")
- sys.stderr.write(" - Lines consisting of a plus (-) followed by one java\n")
- sys.stderr.write(" package name: The package is considered \'legacy\'\n")
- sys.stderr.write(" and excluded from errors.\n")
- sys.stderr.write("\n")
- sys.exit(1)
-
-class Dependency:
- def __init__(self, filename, lineno, lower, top, lowlevel, legacy):
- self.filename = filename
- self.lineno = lineno
- self.lower = lower
- self.top = top
- self.lowlevel = lowlevel
- self.legacy = legacy
- self.uppers = []
- self.transitive = set()
-
- def matches(self, imp):
- for d in self.transitive:
- if imp.startswith(d):
- return True
- return False
-
-class Dependencies:
- def __init__(self, deps):
- def recurse(obj, dep, visited):
- global err
- if dep in visited:
- sys.stderr.write("%s:%d: Circular dependency found:\n"
- % (dep.filename, dep.lineno))
- for v in visited:
- sys.stderr.write("%s:%d: Dependency: %s\n"
- % (v.filename, v.lineno, v.lower))
- err = True
- return
- visited.append(dep)
- for upper in dep.uppers:
- obj.transitive.add(upper)
- if upper in deps:
- recurse(obj, deps[upper], visited)
- self.deps = deps
- self.parts = [(dep.lower.split('.'),dep) for dep in deps.itervalues()]
- # transitive closure of dependencies
- for dep in deps.itervalues():
- recurse(dep, dep, [])
- # disallow everything from the low level components
- for dep in deps.itervalues():
- if dep.lowlevel:
- for d in deps.itervalues():
- if dep != d and not d.legacy:
- dep.transitive.add(d.lower)
- # disallow the 'top' components everywhere but in their own package
- for dep in deps.itervalues():
- if dep.top and not dep.legacy:
- for d in deps.itervalues():
- if dep != d and not d.legacy:
- d.transitive.add(dep.lower)
- for dep in deps.itervalues():
- dep.transitive = set([x+"." for x in dep.transitive])
- if False:
- for dep in deps.itervalues():
- print "-->", dep.lower, "-->", dep.transitive
-
- # Lookup the dep object for the given package. If pkg is a subpackage
- # of one with a rule, that one will be returned. If no matches are found,
- # None is returned.
- def lookup(self, pkg):
- # Returns the number of parts that match
- def compare_parts(parts, pkg):
- if len(parts) > len(pkg):
- return 0
- n = 0
- for i in range(0, len(parts)):
- if parts[i] != pkg[i]:
- return 0
- n = n + 1
- return n
- pkg = pkg.split(".")
- matched = 0
- result = None
- for (parts,dep) in self.parts:
- x = compare_parts(parts, pkg)
- if x > matched:
- matched = x
- result = dep
- return result
-
-def parse_dependency_file(filename):
- global err
- f = file(filename)
- lines = f.readlines()
- f.close()
- def lineno(s, i):
- i[0] = i[0] + 1
- return (i[0],s)
- n = [0]
- lines = [lineno(x,n) for x in lines]
- lines = [(n,s.split("#")[0].strip()) for (n,s) in lines]
- lines = [(n,s) for (n,s) in lines if len(s) > 0]
- lines = [(n,s.split()) for (n,s) in lines]
- deps = {}
- for n,words in lines:
- if len(words) == 1:
- lower = words[0]
- top = True
- legacy = False
- lowlevel = False
- if lower[0] == '+':
- lower = lower[1:]
- top = False
- lowlevel = True
- elif lower[0] == '-':
- lower = lower[1:]
- legacy = True
- if lower in deps:
- sys.stderr.write(("%s:%d: Package '%s' already defined on"
- + " line %d.\n") % (filename, n, lower, deps[lower].lineno))
- err = True
- else:
- deps[lower] = Dependency(filename, n, lower, top, lowlevel, legacy)
- elif len(words) == 2:
- lower = words[0]
- upper = words[1]
- if lower in deps:
- dep = deps[lower]
- if dep.top:
- sys.stderr.write(("%s:%d: Can't add dependency to top level package "
- + "'%s'\n") % (filename, n, lower))
- err = True
- else:
- dep = Dependency(filename, n, lower, False, False, False)
- deps[lower] = dep
- dep.uppers.append(upper)
- else:
- sys.stderr.write("%s:%d: Too many words on line starting at \'%s\'\n" % (
- filename, n, words[2]))
- err = True
- return Dependencies(deps)
-
-def find_java_files(srcs):
- result = []
- for d in srcs:
- if d[0] == '@':
- f = file(d[1:])
- result.extend([fn for fn in [s.strip() for s in f.readlines()]
- if len(fn) != 0])
- f.close()
- else:
- for root, dirs, files in os.walk(d):
- result.extend([os.sep.join((root,f)) for f in files
- if f.lower().endswith(".java")])
- return result
-
-COMMENTS = re.compile("//.*?\n|/\*.*?\*/", re.S)
-PACKAGE = re.compile("package\s+(.*)")
-IMPORT = re.compile("import\s+(.*)")
-
-def examine_java_file(deps, filename):
- global err
- # Yes, this is a crappy java parser. Write a better one if you want to.
- f = file(filename)
- text = f.read()
- f.close()
- text = COMMENTS.sub("", text)
- index = text.find("{")
- if index < 0:
- sys.stderr.write(("%s: Error: Unable to parse java. Can't find class "
- + "declaration.\n") % filename)
- err = True
- return
- text = text[0:index]
- statements = [s.strip() for s in text.split(";")]
- # First comes the package declaration. Then iterate while we see import
- # statements. Anything else is either bad syntax that we don't care about
- # because the compiler will fail, or the beginning of the class declaration.
- m = PACKAGE.match(statements[0])
- if not m:
- sys.stderr.write(("%s: Error: Unable to parse java. Missing package "
- + "statement.\n") % filename)
- err = True
- return
- pkg = m.group(1)
- imports = []
- for statement in statements[1:]:
- m = IMPORT.match(statement)
- if not m:
- break
- imports.append(m.group(1))
- # Do the checking
- if False:
- print filename
- print "'%s' --> %s" % (pkg, imports)
- dep = deps.lookup(pkg)
- if not dep:
- sys.stderr.write(("%s: Error: Package does not appear in dependency file: "
- + "%s\n") % (filename, pkg))
- err = True
- return
- for imp in imports:
- if dep.matches(imp):
- sys.stderr.write("%s: Illegal import in package '%s' of '%s'\n"
- % (filename, pkg, imp))
- err = True
-
-err = False
-
-def main(argv):
- if len(argv) < 3:
- fail_with_usage()
- deps = parse_dependency_file(argv[1])
-
- if err:
- sys.exit(1)
-
- java = find_java_files(argv[2:])
- for filename in java:
- examine_java_file(deps, filename)
-
- if err:
- sys.stderr.write("%s: Using this file as dependency file.\n" % argv[1])
- sys.exit(1)
-
- sys.exit(0)
-
-if __name__ == "__main__":
- main(sys.argv)
-
diff --git a/tools/parsedeps.py b/tools/parsedeps.py
deleted file mode 100755
index 32d8ad7..0000000
--- a/tools/parsedeps.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env python
-# vim: ts=2 sw=2
-
-import optparse
-import re
-import sys
-
-
-class Dependency:
- def __init__(self, tgt):
- self.tgt = tgt
- self.pos = ""
- self.prereqs = set()
- self.visit = 0
-
- def add(self, prereq):
- self.prereqs.add(prereq)
-
-
-class Dependencies:
- def __init__(self):
- self.lines = {}
- self.__visit = 0
- self.count = 0
-
- def add(self, tgt, prereq):
- t = self.lines.get(tgt)
- if not t:
- t = Dependency(tgt)
- self.lines[tgt] = t
- p = self.lines.get(prereq)
- if not p:
- p = Dependency(prereq)
- self.lines[prereq] = p
- t.add(p)
- self.count = self.count + 1
-
- def setPos(self, tgt, pos):
- t = self.lines.get(tgt)
- if not t:
- t = Dependency(tgt)
- self.lines[tgt] = t
- t.pos = pos
-
- def get(self, tgt):
- if self.lines.has_key(tgt):
- return self.lines[tgt]
- else:
- return None
-
- def __iter__(self):
- return self.lines.iteritems()
-
- def trace(self, tgt, prereq):
- self.__visit = self.__visit + 1
- d = self.lines.get(tgt)
- if not d:
- return
- return self.__trace(d, prereq)
-
- def __trace(self, d, prereq):
- if d.visit == self.__visit:
- return d.trace
- if d.tgt == prereq:
- return [ [ d ], ]
- d.visit = self.__visit
- result = []
- for pre in d.prereqs:
- recursed = self.__trace(pre, prereq)
- for r in recursed:
- result.append([ d ] + r)
- d.trace = result
- return result
-
-def help():
- print "Commands:"
- print " dep TARGET Print the prerequisites for TARGET"
- print " trace TARGET PREREQ Print the paths from TARGET to PREREQ"
-
-
-def main(argv):
- opts = optparse.OptionParser()
- opts.add_option("-i", "--interactive", action="store_true", dest="interactive",
- help="Interactive mode")
- (options, args) = opts.parse_args()
-
- deps = Dependencies()
-
- filename = args[0]
- print "Reading %s" % filename
-
- if True:
- f = open(filename)
- for line in f:
- line = line.strip()
- if len(line) > 0:
- if line[0] == '#':
- pos,tgt = line.rsplit(":", 1)
- pos = pos[1:].strip()
- tgt = tgt.strip()
- deps.setPos(tgt, pos)
- else:
- (tgt,prereq) = line.split(':', 1)
- tgt = tgt.strip()
- prereq = prereq.strip()
- deps.add(tgt, prereq)
- f.close()
-
- print "Read %d dependencies. %d targets." % (deps.count, len(deps.lines))
- while True:
- line = raw_input("target> ")
- if not line.strip():
- continue
- split = line.split()
- cmd = split[0]
- if len(split) == 2 and cmd == "dep":
- tgt = split[1]
- d = deps.get(tgt)
- if d:
- for prereq in d.prereqs:
- print prereq.tgt
- elif len(split) == 3 and cmd == "trace":
- tgt = split[1]
- prereq = split[2]
- if False:
- print "from %s to %s" % (tgt, prereq)
- trace = deps.trace(tgt, prereq)
- if trace:
- width = 0
- for g in trace:
- for t in g:
- if len(t.tgt) > width:
- width = len(t.tgt)
- for g in trace:
- for t in g:
- if t.pos:
- print t.tgt, " " * (width-len(t.tgt)), " #", t.pos
- else:
- print t.tgt
- print
- else:
- help()
-
-if __name__ == "__main__":
- try:
- main(sys.argv)
- except KeyboardInterrupt:
- print
- except EOFError:
- print
-
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 465e1a8..d308a55 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -842,14 +842,13 @@
SYSTEM/ after rebuilding recovery.
"""
common.ZipDelete(zip_filename, files_list)
- output_zip = zipfile.ZipFile(zip_filename, "a",
+ with zipfile.ZipFile(zip_filename, "a",
compression=zipfile.ZIP_DEFLATED,
- allowZip64=True)
- for item in files_list:
- file_path = os.path.join(OPTIONS.input_tmp, item)
- assert os.path.exists(file_path)
- common.ZipWrite(output_zip, file_path, arcname=item)
- common.ZipClose(output_zip)
+ allowZip64=True) as output_zip:
+ for item in files_list:
+ file_path = os.path.join(OPTIONS.input_tmp, item)
+ assert os.path.exists(file_path)
+ common.ZipWrite(output_zip, file_path, arcname=item)
def HasPartition(partition_name):
@@ -1176,7 +1175,7 @@
AddVbmetaDigest(output_zip)
if output_zip:
- common.ZipClose(output_zip)
+ output_zip.close()
if OPTIONS.replace_updated_files_list:
ReplaceUpdatedFiles(output_zip.filename,
OPTIONS.replace_updated_files_list)
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index d7b0ba2..8dd7c31 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -63,6 +63,8 @@
self.codename_to_api_level_map = codename_to_api_level_map
self.debugfs_path = os.path.join(
OPTIONS.search_path, "bin", "debugfs_static")
+ self.fsckerofs_path = os.path.join(
+ OPTIONS.search_path, "bin", "fsck.erofs")
self.avbtool = avbtool if avbtool else "avbtool"
self.sign_tool = sign_tool
@@ -80,8 +82,13 @@
"Couldn't find location of debugfs_static: " +
"Path {} does not exist. ".format(self.debugfs_path) +
"Make sure bin/debugfs_static can be found in -p <path>")
- list_cmd = ['deapexer', '--debugfs_path',
- self.debugfs_path, 'list', self.apex_path]
+ if not os.path.exists(self.fsckerofs_path):
+ raise ApexSigningError(
+ "Couldn't find location of fsck.erofs: " +
+ "Path {} does not exist. ".format(self.fsckerofs_path) +
+ "Make sure bin/fsck.erofs can be found in -p <path>")
+ list_cmd = ['deapexer', '--debugfs_path', self.debugfs_path,
+ '--fsckerofs_path', self.fsckerofs_path, 'list', self.apex_path]
entries_names = common.RunAndCheckOutput(list_cmd).split()
apk_entries = [name for name in entries_names if name.endswith('.apk')]
sepolicy_entries = []
@@ -120,9 +127,15 @@
"Couldn't find location of debugfs_static: " +
"Path {} does not exist. ".format(self.debugfs_path) +
"Make sure bin/debugfs_static can be found in -p <path>")
+ if not os.path.exists(self.fsckerofs_path):
+ raise ApexSigningError(
+ "Couldn't find location of fsck.erofs: " +
+ "Path {} does not exist. ".format(self.fsckerofs_path) +
+ "Make sure bin/fsck.erofs can be found in -p <path>")
payload_dir = common.MakeTempDir()
- extract_cmd = ['deapexer', '--debugfs_path',
- self.debugfs_path, 'extract', self.apex_path, payload_dir]
+ extract_cmd = ['deapexer', '--debugfs_path', self.debugfs_path,
+ '--fsckerofs_path', self.fsckerofs_path, 'extract',
+ self.apex_path, payload_dir]
common.RunAndCheckOutput(extract_cmd)
assert os.path.exists(self.apex_path)
@@ -415,7 +428,7 @@
apex_zip = zipfile.ZipFile(apex_file, 'a', allowZip64=True)
common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
- common.ZipClose(apex_zip)
+ apex_zip.close()
# 3. Sign the APEX container with container_key.
signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
@@ -457,6 +470,7 @@
The path to the signed APEX file.
"""
debugfs_path = os.path.join(OPTIONS.search_path, 'bin', 'debugfs_static')
+ fsckerofs_path = os.path.join(OPTIONS.search_path, 'bin', 'fsck.erofs')
# 1. Decompress original_apex inside compressed apex.
original_apex_file = common.MakeTempFile(prefix='original-apex-',
@@ -464,6 +478,7 @@
# Decompression target path should not exist
os.remove(original_apex_file)
common.RunAndCheckOutput(['deapexer', '--debugfs_path', debugfs_path,
+ '--fsckerofs_path', fsckerofs_path,
'decompress', '--input', apex_file,
'--output', original_apex_file])
@@ -529,7 +544,9 @@
output_fp.write(apex_data)
debugfs_path = os.path.join(OPTIONS.search_path, 'bin', 'debugfs_static')
+ fsckerofs_path = os.path.join(OPTIONS.search_path, 'bin', 'fsck.erofs')
cmd = ['deapexer', '--debugfs_path', debugfs_path,
+ '--fsckerofs_path', fsckerofs_path,
'info', '--print-type', apex_file]
try:
@@ -603,11 +620,17 @@
debugfs_path = "debugfs"
if OPTIONS.search_path:
debugfs_path = os.path.join(OPTIONS.search_path, "bin", "debugfs_static")
+
+ fsckerofs_path = "fsck.erofs"
+ if OPTIONS.search_path:
+ fsckerofs_path = os.path.join(OPTIONS.search_path, "bin", "fsck.erofs")
+
deapexer = 'deapexer'
if OPTIONS.search_path:
deapexer_path = os.path.join(OPTIONS.search_path, "bin", "deapexer")
if os.path.isfile(deapexer_path):
deapexer = deapexer_path
+
for apex_filename in os.listdir(target_dir):
apex_filepath = os.path.join(target_dir, apex_filename)
if not os.path.isfile(apex_filepath) or \
@@ -622,6 +645,7 @@
# Check if the file is compressed or not
apex_type = RunAndCheckOutput([
deapexer, "--debugfs_path", debugfs_path,
+ "--fsckerofs_path", fsckerofs_path,
'info', '--print-type', apex_filepath]).rstrip()
if apex_type == 'COMPRESSED':
apex_info.is_compressed = True
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 7639ffd..252b1d5 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -410,7 +410,7 @@
build_command.append("--casefold")
if (needs_compress or prop_dict.get("f2fs_compress") == "true"):
build_command.append("--compression")
- if (prop_dict.get("mount_point") != "data"):
+ if "ro_mount_point" in prop_dict:
build_command.append("--readonly")
if (prop_dict.get("f2fs_compress") == "true"):
build_command.append("--sldc")
@@ -757,6 +757,8 @@
if not copy_prop(prop, "extfs_rsv_pct"):
d["extfs_rsv_pct"] = "0"
+ d["ro_mount_point"] = "1"
+
# Copy partition-specific properties.
d["mount_point"] = mount_point
if mount_point == "system":
diff --git a/tools/releasetools/check_ota_package_signature.py b/tools/releasetools/check_ota_package_signature.py
index b395c19..97957be 100755
--- a/tools/releasetools/check_ota_package_signature.py
+++ b/tools/releasetools/check_ota_package_signature.py
@@ -142,7 +142,7 @@
"""Verifies the payload and metadata signatures in an A/B OTA payload."""
package_zip = zipfile.ZipFile(package, 'r', allowZip64=True)
if 'payload.bin' not in package_zip.namelist():
- common.ZipClose(package_zip)
+ package_zip.close()
return
print('Verifying A/B OTA payload signatures...')
@@ -160,7 +160,7 @@
'--in_file=' + payload_file,
'--public_key=' + pubkey]
common.RunAndCheckOutput(cmd)
- common.ZipClose(package_zip)
+ package_zip.close()
# Verified successfully upon reaching here.
print('\nPayload signatures VERIFIED\n\n')
diff --git a/tools/releasetools/check_target_files_vintf.py b/tools/releasetools/check_target_files_vintf.py
index c369a59..b32b85c 100755
--- a/tools/releasetools/check_target_files_vintf.py
+++ b/tools/releasetools/check_target_files_vintf.py
@@ -229,11 +229,15 @@
apex-info-list.xml file
"""
- debugfs_path = 'debugfs'
deapexer = 'deapexer'
+ debugfs_path = 'debugfs'
+ blkid_path = 'blkid'
+ fsckerofs_path = 'fsck.erofs'
if OPTIONS.search_path:
debugfs_path = os.path.join(OPTIONS.search_path, 'bin', 'debugfs_static')
deapexer_path = os.path.join(OPTIONS.search_path, 'bin', 'deapexer')
+ blkid_path = os.path.join(OPTIONS.search_path, 'bin', 'blkid')
+ fsckerofs_path = os.path.join(OPTIONS.search_path, 'bin', 'fsck.erofs')
if os.path.isfile(deapexer_path):
deapexer = deapexer_path
@@ -257,6 +261,8 @@
cmd = [deapexer,
'--debugfs_path', debugfs_path,
+ '--fsckerofs_path', fsckerofs_path,
+ '--blkid_path', blkid_path,
'extract',
apex,
os.path.join(outp, info['name'])]
@@ -280,8 +286,8 @@
inp_partition = os.path.join(inp, target_files_rel_path,"apex")
if os.path.exists(inp_partition):
apex_dir = root_dir + os.path.join(device_path + "/apex");
- os.makedirs(apex_dir)
- os.rename(inp_partition, apex_dir)
+ os.makedirs(root_dir + device_path)
+ shutil.copytree(inp_partition, apex_dir, symlinks=True)
ExtractApexes(apex_dir, extracted_root)
create_info_file = True
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 715802f..2f05d44 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -2797,18 +2797,6 @@
def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
compress_type=None):
- # http://b/18015246
- # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
- # for files larger than 2GiB. We can work around this by adjusting their
- # limit. Note that `zipfile.writestr()` will not work for strings larger than
- # 2GiB. The Python interpreter sometimes rejects strings that large (though
- # it isn't clear to me exactly what circumstances cause this).
- # `zipfile.write()` must be used directly to work around this.
- #
- # This mess can be avoided if we port to python3.
- saved_zip64_limit = zipfile.ZIP64_LIMIT
- zipfile.ZIP64_LIMIT = (1 << 32) - 1
-
if compress_type is None:
compress_type = zip_file.compression
if arcname is None:
@@ -2834,14 +2822,13 @@
finally:
os.chmod(filename, saved_stat.st_mode)
os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
- zipfile.ZIP64_LIMIT = saved_zip64_limit
def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
compress_type=None):
"""Wrap zipfile.writestr() function to work around the zip64 limit.
- Even with the ZIP64_LIMIT workaround, it won't allow writing a string
+ Python's zip implementation won't allow writing a string
longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
when calling crc32(bytes).
@@ -2850,9 +2837,6 @@
when we know the string won't be too long.
"""
- saved_zip64_limit = zipfile.ZIP64_LIMIT
- zipfile.ZIP64_LIMIT = (1 << 32) - 1
-
if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
zinfo.compress_type = zip_file.compression
@@ -2885,7 +2869,6 @@
zinfo.date_time = (2009, 1, 1, 0, 0, 0)
zip_file.writestr(zinfo, data)
- zipfile.ZIP64_LIMIT = saved_zip64_limit
def ZipDelete(zip_filename, entries, force=False):
@@ -2919,18 +2902,6 @@
os.replace(new_zipfile, zip_filename)
-def ZipClose(zip_file):
- # http://b/18015246
- # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
- # central directory.
- saved_zip64_limit = zipfile.ZIP64_LIMIT
- zipfile.ZIP64_LIMIT = (1 << 32) - 1
-
- zip_file.close()
-
- zipfile.ZIP64_LIMIT = saved_zip64_limit
-
-
class DeviceSpecificParams(object):
module = None
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index 76da89c..f8bdd81 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -173,7 +173,7 @@
logger.info('Writing super.img to archive...')
with zipfile.ZipFile(
output_file, 'a', compression=zipfile.ZIP_DEFLATED,
- allowZip64=not OPTIONS.sparse_userimages) as output_zip:
+ allowZip64=True) as output_zip:
common.ZipWrite(output_zip, super_file, 'super.img')
diff --git a/tools/releasetools/non_ab_ota.py b/tools/releasetools/non_ab_ota.py
index 6c927ec..ac85aa4 100644
--- a/tools/releasetools/non_ab_ota.py
+++ b/tools/releasetools/non_ab_ota.py
@@ -277,7 +277,7 @@
# We haven't written the metadata entry, which will be done in
# FinalizeMetadata.
- common.ZipClose(output_zip)
+ output_zip.close()
needed_property_files = (
NonAbOtaPropertyFiles(),
@@ -531,7 +531,7 @@
# We haven't written the metadata entry yet, which will be handled in
# FinalizeMetadata().
- common.ZipClose(output_zip)
+ output_zip.close()
# Sign the generated zip package unless no_signing is specified.
needed_property_files = (
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 9d5c67d..60e95ad 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -487,7 +487,7 @@
else:
common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
- common.ZipClose(target_zip)
+ target_zip.close()
return target_file
@@ -624,7 +624,7 @@
# TODO(xunchang) handle META/postinstall_config.txt'
- common.ZipClose(partial_target_zip)
+ partial_target_zip.close()
return partial_target_file
@@ -709,7 +709,7 @@
# Write new ab_partitions.txt file
common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
- common.ZipClose(target_zip)
+ target_zip.close()
return target_file
@@ -1017,11 +1017,11 @@
common.ZipWriteStr(output_zip, "apex_info.pb", ota_apex_info,
compress_type=zipfile.ZIP_STORED)
- common.ZipClose(target_zip)
+ target_zip.close()
# We haven't written the metadata entry yet, which will be handled in
# FinalizeMetadata().
- common.ZipClose(output_zip)
+ output_zip.close()
FinalizeMetadata(metadata, staging_file, output_file,
package_key=OPTIONS.package_key)
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 9f41874..e36a2be 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -22,7 +22,7 @@
import ota_metadata_pb2
import common
-from common import (ZipDelete, ZipClose, OPTIONS, MakeTempFile,
+from common import (ZipDelete, OPTIONS, MakeTempFile,
ZipWriteStr, BuildInfo, LoadDictionaryFromFile,
SignFile, PARTITIONS_WITH_BUILD_PROP, PartitionBuildProps,
GetRamdiskFormat)
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index d3fbdad..5f48dd0 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -99,14 +99,14 @@
The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is
set to true.
- --avb_{boot,recovery,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
- vbmeta_vendor}_algorithm <algorithm>
- --avb_{boot,recovery,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
- vbmeta_vendor}_key <key>
+ --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,
+ vbmeta_system,vbmeta_vendor}_algorithm <algorithm>
+ --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,
+ vbmeta_system,vbmeta_vendor}_key <key>
Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
the specified image. Otherwise it uses the existing values in info dict.
- --avb_{apex,boot,recovery,system,system_other,vendor,dtbo,vbmeta,
+ --avb_{apex,init_boot,boot,recovery,system,system_other,vendor,dtbo,vbmeta,
vbmeta_system,vbmeta_vendor}_extra_args <args>
Specify any additional args that are needed to AVB-sign the image
(e.g. "--signing_helper /path/to/helper"). The args will be appended to
@@ -901,7 +901,7 @@
certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
for k in keys:
common.ZipWrite(certs_zip, k)
- common.ZipClose(certs_zip)
+ certs_zip.close()
common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
@@ -1363,6 +1363,12 @@
OPTIONS.avb_algorithms['dtbo'] = a
elif o == "--avb_dtbo_extra_args":
OPTIONS.avb_extra_args['dtbo'] = a
+ elif o == "--avb_init_boot_key":
+ OPTIONS.avb_keys['init_boot'] = a
+ elif o == "--avb_init_boot_algorithm":
+ OPTIONS.avb_algorithms['init_boot'] = a
+ elif o == "--avb_init_boot_extra_args":
+ OPTIONS.avb_extra_args['init_boot'] = a
elif o == "--avb_recovery_key":
OPTIONS.avb_keys['recovery'] = a
elif o == "--avb_recovery_algorithm":
@@ -1458,6 +1464,9 @@
"avb_dtbo_algorithm=",
"avb_dtbo_key=",
"avb_dtbo_extra_args=",
+ "avb_init_boot_algorithm=",
+ "avb_init_boot_key=",
+ "avb_init_boot_extra_args=",
"avb_recovery_algorithm=",
"avb_recovery_key=",
"avb_recovery_extra_args=",
@@ -1529,8 +1538,8 @@
platform_api_level, codename_to_api_level_map,
compressed_extension)
- common.ZipClose(input_zip)
- common.ZipClose(output_zip)
+ input_zip.close()
+ output_zip.close()
if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
BuildVendorPartitions(args[1])
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 2a0e592..8c9655ad0 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -222,17 +222,17 @@
info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
build_info = common.BuildInfo(info_dict)
self.assertEqual(
- 'product-brand/product-name/product-device:version-release/build-id/'
- 'version-incremental:build-type/build-tags', build_info.fingerprint)
+ 'product-brand/product-name/product-device:version-release/build-id/'
+ 'version-incremental:build-type/build-tags', build_info.fingerprint)
build_props = info_dict['build.prop'].build_props
del build_props['ro.build.id']
build_props['ro.build.legacy.id'] = 'legacy-build-id'
build_info = common.BuildInfo(info_dict, use_legacy_id=True)
self.assertEqual(
- 'product-brand/product-name/product-device:version-release/'
- 'legacy-build-id/version-incremental:build-type/build-tags',
- build_info.fingerprint)
+ 'product-brand/product-name/product-device:version-release/'
+ 'legacy-build-id/version-incremental:build-type/build-tags',
+ build_info.fingerprint)
self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
False)
@@ -241,9 +241,9 @@
info_dict['vbmeta_digest'] = 'abcde12345'
build_info = common.BuildInfo(info_dict, use_legacy_id=False)
self.assertEqual(
- 'product-brand/product-name/product-device:version-release/'
- 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
- build_info.fingerprint)
+ 'product-brand/product-name/product-device:version-release/'
+ 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
+ build_info.fingerprint)
def test___getitem__(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
@@ -376,7 +376,7 @@
info_dict['build.prop'].build_props[
'ro.product.property_source_order'] = 'bad-source'
with self.assertRaisesRegexp(common.ExternalError,
- 'Invalid ro.product.property_source_order'):
+ 'Invalid ro.product.property_source_order'):
info = common.BuildInfo(info_dict, None)
info.GetBuildProp('ro.product.device')
@@ -459,7 +459,7 @@
time.sleep(5) # Make sure the atime/mtime will change measurably.
common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
- common.ZipClose(zip_file)
+ zip_file.close()
self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
test_file_name, expected_stat, expected_mode,
@@ -494,7 +494,7 @@
expected_mode = extra_args.get("perms", zinfo_perms)
common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
- common.ZipClose(zip_file)
+ zip_file.close()
self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
expected_mode=expected_mode,
@@ -536,7 +536,7 @@
common.ZipWrite(zip_file, test_file_name, **extra_args)
common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
- common.ZipClose(zip_file)
+ zip_file.close()
# Verify the contents written by ZipWrite().
self._verify(zip_file, zip_file_name, arcname_large,
@@ -551,12 +551,6 @@
os.remove(zip_file_name)
os.remove(test_file_name)
- def _test_reset_ZIP64_LIMIT(self, func, *args):
- default_limit = (1 << 31) - 1
- self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
- func(*args)
- self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
-
def test_ZipWrite(self):
file_contents = os.urandom(1024)
self._test_ZipWrite(file_contents)
@@ -581,7 +575,7 @@
})
def test_ZipWrite_resets_ZIP64_LIMIT(self):
- self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
+ self._test_ZipWrite("")
def test_ZipWriteStr(self):
random_string = os.urandom(1024)
@@ -632,9 +626,9 @@
})
def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
- self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
+ self._test_ZipWriteStr('foo', b'')
zinfo = zipfile.ZipInfo(filename="foo")
- self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
+ self._test_ZipWriteStr(zinfo, b'')
def test_bug21309935(self):
zip_file = tempfile.NamedTemporaryFile(delete=False)
@@ -656,7 +650,7 @@
zinfo = zipfile.ZipInfo(filename="qux")
zinfo.external_attr = 0o700 << 16
common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
- common.ZipClose(zip_file)
+ zip_file.close()
self._verify(zip_file, zip_file_name, "foo",
sha1(random_string).hexdigest(),
@@ -683,7 +677,7 @@
common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
- common.ZipClose(output_zip)
+ output_zip.close()
zip_file.close()
try:
@@ -731,8 +725,8 @@
common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
- common.ZipClose(output_zip)
- common.ZipClose(output_zip)
+ output_zip.close()
+ output_zip.close()
return zip_file
@test_utils.SkipIfExternalToolsUnavailable()
@@ -819,9 +813,9 @@
)
APKCERTS_CERTMAP1 = {
- 'RecoveryLocalizer.apk' : 'certs/devkey',
- 'Settings.apk' : 'build/make/target/product/security/platform',
- 'TV.apk' : 'PRESIGNED',
+ 'RecoveryLocalizer.apk': 'certs/devkey',
+ 'Settings.apk': 'build/make/target/product/security/platform',
+ 'TV.apk': 'PRESIGNED',
}
APKCERTS_TXT2 = (
@@ -836,10 +830,10 @@
)
APKCERTS_CERTMAP2 = {
- 'Compressed1.apk' : 'certs/compressed1',
- 'Compressed2a.apk' : 'certs/compressed2',
- 'Compressed2b.apk' : 'certs/compressed2',
- 'Compressed3.apk' : 'certs/compressed3',
+ 'Compressed1.apk': 'certs/compressed1',
+ 'Compressed2a.apk': 'certs/compressed2',
+ 'Compressed2b.apk': 'certs/compressed2',
+ 'Compressed3.apk': 'certs/compressed3',
}
APKCERTS_TXT3 = (
@@ -848,7 +842,7 @@
)
APKCERTS_CERTMAP3 = {
- 'Compressed4.apk' : 'certs/compressed4',
+ 'Compressed4.apk': 'certs/compressed4',
}
# Test parsing with no optional fields, both optional fields, and only the
@@ -865,9 +859,9 @@
)
APKCERTS_CERTMAP4 = {
- 'RecoveryLocalizer.apk' : 'certs/devkey',
- 'Settings.apk' : 'build/make/target/product/security/platform',
- 'TV.apk' : 'PRESIGNED',
+ 'RecoveryLocalizer.apk': 'certs/devkey',
+ 'Settings.apk': 'build/make/target/product/security/platform',
+ 'TV.apk': 'PRESIGNED',
}
def setUp(self):
@@ -971,7 +965,7 @@
extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
with open(extracted_from_privkey, 'rb') as privkey_fp, \
- open(extracted_from_pubkey, 'rb') as pubkey_fp:
+ open(extracted_from_pubkey, 'rb') as pubkey_fp:
self.assertEqual(privkey_fp.read(), pubkey_fp.read())
def test_ParseCertificate(self):
@@ -1235,7 +1229,8 @@
self.assertEqual(
'1-5 9-10',
sparse_image.file_map['//system/file1'].extra['text_str'])
- self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
+ self.assertTrue(
+ sparse_image.file_map['//system/file2'].extra['incomplete'])
self.assertTrue(
sparse_image.file_map['/system/app/file3'].extra['incomplete'])
@@ -1343,7 +1338,7 @@
'recovery_api_version': 3,
'fstab_version': 2,
'system_root_image': 'true',
- 'no_recovery' : 'true',
+ 'no_recovery': 'true',
'recovery_as_boot': 'true',
}
@@ -1664,6 +1659,7 @@
self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
test_file.name, 'generic_kernel')
+
class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
"""Checks the format of install-recovery.sh.
@@ -1673,7 +1669,7 @@
def setUp(self):
self._tempdir = common.MakeTempDir()
# Create a fake dict that contains the fstab info for boot&recovery.
- self._info = {"fstab" : {}}
+ self._info = {"fstab": {}}
fake_fstab = [
"/dev/soc.0/by-name/boot /boot emmc defaults defaults",
"/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
@@ -2020,11 +2016,11 @@
input_zip, 'odm', placeholder_values)
self.assertEqual({
- 'ro.odm.build.date.utc': '1578430045',
- 'ro.odm.build.fingerprint':
- 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
- 'ro.product.odm.device': 'coral',
- 'ro.product.odm.name': 'product1',
+ 'ro.odm.build.date.utc': '1578430045',
+ 'ro.odm.build.fingerprint':
+ 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
+ 'ro.product.odm.device': 'coral',
+ 'ro.product.odm.name': 'product1',
}, partition_props.build_props)
with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
@@ -2207,8 +2203,8 @@
copied_props = copy.deepcopy(partition_props)
self.assertEqual({
- 'ro.odm.build.date.utc': '1578430045',
- 'ro.odm.build.fingerprint':
- 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
- 'ro.product.odm.device': 'coral',
+ 'ro.odm.build.date.utc': '1578430045',
+ 'ro.odm.build.fingerprint':
+ 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
+ 'ro.product.odm.device': 'coral',
}, copied_props.build_props)
diff --git a/tools/soong_to_convert.py b/tools/soong_to_convert.py
index 949131b..649829f 100755
--- a/tools/soong_to_convert.py
+++ b/tools/soong_to_convert.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -50,9 +50,6 @@
Not all problems can be discovered, but this is a starting point.
"""
-
-from __future__ import print_function
-
import csv
import sys
@@ -113,7 +110,7 @@
def main(filename):
"""Read the CSV file, print the results"""
- with open(filename, 'rb') as csvfile:
+ with open(filename, 'r') as csvfile:
results = process(csv.reader(csvfile))
native_results = filter(results, "native")
diff --git a/tools/stub_diff_analyzer.py b/tools/stub_diff_analyzer.py
new file mode 100644
index 0000000..e49d092
--- /dev/null
+++ b/tools/stub_diff_analyzer.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from sys import exit
+from typing import List
+from glob import glob
+from pathlib import Path
+from collections import defaultdict
+from difflib import Differ
+from re import split
+from tqdm import tqdm
+import argparse
+
+
+DIFFER_CODE_LEN = 2
+
+class DifferCodes:
+ COMMON = ' '
+ UNIQUE_FIRST = '- '
+ UNIQUE_SECOND = '+ '
+ DIFF_IDENT = '? '
+
+class FilesDiffAnalyzer:
+ def __init__(self, args) -> None:
+ self.out_dir = args.out_dir
+ self.show_diff = args.show_diff
+ self.skip_words = args.skip_words
+ self.first_dir = args.first_dir
+ self.second_dir = args.second_dir
+ self.include_common = args.include_common
+
+ self.first_dir_files = self.get_files(self.first_dir)
+ self.second_dir_files = self.get_files(self.second_dir)
+ self.common_file_map = defaultdict(set)
+
+ self.map_common_files(self.first_dir_files, self.first_dir)
+ self.map_common_files(self.second_dir_files, self.second_dir)
+
+ def get_files(self, dir: str) -> List[str]:
+ """Get all files directory in the input directory including the files in the subdirectories
+
+ Recursively finds all files in the input directory.
+ Returns a list of file directory strings, which do not include directories but only files.
+ List is sorted in alphabetical order of the file directories.
+
+ Args:
+ dir: Directory to get the files. String.
+
+ Returns:
+ A list of file directory strings within the input directory.
+ Sorted in Alphabetical order.
+
+ Raises:
+ FileNotFoundError: An error occurred accessing the non-existing directory
+ """
+
+ if not dir_exists(dir):
+ raise FileNotFoundError("Directory does not exist")
+
+ if dir[:-2] != "**":
+ if dir[:-1] != "/":
+ dir += "/"
+ dir += "**"
+
+ return [file for file in sorted(glob(dir, recursive=True)) if Path(file).is_file()]
+
+ def map_common_files(self, files: List[str], dir: str) -> None:
+ for file in files:
+ file_name = file.split(dir, 1)[-1]
+ self.common_file_map[file_name].add(dir)
+ return
+
+ def compare_file_contents(self, first_file: str, second_file: str) -> List[str]:
+ """Compare the contents of the files and return different lines
+
+ Given two file directory strings, compare the contents of the two files
+ and return the list of file contents string prepended with unique identifier codes.
+ The identifier codes include:
+ - ' '(two empty space characters): Line common to two files
+ - '- '(minus followed by a space) : Line unique to first file
+ - '+ '(plus followed by a space) : Line unique to second file
+
+ Args:
+ first_file: First file directory string to compare the content
+ second_file: Second file directory string to compare the content
+
+ Returns:
+ A list of the file content strings. For example:
+
+ [
+ " Foo",
+ "- Bar",
+ "+ Baz"
+ ]
+ """
+
+ d = Differ()
+ first_file_contents = sort_methods(get_file_contents(first_file))
+ second_file_contents = sort_methods(get_file_contents(second_file))
+ diff = list(d.compare(first_file_contents, second_file_contents))
+ ret = [f"diff {first_file} {second_file}"]
+
+ idx = 0
+ while idx < len(diff):
+ line = diff[idx]
+ line_code = line[:DIFFER_CODE_LEN]
+
+ match line_code:
+ case DifferCodes.COMMON:
+ if self.include_common:
+ ret.append(line)
+
+ case DifferCodes.UNIQUE_FIRST:
+ # Should compare line
+ if (idx < len(diff) - 1 and
+ (next_line_code := diff[idx + 1][:DIFFER_CODE_LEN])
+ not in (DifferCodes.UNIQUE_FIRST, DifferCodes.COMMON)):
+ delta = 1 if next_line_code == DifferCodes.UNIQUE_SECOND else 2
+ line_to_compare = diff[idx + delta]
+ if self.lines_differ(line, line_to_compare):
+ ret.extend([line, line_to_compare])
+ else:
+ if self.include_common:
+ ret.append(DifferCodes.COMMON +
+ line[DIFFER_CODE_LEN:])
+ idx += delta
+ else:
+ ret.append(line)
+
+ case DifferCodes.UNIQUE_SECOND:
+ ret.append(line)
+
+ case DifferCodes.DIFF_IDENT:
+ pass
+ idx += 1
+ return ret
+
+ def lines_differ(self, line1: str, line2: str) -> bool:
+ """Check if the input lines are different or not
+
+ Compare the two lines word by word and check if the two lines are different or not.
+ If the different words in the comparing lines are included in skip_words,
+ the lines are not considered different.
+
+ Args:
+ line1: first line to compare
+ line2: second line to compare
+
+ Returns:
+ Boolean value indicating if the two lines are different or not
+
+ """
+ # Split by '.' or ' '(whitespace)
+ def split_words(line: str) -> List[str]:
+ return split('\\s|\\.', line[DIFFER_CODE_LEN:])
+
+ line1_words, line2_words = split_words(line1), split_words(line2)
+ if len(line1_words) != len(line2_words):
+ return True
+
+ for word1, word2 in zip(line1_words, line2_words):
+ if word1 != word2:
+ # not check if words are equal to skip word, but
+ # check if words contain skip word as substring
+ if all(sw not in word1 and sw not in word2 for sw in self.skip_words):
+ return True
+
+ return False
+
+ def analyze(self) -> None:
+ """Analyze file contents in both directories and write to output or console.
+ """
+ for file in tqdm(sorted(self.common_file_map.keys())):
+ val = self.common_file_map[file]
+
+ # When file exists in both directories
+ lines = list()
+ if val == set([self.first_dir, self.second_dir]):
+ lines = self.compare_file_contents(
+ self.first_dir + file, self.second_dir + file)
+ else:
+ existing_dir, not_existing_dir = (
+ (self.first_dir, self.second_dir) if self.first_dir in val
+ else (self.second_dir, self.first_dir))
+
+ lines = [f"{not_existing_dir}{file} does not exist."]
+
+ if self.show_diff:
+ lines.append(f"Content of {existing_dir}{file}: \n")
+ lines.extend(get_file_contents(existing_dir + file))
+
+ self.write(lines)
+
+ def write(self, lines: List[str]) -> None:
+ if self.out_dir == "":
+ pprint(lines)
+ else:
+ write_lines(self.out_dir, lines)
+
+###
+# Helper functions
+###
+
+def sort_methods(lines: List[str]) -> List[str]:
+ """Sort class methods in the file contents by alphabetical order
+
+ Given lines of Java file contents, return lines with class methods sorted in alphabetical order.
+ Also omit empty lines or lines with spaces.
+ For example:
+ l = [
+ "package android.test;",
+ "",
+ "public static final int ORANGE = 1;",
+ "",
+ "public class TestClass {",
+ "public TestClass() { throw new RuntimeException("Stub!"); }",
+ "public void foo() { throw new RuntimeException("Stub!"); }",
+ "public void bar() { throw new RuntimeException("Stub!"); }",
+ "}"
+ ]
+ sort_methods(l) returns
+ [
+ "package android.test;",
+ "public static final int ORANGE = 1;",
+ "public class TestClass {",
+ "public TestClass() { throw new RuntimeException("Stub!"); }",
+ "public void bar() { throw new RuntimeException("Stub!"); }",
+ "public void foo() { throw new RuntimeException("Stub!"); }",
+ "}"
+ ]
+
+ Args:
+ lines: List of strings consisted of Java file contents.
+
+ Returns:
+ A list of string with sorted class methods.
+
+ """
+ def is_not_blank(l: str) -> bool:
+ return bool(l) and not l.isspace()
+
+ ret = list()
+
+ in_class = False
+ buffer = list()
+ for line in lines:
+ if not in_class:
+ if "class" in line:
+ in_class = True
+ ret.append(line)
+ else:
+ # Adding static variables, package info, etc.
+ # Skipping empty or space lines.
+ if is_not_blank(line):
+ ret.append(line)
+ else:
+ # End of class
+ if line and line[0] == "}":
+ in_class = False
+ ret.extend(sorted(buffer))
+ buffer = list()
+ ret.append(line)
+ else:
+ if is_not_blank(line):
+ buffer.append(line)
+
+ return ret
+
+def get_file_contents(file_path: str) -> List[str]:
+ lines = list()
+ with open(file_path) as f:
+ lines = [line.rstrip('\n') for line in f]
+ f.close()
+ return lines
+
+def pprint(l: List[str]) -> None:
+ for line in l:
+ print(line)
+
+def write_lines(out_dir: str, lines: List[str]) -> None:
+ with open(out_dir, "a") as f:
+ f.writelines(line + '\n' for line in lines)
+ f.write("\n")
+ f.close()
+
+def dir_exists(dir: str) -> bool:
+ return Path(dir).exists()
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('first_dir', action='store', type=str,
+ help="first path to compare file directory and contents")
+ parser.add_argument('second_dir', action='store', type=str,
+ help="second path to compare file directory and contents")
+ parser.add_argument('--out', dest='out_dir',
+ action='store', default="", type=str,
+ help="optional directory to write log. If not set, will print to console")
+ parser.add_argument('--show-diff-file', dest='show_diff',
+ action=argparse.BooleanOptionalAction,
+ help="optional flag. If passed, will print out the content of the file unique to each directories")
+ parser.add_argument('--include-common', dest='include_common',
+ action=argparse.BooleanOptionalAction,
+ help="optional flag. If passed, will print out the contents common to both files as well,\
+ instead of printing only diff lines.")
+ parser.add_argument('--skip-words', nargs='+',
+ dest='skip_words', default=[], help="optional words to skip in comparison")
+
+ args = parser.parse_args()
+
+ if not args.first_dir or not args.second_dir:
+ parser.print_usage()
+ exit(0)
+
+ analyzer = FilesDiffAnalyzer(args)
+ analyzer.analyze()
diff --git a/tools/whichgit b/tools/whichgit
index 24d6d87..b0bf2e4 100755
--- a/tools/whichgit
+++ b/tools/whichgit
@@ -95,11 +95,12 @@
# Print the list of git directories that has one or more of the sources in it
for project in sorted(get_referenced_projects(get_git_dirs(), sources)):
print(project)
- if "*" in args.why or project in args.why:
- prefix = project + "/"
- for f in sources:
- if f.startswith(prefix):
- print(" " + f)
+ if args.why:
+ if "*" in args.why or project in args.why:
+ prefix = project + "/"
+ for f in sources:
+ if f.startswith(prefix):
+ print(" " + f)
if __name__ == "__main__":