commit e6bbafb4ff552c7f1135a266c717e52d13537f5f Author: cpeng Date: Mon Aug 25 08:23:45 2025 +0800 android 13 from xiaosuan diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 120000 index 0000000..0a46755 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1 @@ +make/CleanSpec.mk \ No newline at end of file diff --git a/bazel/OWNERS b/bazel/OWNERS new file mode 100644 index 0000000..4cac0f5 --- /dev/null +++ b/bazel/OWNERS @@ -0,0 +1 @@ +include platform/build/soong:/OWNERS diff --git a/bazel/README.md b/bazel/README.md new file mode 100644 index 0000000..a2cad3d --- /dev/null +++ b/bazel/README.md @@ -0,0 +1,5 @@ +# Bazel + +The code in this directory is experimental. Bazel support for Android Platform +is undergoing active development and workflow stability is currently not +guaranteed. diff --git a/bazel/bazel.BUILD b/bazel/bazel.BUILD new file mode 100644 index 0000000..f928935 --- /dev/null +++ b/bazel/bazel.BUILD @@ -0,0 +1,14 @@ +# This filegroup is necessary because Bazel requires that every file in the .d +# file of a compilation action is in a subdirectory of one of the transitive +# dependencies of the rule being compiled. +# +# (this is not an intentional feature but accidentally results from the fact +# that lines in the .d files must be turned into Artifact instances and thus +# need a source root. See ArtifactFactory.findSourceRoot() for the pertinent +# logic) +# +# The easiest way to ensure this is to add this filegroup to one of the +# dependencies of the cc_toolchain. Then the root directory of the repository +# becomes part of said transitive dependencies and thus every file is in a +# subdirectory of it. +filegroup(name="empty", visibility=["//visibility:public"]) diff --git a/bazel/bazel.WORKSPACE b/bazel/bazel.WORKSPACE new file mode 100644 index 0000000..4b889d1 --- /dev/null +++ b/bazel/bazel.WORKSPACE @@ -0,0 +1,91 @@ +load("//build/bazel/rules:soong_injection.bzl", "soong_injection_repository") +load("//build/bazel/rules:make_injection.bzl", "make_injection_repository") + +register_toolchains( + "//prebuilts/build-tools:py_toolchain", + "//prebuilts/clang/host/linux-x86:all", +) + +# This repository provides files that Soong emits during bp2build (other than +# converted BUILD files), mostly .bzl files containing constants to support the +# converted BUILD files. +soong_injection_repository(name="soong_injection") + +# This is a repository rule to allow Bazel builds to depend on Soong-built +# prebuilts for migration purposes. +make_injection_repository( + name = "make_injection", + binaries = [ + # APEX tools + "apex_compression_tool", + "apexer", + "conv_apex_manifest", + "deapexer", + "sefcontext_compile", + ], + target_module_files = { + # For APEX comparisons + "com.android.tzdata": ["system/apex/com.android.tzdata.apex"], + "com.android.runtime": ["system/apex/com.android.runtime.apex"], + "com.android.adbd": ["system/apex/com.android.adbd.capex"], + "build.bazel.examples.apex.minimal": ["system/product/apex/build.bazel.examples.apex.minimal.apex"], + }, + watch_android_bp_files = [ + "//:build/bazel/examples/apex/minimal/Android.bp", # for build.bazel.examples.apex.minimal + "//:packages/modules/adbd/apex/Android.bp", # for com.android.adbd + # TODO(b/210399979) - add the other .bp files to watch for the other modules built in these rule + ], +) + +local_repository( + name = "rules_cc", + path = "build/bazel/rules_cc", +) + +local_repository( + name = "bazel_skylib", + path = "external/bazel-skylib", +) + +local_repository( + name = "rules_android", + path = "external/bazelbuild-rules_android", +) + +register_toolchains( + # For Starlark Android rules + "//prebuilts/sdk:android_default_toolchain", + "//prebuilts/sdk:android_sdk_tools", + + # For native android_binary + "//prebuilts/sdk:android_sdk_tools_for_native_android_binary", + + # For APEX rules + "//build/bazel/rules/apex:all" +) + +bind( + name = "databinding_annotation_processor", + actual = "//prebuilts/sdk:compiler_annotation_processor", +) + +bind( + name = "android/dx_jar_import", + actual = "//prebuilts/sdk:dx_jar_import", +) + +# The r8.jar in prebuilts/r8 happens to have the d8 classes needed +# for Android app building, whereas the d8.jar in prebuilts/sdk/tools doesn't. +bind( + name = "android/d8_jar_import", + actual = "//prebuilts/r8:r8_jar_import", +) + +# TODO(b/201242197): Avoid downloading remote_coverage_tools (on CI) by creating +# a stub workspace. Test rules (e.g. sh_test) depend on this external dep, but +# we don't support coverage yet. Either vendor the external dep into AOSP, or +# cut the dependency from test rules to the external repo. +local_repository( + name = "remote_coverage_tools", + path = "build/bazel/rules/coverage/remote_coverage_tools", +) diff --git a/bazel/bazel.sh b/bazel/bazel.sh new file mode 100755 index 0000000..c30a8f5 --- /dev/null +++ b/bazel/bazel.sh @@ -0,0 +1,198 @@ +#!/bin/bash + +set -eo pipefail + +# TODO: Refactor build/make/envsetup.sh to make gettop() available elsewhere +function gettop +{ + local TOPFILE=build/bazel/bazel.sh + if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then + # The following circumlocution ensures we remove symlinks from TOP. + (cd "$TOP"; PWD= /bin/pwd) + else + if [ -f $TOPFILE ] ; then + # The following circumlocution (repeated below as well) ensures + # that we record the true directory name and not one that is + # faked up with symlink names. + PWD= /bin/pwd + else + local HERE=$PWD + local T= + while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do + \cd .. + T=`PWD= /bin/pwd -P` + done + \cd "$HERE" + if [ -f "$T/$TOPFILE" ]; then + echo "$T" + fi + fi + fi +} + +# TODO: Refactor build/soong/scripts/microfactory.bash to make getoutdir() available elsewhere +function getoutdir +{ + local out_dir="${OUT_DIR-}" + if [ -z "${out_dir}" ]; then + if [ "${OUT_DIR_COMMON_BASE-}" ]; then + out_dir="${OUT_DIR_COMMON_BASE}/$(basename ${TOP})" + else + out_dir="out" + fi + fi + if [[ "${out_dir}" != /* ]]; then + out_dir="${TOP}/${out_dir}" + fi + echo "${out_dir}" +} + +TOP="$(gettop)" +if [ ! "$TOP" ]; then + >&2 echo "Couldn't locate the top of the tree. Try setting TOP." + exit 1 +fi + +case $(uname -s) in + Darwin) + ANDROID_BAZEL_PATH="${TOP}/prebuilts/bazel/darwin-x86_64/bazel" + ANDROID_BAZELRC_NAME="darwin.bazelrc" + ANDROID_BAZEL_JDK_PATH="${TOP}/prebuilts/jdk/jdk11/darwin-x86" + + # Lock down PATH in action execution environment, thereby removing + # Bazel's default /bin, /usr/bin, /usr/local/bin and ensuring + # hermeticity from the system. + # + # The new PATH components are: + # + # - prebuilts/build-tools/path: contains checked-in tools that can be + # used as executables in actions. + # + # - out/.path: a special directory created by path_interposer with + # config from ui/build/paths/config.go for allowlisting specific + # binaries not in prebuilts/build-tools/path, but on the host system. + # If one runs Bazel without soong_ui, then this directory wouldn't + # exist, making standalone Bazel execution's PATH variable stricter than + # Bazel execution within soong_ui. + RESTRICTED_PATH="${TOP}/prebuilts/build-tools/path/darwin-x86:${TOP}/out/.path" + ;; + Linux) + ANDROID_BAZEL_PATH="${TOP}/prebuilts/bazel/linux-x86_64/bazel" + ANDROID_BAZELRC_NAME="linux.bazelrc" + ANDROID_BAZEL_JDK_PATH="${TOP}/prebuilts/jdk/jdk11/linux-x86" + RESTRICTED_PATH="${TOP}/prebuilts/build-tools/path/linux-x86:${TOP}/out/.path" + ;; + *) + >&2 echo "Bazel is supported on Linux and Darwin only. Your OS is not supported for Bazel usage, based on 'uname -s': $(uname -s)" + exit 1 + ;; +esac + +function verify_soong_outputs_exist() { + local to_check="${ABSOLUTE_OUT_DIR}/.path" + local no_soong=0 + if [[ ! -d "${to_check}" ]]; then + no_soong=1 + fi + + local bazel_configs=( + "bp2build" + "queryview" + ) + local valid_bazel_config=0 + for c in "${bazel_configs[@]}" + do + if [[ -d "${ABSOLUTE_OUT_DIR}/soong/""${c}" ]]; then + valid_bazel_config=1 + fi + done + + if [[ "${no_soong}" -eq "1" || "${valid_bazel_config}" -eq "0" ]]; then + >&2 echo "Error: missing generated Bazel files. Have you run bp2build or queryview?" + >&2 echo "Run bp2build with the command: m bp2build" + >&2 echo "Run queryview with the command: m queryview" + >&2 echo "Alternatively, for non-queryview applications, invoke Bazel using 'b' with the command: source envsetup.sh; b query/build/test " + exit 1 + fi +} + +function create_bazelrc() { + cat > "${ABSOLUTE_OUT_DIR}/bazel/path.bazelrc" <&2 echo "Couldn't locate Bazel binary" + exit 1 +fi + +if [ -n "$ANDROID_BAZELRC_PATH" -a -f "$ANDROID_BAZELRC_PATH" ]; then + export ANDROID_BAZELRC_PATH +else + >&2 echo "Couldn't locate bazelrc file for Bazel" + exit 1 +fi + +if [ -n "$ANDROID_BAZEL_JDK_PATH" -a -d "$ANDROID_BAZEL_JDK_PATH" ]; then + export ANDROID_BAZEL_JDK_PATH +else + >&2 echo "Couldn't locate JDK to use for Bazel" + exit 1 +fi + +ABSOLUTE_OUT_DIR="$(getoutdir)" + +# In order to be able to load JNI libraries, this directory needs to exist +mkdir -p "${ABSOLUTE_OUT_DIR}/bazel/javatmp" + +ADDITIONAL_FLAGS=() +if [[ "${STANDALONE_BAZEL}" =~ ^(true|TRUE|1)$ ]]; then + # STANDALONE_BAZEL is set. + >&2 echo "WARNING: Using Bazel in standalone mode. This mode is not integrated with Soong and Make, and is not supported" + >&2 echo "for Android Platform builds. Use this mode at your own risk." + >&2 echo +else + # STANDALONE_BAZEL is not set. + >&2 echo "WARNING: Bazel support for the Android Platform is experimental and is undergoing development." + >&2 echo "WARNING: Currently, build stability is not guaranteed. Thank you." + >&2 echo + + # Generate a bazelrc with dynamic content, like the absolute path to PATH variable values. + create_bazelrc + # Check that the Bazel synthetic workspace and other required inputs exist before handing over control to Bazel. + verify_soong_outputs_exist + ADDITIONAL_FLAGS+=("--bazelrc=${ABSOLUTE_OUT_DIR}/bazel/path.bazelrc") +fi + +JAVA_HOME="${ANDROID_BAZEL_JDK_PATH}" "${ANDROID_BAZEL_PATH}" \ + --server_javabase="${ANDROID_BAZEL_JDK_PATH}" \ + --output_user_root="${ABSOLUTE_OUT_DIR}/bazel/output_user_root" \ + --host_jvm_args=-Djava.io.tmpdir="${ABSOLUTE_OUT_DIR}/bazel/javatmp" \ + --bazelrc="${ANDROID_BAZELRC_PATH}" \ + "${ADDITIONAL_FLAGS[@]}" \ + "$@" diff --git a/bazel/ci/bp2build.sh b/bazel/ci/bp2build.sh new file mode 100755 index 0000000..8f9e7ac --- /dev/null +++ b/bazel/ci/bp2build.sh @@ -0,0 +1,130 @@ +#!/bin/bash -eux +# Verifies that bp2build-generated BUILD files result in successful Bazel +# builds. +# +# This verification script is designed to be used for continuous integration +# tests, though may also be used for manual developer verification. + +####### +# Setup +####### + +if [[ -z ${DIST_DIR+x} ]]; then + echo "DIST_DIR not set. Using out/dist. This should only be used for manual developer testing." + DIST_DIR="out/dist" +fi + +# Generate BUILD files into out/soong/bp2build +AOSP_ROOT="$(dirname $0)/../../.." +"${AOSP_ROOT}/build/soong/soong_ui.bash" --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build dist + +# Dist the entire workspace of generated BUILD files, rooted from +# out/soong/bp2build. This is done early so it's available even if builds/tests +# fail. +tar -czf "${DIST_DIR}/bp2build_generated_workspace.tar.gz" -C out/soong/bp2build . + +# Remove the ninja_build output marker file to communicate to buildbot that this is not a regular Ninja build, and its +# output should not be parsed as such. +rm -f out/ninja_build + +# Before you add flags to this list, cosnider adding it to the "ci" bazelrc +# config instead of this list so that flags are not duplicated between scripts +# and bazelrc, and bazelrc is the Bazel-native way of organizing flags. +FLAGS_LIST=( + --config=bp2build + --config=ci +) +FLAGS="${FLAGS_LIST[@]}" + +############### +# Build targets +############### +BUILD_TARGETS_LIST=( + //art/... + //bionic/... + //bootable/recovery/tools/recovery_l10n/... + //build/... + //cts/... + //development/... + //external/... + //frameworks/... + //libnativehelper/... + //packages/... + //prebuilts/clang/host/linux-x86:all + //system/... + //tools/apksig/... + //tools/platform-compat/... + + # These tools only build for host currently + -//external/e2fsprogs/misc:all + -//external/e2fsprogs/resize:all + -//external/e2fsprogs/debugfs:all + -//external/e2fsprogs/e2fsck:all +) +BUILD_TARGETS="${BUILD_TARGETS_LIST[@]}" +# Iterate over various architectures supported in the platform build. +tools/bazel --max_idle_secs=5 build ${FLAGS} --platforms //build/bazel/platforms:android_x86 -k -- ${BUILD_TARGETS} +tools/bazel --max_idle_secs=5 build ${FLAGS} --platforms //build/bazel/platforms:android_x86_64 -k -- ${BUILD_TARGETS} +tools/bazel --max_idle_secs=5 build ${FLAGS} --platforms //build/bazel/platforms:android_arm -k -- ${BUILD_TARGETS} +tools/bazel --max_idle_secs=5 build ${FLAGS} --platforms //build/bazel/platforms:android_arm64 -k -- ${BUILD_TARGETS} + +HOST_INCOMPATIBLE_TARGETS=( + # TODO(b/217756861): Apex toolchain is incompatible with host arches but apex modules do + # not have this restriction + -//build/bazel/examples/apex/... + -//packages/modules/adb/apex:com.android.adbd + -//system/timezone/apex:com.android.tzdata + -//build/bazel/tests/apex/... + -//build/bazel/ci/dist/... + + # TODO(b/217927043): Determine how to address targets that are device only + -//system/core/libpackagelistparser:all + -//external/icu/libicu:all + //external/icu/libicu:libicu + -//external/icu/icu4c/source/tools/ctestfw:all + + # TODO(b/217926427): determine why these host_supported modules do not build on host + -//packages/modules/adb:all + -//packages/modules/adb/pairing_connection:all +) + +# build for host +tools/bazel --max_idle_secs=5 build ${FLAGS} \ + --platforms //build/bazel/platforms:linux_x86_64 \ + -- ${BUILD_TARGETS} "${HOST_INCOMPATIBLE_TARGETS[@]}" + +########### +# Run tests +########### +tools/bazel --max_idle_secs=5 test ${FLAGS} //build/bazel/tests/... //build/bazel/rules/apex/... //build/bazel/scripts/... + +########### +# Dist mainline modules +########### +tools/bazel --max_idle_secs=5 run //build/bazel/ci/dist:mainline_modules ${FLAGS} --platforms=//build/bazel/platforms:android_x86 -- --dist_dir="${DIST_DIR}/mainline_modules_x86" +tools/bazel --max_idle_secs=5 run //build/bazel/ci/dist:mainline_modules ${FLAGS} --platforms=//build/bazel/platforms:android_x86_64 -- --dist_dir="${DIST_DIR}/mainline_modules_x86_64" +tools/bazel --max_idle_secs=5 run //build/bazel/ci/dist:mainline_modules ${FLAGS} --platforms=//build/bazel/platforms:android_arm -- --dist_dir="${DIST_DIR}/mainline_modules_arm" +tools/bazel --max_idle_secs=5 run //build/bazel/ci/dist:mainline_modules ${FLAGS} --platforms=//build/bazel/platforms:android_arm64 -- --dist_dir="${DIST_DIR}/mainline_modules_arm64" + +################### +# bp2build-progress +################### + +# Generate bp2build progress reports and graphs for these modules into the dist +# dir so that they can be downloaded from the CI artifact list. +BP2BUILD_PROGRESS_MODULES=( + com.android.runtime + com.android.neuralnetworks + com.android.media.swcodec +) +bp2build_progress_script="${AOSP_ROOT}/build/bazel/scripts/bp2build-progress/bp2build-progress.py" +bp2build_progress_output_dir="${DIST_DIR}/bp2build-progress" +mkdir -p "${bp2build_progress_output_dir}" + +report_args="" +for m in "${BP2BUILD_PROGRESS_MODULES[@]}"; do + report_args="$report_args -m ""${m}" + "${bp2build_progress_script}" graph -m "${m}" --use_queryview=true > "${bp2build_progress_output_dir}/${m}_graph.dot" +done + +"${bp2build_progress_script}" report ${report_args} --use_queryview=true > "${bp2build_progress_output_dir}/progress_report.txt" diff --git a/bazel/ci/diffs.sh b/bazel/ci/diffs.sh new file mode 100755 index 0000000..210b61b --- /dev/null +++ b/bazel/ci/diffs.sh @@ -0,0 +1,88 @@ +#!/bin/bash -eu +# checks the diff between legacy Soong built artifacts and their counterparts +# built with bazel/mixed build +export TARGET_PRODUCT=aosp_arm64 +export TARGET_BUILD_VARIANT=userdebug + +build/soong/soong_ui.bash \ + --build-mode \ + --all-modules \ + --dir="$(pwd)" \ + bp2build +tools/bazel build --config=bp2build //build/bazel/scripts/difftool:collect_zip +tools/bazel build --config=bp2build //build/bazel/scripts/difftool:difftool_zip + +# the following 2 arrays must be of the same size +MODULES=( + libnativehelper +) +OUTPUTS=( + JNIHelp.o +) +PATH_FILTERS=( + "linux_glibc_x86_shared/\|linux_x86-fastbuild" + "linux_glibc_x86_64_shared/\|linux_x86_64-fastbuild" + "android_arm64[-_]" +# "android_arm[-_]" TODO(usta) investigate why there is a diff for this +) +readonly AOSP_ROOT="$(readlink -f "$(dirname "$0")"/../../..)" +#TODO(usta): absolute path isn't compatible with collect.py and ninja +readonly LEGACY_OUTPUT_SEARCH_TREE="out/soong/.intermediates/libnativehelper" +readonly MIXED_OUTPUT_SEARCH_TREE="out/bazel/output/execroot/__main__/bazel-out" +readonly NINJA_FILE="$AOSP_ROOT/out/combined-$TARGET_PRODUCT.ninja" +# python is expected in PATH but used only to start a zipped python archive, +# which bundles its own interpreter. We could also simply use `tools/bazel run` +# instead however that sets the working directly differently and collect.py +# won't work because it expects paths relative to $OUT_DIR +# TODO(usta) make collect.py work with absolute paths and maybe consider +# using `tools/bazel run` on the `py_binary` target directly instead of using +# the python_zip_file filegroup's output +readonly stub_python=python3 +readonly LEGACY_COLLECTION="$AOSP_ROOT/out/diff_metadata/legacy" +readonly MIXED_COLLECTION="$AOSP_ROOT/out/diff_metadata/mixed" +mkdir -p "$LEGACY_COLLECTION" +mkdir -p "$MIXED_COLLECTION" + +function findIn() { + result=$(find "$1" -name "$3" | grep "$2") + count=$(echo "$result" | wc -l) + if [ "$count" != 1 ]; then + printf "multiple files found instead of exactly ONE:\n%s\n" "$result" 1>&2 + exit 1 + fi + echo "$result" +} + +for ((i = 0; i < ${#MODULES[@]}; i++)); do + MODULE=${MODULES[$i]} + echo "Building $MODULE for comparison" + build/soong/soong_ui.bash --make-mode "$MODULE" + $stub_python "bazel-bin/build/bazel/scripts/difftool/collect.zip" \ + "$NINJA_FILE" "$LEGACY_COLLECTION" + build/soong/soong_ui.bash \ + --make-mode \ + USE_BAZEL_ANALYSIS=1 \ + BAZEL_STARTUP_ARGS="--max_idle_secs=5" \ + BAZEL_BUILD_ARGS="--color=no --curses=no --noshow_progress" \ + "$MODULE" + $stub_python "bazel-bin/build/bazel/scripts/difftool/collect.zip" \ + "$NINJA_FILE" "$MIXED_COLLECTION" + OUTPUT=${OUTPUTS[$i]} + for ((j = 0; j < ${#PATH_FILTERS[@]}; j++)); do + PATH_FILTER=${PATH_FILTERS[$j]} + LEGACY_OUTPUT=$(findIn "$LEGACY_OUTPUT_SEARCH_TREE" "$PATH_FILTER" "$OUTPUT") + MIXED_OUTPUT=$(findIn "$MIXED_OUTPUT_SEARCH_TREE" "$PATH_FILTER" "$OUTPUT") + + LEGACY_COLLECTION_DIR=$(dirname "$LEGACY_COLLECTION/$LEGACY_OUTPUT") + mkdir -p "$LEGACY_COLLECTION_DIR" + cp "$LEGACY_OUTPUT" "$LEGACY_COLLECTION_DIR" + MIXED_COLLECTION_DIR=$(dirname "$MIXED_COLLECTION/$MIXED_OUTPUT") + mkdir -p "$MIXED_COLLECTION_DIR" + cp "$MIXED_OUTPUT" "$MIXED_COLLECTION_DIR" + + $stub_python "bazel-bin/build/bazel/scripts/difftool/difftool.zip" \ + --level=SEVERE -v "$LEGACY_COLLECTION" "$MIXED_COLLECTION" \ + -l="$LEGACY_OUTPUT" -r="$MIXED_OUTPUT" + done +done + diff --git a/bazel/ci/dist/BUILD b/bazel/ci/dist/BUILD new file mode 100644 index 0000000..c4cd15e --- /dev/null +++ b/bazel/ci/dist/BUILD @@ -0,0 +1,12 @@ +load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir") + +# bazel run --package_path=out/soong/workspace //build/bazel/ci/dist:mainline_modules -- --dist_dir=/tmp/dist +# TODO(jingwen): use a split transition on --platforms to dist all 4 architectures in a single invocation. +copy_to_dist_dir( + name = "mainline_modules", + data = [ + "//system/timezone/apex:com.android.tzdata.apex", + "//packages/modules/adb/apex:com.android.adbd.apex", + ], + flat = True, +) diff --git a/bazel/ci/mixed_droid.sh b/bazel/ci/mixed_droid.sh new file mode 100755 index 0000000..06dcf39 --- /dev/null +++ b/bazel/ci/mixed_droid.sh @@ -0,0 +1,30 @@ +#!/bin/bash -eux +# Verifies mixed builds succeeds when building "droid". +# This verification script is designed to be used for continuous integration +# tests, though may also be used for manual developer verification. + +if [[ -z ${DIST_DIR+x} ]]; then + echo "DIST_DIR not set. Using out/dist. This should only be used for manual developer testing." + DIST_DIR="out/dist" +fi + +# Run a mixed build of "droid" +build/soong/soong_ui.bash --make-mode \ + --mk-metrics \ + BP2BUILD_VERBOSE=1 \ + USE_BAZEL_ANALYSIS=1 \ + BAZEL_STARTUP_ARGS="--max_idle_secs=5" \ + BAZEL_BUILD_ARGS="--color=no --curses=no --show_progress_rate_limit=5" \ + TARGET_PRODUCT=aosp_arm64 \ + TARGET_BUILD_VARIANT=userdebug \ + droid platform_tests \ + dist DIST_DIR=$DIST_DIR + +# Verify there are artifacts under the out directory that originated from bazel. +echo "Verifying OUT_DIR contains bazel-out..." +if find out/ -type d -name bazel-out &>/dev/null; then + echo "bazel-out found." +else + echo "bazel-out not found. This may indicate that mixed builds are silently not running." + exit 1 +fi diff --git a/bazel/ci/mixed_libc.sh b/bazel/ci/mixed_libc.sh new file mode 100755 index 0000000..e939a4e --- /dev/null +++ b/bazel/ci/mixed_libc.sh @@ -0,0 +1,41 @@ +#!/bin/bash -eux +# Verifies mixed builds succeeds when building "libc". +# This verification script is designed to be used for continuous integration +# tests, though may also be used for manual developer verification. + +if [[ -z ${DIST_DIR+x} ]]; then + echo "DIST_DIR not set. Using out/dist. This should only be used for manual developer testing." + DIST_DIR="out/dist" +fi + +TARGETS=( + libbacktrace + libfdtrack + libsimpleperf + com.android.adbd + com.android.runtime + bluetoothtbd + framework-minus-apex +) + +# Run a mixed build of "libc" +build/soong/soong_ui.bash --make-mode \ + --mk-metrics \ + BP2BUILD_VERBOSE=1 \ + USE_BAZEL_ANALYSIS=1 \ + BAZEL_STARTUP_ARGS="--max_idle_secs=5" \ + BAZEL_BUILD_ARGS="--color=no --curses=no --show_progress_rate_limit=5" \ + TARGET_PRODUCT=aosp_arm64 \ + TARGET_BUILD_VARIANT=userdebug \ + "${TARGETS[@]}" \ + dist DIST_DIR=$DIST_DIR + +# Verify there are artifacts under the out directory that originated from bazel. +echo "Verifying OUT_DIR contains bazel-out..." +if find out/ -type d -name bazel-out &>/dev/null; then + echo "bazel-out found." +else + echo "bazel-out not found. This may indicate that mixed builds are silently not running." + exit 1 +fi + diff --git a/bazel/ci/rbc_dashboard.py b/bazel/ci/rbc_dashboard.py new file mode 100755 index 0000000..2e3ef1b --- /dev/null +++ b/bazel/ci/rbc_dashboard.py @@ -0,0 +1,467 @@ +#!/usr/bin/env python3 +"""Generates a dashboard for the current RBC product/board config conversion status.""" +# pylint: disable=line-too-long + +import argparse +import asyncio +import dataclasses +import datetime +import os +import re +import shutil +import socket +import subprocess +import sys +import time +from typing import List, Tuple +import xml.etree.ElementTree as ET + +_PRODUCT_REGEX = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*)(?:-(user|userdebug|eng))?') + + +@dataclasses.dataclass(frozen=True) +class Product: + """Represents a TARGET_PRODUCT and TARGET_BUILD_VARIANT.""" + product: str + variant: str + + def __post_init__(self): + if not _PRODUCT_REGEX.match(str(self)): + raise ValueError(f'Invalid product name: {self}') + + def __str__(self): + return self.product + '-' + self.variant + + +@dataclasses.dataclass(frozen=True) +class ProductResult: + baseline_success: bool + product_success: bool + board_success: bool + product_has_diffs: bool + board_has_diffs: bool + + def success(self) -> bool: + return not self.baseline_success or ( + self.product_success and self.board_success + and not self.product_has_diffs and not self.board_has_diffs) + + +@dataclasses.dataclass(frozen=True) +class Directories: + out: str + out_baseline: str + out_product: str + out_board: str + results: str + + +def get_top() -> str: + path = '.' + while not os.path.isfile(os.path.join(path, 'build/soong/soong_ui.bash')): + if os.path.abspath(path) == '/': + sys.exit('Could not find android source tree root.') + path = os.path.join(path, '..') + return os.path.abspath(path) + + +def get_build_var(variable, product: Product) -> str: + """Returns the result of the shell command get_build_var.""" + env = { + **os.environ, + 'TARGET_PRODUCT': product.product, + 'TARGET_BUILD_VARIANT': product.variant, + } + return subprocess.run([ + 'build/soong/soong_ui.bash', + '--dumpvar-mode', + variable + ], check=True, capture_output=True, env=env, text=True).stdout.strip() + + +async def run_jailed_command(args: List[str], out_dir: str, env=None) -> bool: + """Runs a command, saves its output to out_dir/build.log, and returns if it succeeded.""" + with open(os.path.join(out_dir, 'build.log'), 'wb') as f: + result = await asyncio.create_subprocess_exec( + 'prebuilts/build-tools/linux-x86/bin/nsjail', + '-q', + '--cwd', + os.getcwd(), + '-e', + '-B', + '/', + '-B', + f'{os.path.abspath(out_dir)}:{os.path.abspath("out")}', + '--time_limit', + '0', + '--skip_setsid', + '--keep_caps', + '--disable_clone_newcgroup', + '--disable_clone_newnet', + '--rlimit_as', + 'soft', + '--rlimit_core', + 'soft', + '--rlimit_cpu', + 'soft', + '--rlimit_fsize', + 'soft', + '--rlimit_nofile', + 'soft', + '--proc_rw', + '--hostname', + socket.gethostname(), + '--', + *args, stdout=f, stderr=subprocess.STDOUT, env=env) + return await result.wait() == 0 + + +async def run_build(flags: List[str], out_dir: str) -> bool: + return await run_jailed_command([ + 'build/soong/soong_ui.bash', + '--make-mode', + *flags, + '--skip-ninja', + 'nothing' + ], out_dir) + + +async def run_config(product: Product, rbc_product: bool, rbc_board: bool, out_dir: str) -> bool: + """Runs config.mk and saves results to out/rbc_variable_dump.txt.""" + env = { + 'OUT_DIR': 'out', + 'TMPDIR': 'tmp', + 'BUILD_DATETIME_FILE': 'out/build_date.txt', + 'CALLED_FROM_SETUP': 'true', + 'TARGET_PRODUCT': product.product, + 'TARGET_BUILD_VARIANT': product.variant, + 'RBC_PRODUCT_CONFIG': 'true' if rbc_product else '', + 'RBC_BOARD_CONFIG': 'true' if rbc_board else '', + 'RBC_DUMP_CONFIG_FILE': 'out/rbc_variable_dump.txt', + } + return await run_jailed_command([ + 'prebuilts/build-tools/linux-x86/bin/ckati', + '-f', + 'build/make/core/config.mk' + ], out_dir, env=env) + + +async def has_diffs(success: bool, file_pairs: List[Tuple[str]], results_folder: str) -> bool: + """Returns true if the two out folders provided have differing ninja files.""" + if not success: + return False + results = [] + for pair in file_pairs: + name = 'soong_build.ninja' if pair[0].endswith('soong/build.ninja') else os.path.basename(pair[0]) + with open(os.path.join(results_folder, name)+'.diff', 'wb') as f: + results.append((await asyncio.create_subprocess_exec( + 'diff', + pair[0], + pair[1], + stdout=f, stderr=subprocess.STDOUT)).wait()) + + for return_code in await asyncio.gather(*results): + if return_code != 0: + return True + return False + + +def generate_html_row(num: int, product: Product, results: ProductResult): + def generate_status_cell(success: bool, diffs: bool) -> str: + message = 'Success' + if diffs: + message = 'Results differed' + if not success: + message = 'Build failed' + return f'{message}' + + return f''' + + {num} + {product if results.success() and results.baseline_success else f'{product}'} + {generate_status_cell(results.baseline_success, False)} + {generate_status_cell(results.product_success, results.product_has_diffs)} + {generate_status_cell(results.board_success, results.board_has_diffs)} + + ''' + + +def get_branch() -> str: + try: + tree = ET.parse('.repo/manifests/default.xml') + default_tag = tree.getroot().find('default') + return default_tag.get('remote') + '/' + default_tag.get('revision') + except Exception as e: # pylint: disable=broad-except + print(str(e), file=sys.stderr) + return 'Unknown' + + +def cleanup_empty_files(path): + if os.path.isfile(path): + if os.path.getsize(path) == 0: + os.remove(path) + elif os.path.isdir(path): + for subfile in os.listdir(path): + cleanup_empty_files(os.path.join(path, subfile)) + if not os.listdir(path): + os.rmdir(path) + + +async def test_one_product(product: Product, dirs: Directories) -> ProductResult: + """Runs the builds and tests for differences for a single product.""" + baseline_success, product_success, board_success = await asyncio.gather( + run_build([ + f'TARGET_PRODUCT={product.product}', + f'TARGET_BUILD_VARIANT={product.variant}', + ], dirs.out_baseline), + run_build([ + f'TARGET_PRODUCT={product.product}', + f'TARGET_BUILD_VARIANT={product.variant}', + 'RBC_PRODUCT_CONFIG=1', + ], dirs.out_product), + run_build([ + f'TARGET_PRODUCT={product.product}', + f'TARGET_BUILD_VARIANT={product.variant}', + 'RBC_BOARD_CONFIG=1', + ], dirs.out_board), + ) + + product_dashboard_folder = os.path.join(dirs.results, str(product)) + os.mkdir(product_dashboard_folder) + os.mkdir(product_dashboard_folder+'/baseline') + os.mkdir(product_dashboard_folder+'/product') + os.mkdir(product_dashboard_folder+'/board') + + if not baseline_success: + shutil.copy2(os.path.join(dirs.out_baseline, 'build.log'), + f'{product_dashboard_folder}/baseline/build.log') + if not product_success: + shutil.copy2(os.path.join(dirs.out_product, 'build.log'), + f'{product_dashboard_folder}/product/build.log') + if not board_success: + shutil.copy2(os.path.join(dirs.out_board, 'build.log'), + f'{product_dashboard_folder}/board/build.log') + + files = [f'build-{product.product}.ninja', f'build-{product.product}-package.ninja', 'soong/build.ninja'] + product_files = [(os.path.join(dirs.out_baseline, x), os.path.join(dirs.out_product, x)) for x in files] + board_files = [(os.path.join(dirs.out_baseline, x), os.path.join(dirs.out_board, x)) for x in files] + product_has_diffs, board_has_diffs = await asyncio.gather( + has_diffs(baseline_success and product_success, product_files, product_dashboard_folder+'/product'), + has_diffs(baseline_success and board_success, board_files, product_dashboard_folder+'/board')) + + # delete files that contain the product name in them to save space, + # otherwise the ninja files end up filling up the whole harddrive + for out_folder in [dirs.out_baseline, dirs.out_product, dirs.out_board]: + for subfolder in ['', 'soong']: + folder = os.path.join(out_folder, subfolder) + for file in os.listdir(folder): + if os.path.isfile(os.path.join(folder, file)) and product.product in file: + os.remove(os.path.join(folder, file)) + + cleanup_empty_files(product_dashboard_folder) + + return ProductResult(baseline_success, product_success, board_success, product_has_diffs, board_has_diffs) + + +async def test_one_product_quick(product: Product, dirs: Directories) -> ProductResult: + """Runs the builds and tests for differences for a single product.""" + baseline_success, product_success, board_success = await asyncio.gather( + run_config( + product, + False, + False, + dirs.out_baseline), + run_config( + product, + True, + False, + dirs.out_product), + run_config( + product, + False, + True, + dirs.out_board), + ) + + product_dashboard_folder = os.path.join(dirs.results, str(product)) + os.mkdir(product_dashboard_folder) + os.mkdir(product_dashboard_folder+'/baseline') + os.mkdir(product_dashboard_folder+'/product') + os.mkdir(product_dashboard_folder+'/board') + + if not baseline_success: + shutil.copy2(os.path.join(dirs.out_baseline, 'build.log'), + f'{product_dashboard_folder}/baseline/build.log') + if not product_success: + shutil.copy2(os.path.join(dirs.out_product, 'build.log'), + f'{product_dashboard_folder}/product/build.log') + if not board_success: + shutil.copy2(os.path.join(dirs.out_board, 'build.log'), + f'{product_dashboard_folder}/board/build.log') + + files = ['rbc_variable_dump.txt'] + product_files = [(os.path.join(dirs.out_baseline, x), os.path.join(dirs.out_product, x)) for x in files] + board_files = [(os.path.join(dirs.out_baseline, x), os.path.join(dirs.out_board, x)) for x in files] + product_has_diffs, board_has_diffs = await asyncio.gather( + has_diffs(baseline_success and product_success, product_files, product_dashboard_folder+'/product'), + has_diffs(baseline_success and board_success, board_files, product_dashboard_folder+'/board')) + + cleanup_empty_files(product_dashboard_folder) + + return ProductResult(baseline_success, product_success, board_success, product_has_diffs, board_has_diffs) + + +async def main(): + parser = argparse.ArgumentParser( + description='Generates a dashboard of the starlark product configuration conversion.') + parser.add_argument('products', nargs='*', + help='list of products to test. If not given, all ' + + 'products will be tested. ' + + 'Example: aosp_arm64-userdebug') + parser.add_argument('--quick', action='store_true', + help='Run a quick test. This will only run config.mk and ' + + 'diff the make variables at the end of it, instead of ' + + 'diffing the full ninja files.') + parser.add_argument('--exclude', nargs='+', default=[], + help='Exclude these producs from the build. Useful if not ' + + 'supplying a list of products manually.') + parser.add_argument('--results-directory', + help='Directory to store results in. Defaults to $(OUT_DIR)/rbc_dashboard. ' + + 'Warning: will be cleared!') + args = parser.parse_args() + + if args.results_directory: + args.results_directory = os.path.abspath(args.results_directory) + + os.chdir(get_top()) + + def str_to_product(p: str) -> Product: + match = _PRODUCT_REGEX.fullmatch(p) + if not match: + sys.exit(f'Invalid product name: {p}. Example: aosp_arm64-userdebug') + return Product(match.group(1), match.group(2) if match.group(2) else 'userdebug') + + products = [str_to_product(p) for p in args.products] + + if not products: + products = list(map(lambda x: Product(x, 'userdebug'), get_build_var( + 'all_named_products', Product('aosp_arm64', 'userdebug')).split())) + + excluded = [str_to_product(p) for p in args.exclude] + products = [p for p in products if p not in excluded] + + for i, product in enumerate(products): + for j, product2 in enumerate(products): + if i != j and product.product == product2.product: + sys.exit(f'Product {product.product} cannot be repeated.') + + out_dir = get_build_var('OUT_DIR', Product('aosp_arm64', 'userdebug')) + + dirs = Directories( + out=out_dir, + out_baseline=os.path.join(out_dir, 'rbc_out_baseline'), + out_product=os.path.join(out_dir, 'rbc_out_product'), + out_board=os.path.join(out_dir, 'rbc_out_board'), + results=args.results_directory if args.results_directory else os.path.join(out_dir, 'rbc_dashboard')) + + for folder in [dirs.out_baseline, dirs.out_product, dirs.out_board, dirs.results]: + # delete and recreate the out directories. You can't reuse them for + # a particular product, because after we delete some product-specific + # files inside the out dir to save space, the build will fail if you + # try to build the same product again. + shutil.rmtree(folder, ignore_errors=True) + os.makedirs(folder) + + # When running in quick mode, we still need to build + # mk2rbc/rbcrun/AndroidProducts.mk.list, so run a get_build_var command to do + # that in each folder. + if args.quick: + commands = [] + for folder in [dirs.out_baseline, dirs.out_product, dirs.out_board]: + commands.append(run_jailed_command([ + 'build/soong/soong_ui.bash', + '--dumpvar-mode', + 'TARGET_PRODUCT' + ], folder)) + for success in await asyncio.gather(*commands): + if not success: + sys.exit('Failed to setup output directories') + + with open(os.path.join(dirs.results, 'index.html'), 'w') as f: + f.write(f''' + +

RBC Product/Board conversion status

+ Generated on {datetime.date.today()} for branch {get_branch()} + + + + + + + + \n''') + f.flush() + + all_results = [] + start_time = time.time() + print(f'{"Current product":31.31} | {"Time Elapsed":>16} | {"Per each":>8} | {"ETA":>16} | Status') + print('-' * 91) + for i, product in enumerate(products): + if i > 0: + elapsed_time = time.time() - start_time + time_per_product = elapsed_time / i + eta = time_per_product * (len(products) - i) + elapsed_time_str = str(datetime.timedelta(seconds=int(elapsed_time))) + time_per_product_str = str(datetime.timedelta(seconds=int(time_per_product))) + eta_str = str(datetime.timedelta(seconds=int(eta))) + print(f'{f"{i+1}/{len(products)} {product}":31.31} | {elapsed_time_str:>16} | {time_per_product_str:>8} | {eta_str:>16} | ', end='', flush=True) + else: + print(f'{f"{i+1}/{len(products)} {product}":31.31} | {"":>16} | {"":>8} | {"":>16} | ', end='', flush=True) + + if not args.quick: + result = await test_one_product(product, dirs) + else: + result = await test_one_product_quick(product, dirs) + + all_results.append(result) + + if result.success(): + print('Success') + else: + print('Failure') + + f.write(generate_html_row(i+1, product, result)) + f.flush() + + baseline_successes = len([x for x in all_results if x.baseline_success]) + product_successes = len([x for x in all_results if x.product_success and not x.product_has_diffs]) + board_successes = len([x for x in all_results if x.board_success and not x.board_has_diffs]) + f.write(f''' + + + + + + + + + + + + + + +
#productbaselineRBC product configRBC board config
# Successful{baseline_successes}{product_successes}{board_successes}
# FailedN/A{baseline_successes - product_successes}{baseline_successes - board_successes}
+ Finished running successfully. + \n''') + + print('Success!') + print('file://'+os.path.abspath(os.path.join(dirs.results, 'index.html'))) + + for result in all_results: + if result.baseline_success and not result.success(): + print('There were one or more failing products. See the html report for details.') + sys.exit(1) + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/bazel/ci/rbc_regression_test.sh b/bazel/ci/rbc_regression_test.sh new file mode 100755 index 0000000..b1f4e57 --- /dev/null +++ b/bazel/ci/rbc_regression_test.sh @@ -0,0 +1,92 @@ +#!/bin/bash -u +# Regression test for the product and/or board configuration converter. +# +# Builds 'nothing' for a given product-variant twice: with product/board +# config makefiles converted to Starlark, and without such conversion. +# The generated Ninja files should be the same. +set -u + +function die() { + echo $@ >&2 + exit 1 +} + +function usage() { + cat <&2 +Usage: $myname [-p] [-b] [-q] [-r] [product-variant ...] + -p: Test RBC product configuration. This is implied if -b is not supplied + -b: Test RBC board configuration. This is implied if -p is not supplied + -q: Quiet. Suppress all output other than a failure message + -r: Retain Ninja files +EOF + exit 1 +} + +function build() { + local -r flavor="$1" + local -r product="$2" + local -r variant="$3" + shift 3 + command="build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant $@ nothing" + if ! ANDROID_QUIET_BUILD=$quiet $command; then + printf "%s-%s: %s build failed, actual command:\n %s\n" $product $variant $flavor "$command" >&2 + exit 1 + fi +} + +mypath=$(realpath "$0") +declare -r mydir=${mypath%/*/*/*/*} +declare -r myname=${mypath#${mydir}/} + +flags_rbc=() +quiet= +while getopts "bkpqr" o; do + case "${o}" in + k) ;; # backward compatibility to be removed later + q) quiet=true ;; + b) flags_rbc+=(RBC_BOARD_CONFIG=true) ;; + p) flags_rbc+=(RBC_PRODUCT_CONFIG=true) ;; + r) retain_files=t ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) +[[ $# -gt 0 ]] || usage +((${#flags_rbc[@]})) || flags_rbc+=(RBC_PRODUCT_CONFIG=true RBC_BOARD_CONFIG=true) + +cd $mydir +rc=0 +for arg in $@; do + [[ "$arg" =~ ^([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)$ ]] || \ + die "Invalid product name: $arg. Example: aosp_arm64-userdebug" + product="${BASH_REMATCH[1]}" + variant="${BASH_REMATCH[2]}" + ninja_files=(soong/build.ninja build-${product}.ninja build-${product}-package.ninja) + + # Build with converter, save Ninja files, build without it. + saved_ninja_dir=out/ninja_rbc/${product}-${variant} + build RBC $product $variant ${flags_rbc[@]} && \ + rm -rf $saved_ninja_dir && mkdir -p $saved_ninja_dir/soong && \ + (for f in ${ninja_files[@]}; do mv -f out/$f $saved_ninja_dir/$f || exit 1; done) && \ + build baseline $product $variant + rc=$? + + # Compare Ninja files + if ((rc==0)); then + for f in "${ninja_files[@]}"; do + diff_file=$(mktemp) + diff out/$f $saved_ninja_dir/$f | head >& $diff_file + if [[ -s $diff_file ]]; then + echo ${product}-${variant}: "$f" is different '< make, > RBC):' >&2 + cat $diff_file >&2 + echo ... + rc=1 + fi + rm $diff_file + done + fi + [[ -n "${retain_files:-}" ]] || rm -rf $saved_ninja_dir +done + +((rc==0)) || printf "In order to reproduce the failures above, run\n %s -\n" $myname >&2 +exit $rc diff --git a/bazel/common.bazelrc b/bazel/common.bazelrc new file mode 100644 index 0000000..368efa2 --- /dev/null +++ b/bazel/common.bazelrc @@ -0,0 +1,109 @@ +# Platforms and toolchains for AOSP. +# +# Set default target platform for builds to rely on product config's arch and os variables +build --platforms //build/bazel/platforms:android_target + +# Use the target platform (android_x86, android_arm) in the bazel-out/ output +# directory name fragment instead of the CPU (darwin, k8). This avoids +# thrashing the output directory when switching between top level target +# --platforms values. +build --experimental_platform_in_output_dir + +# Use toolchain resolution to find the cc toolchain. +build --incompatible_enable_cc_toolchain_resolution + +# Ensure that the host_javabase always use @local_jdk, the checked-in JDK. +build --tool_java_runtime_version=local_jdk +build --java_runtime_version=local_jdk + +# Lock down the PATH variable in actions to /usr/bin and /usr/local/bin. +build --experimental_strict_action_env + +# Explicitly allow unresolved symlinks (it's an experimental Bazel feature) +build --experimental_allow_unresolved_symlinks + +# Enable usage of experimental cc-related build APIs +build --experimental_cc_shared_library +build --experimental_starlark_cc_import + +# Do not tokenize copts, other than strings that consist of a single Make +# variable. This prevents the need to double-escape characters like backslashes +# and quotes in copts. +build --features no_copts_tokenization + +# Disable local cpp toolchain detection, as it is explicitly declared in AOSP. +build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 + +build --proto_compiler=//external/protobuf:aprotoc + +# Disable sandboxing for CppCompile actions, as headers are not fully specified. +# TODO(b/186116353): This is a temporary fix, as appropriately-sandboxed actions +# are a long term goal. +build --strategy=CppCompile=standalone + +# Enable use of the implementation_deps attribute in native cc rules +build --experimental_cc_implementation_deps + +# Enable building targets in //external:__subpackages__. +common --experimental_sibling_repository_layout +common --experimental_disable_external_package + +# Enable toplevel_output_directories and Ninja executor in Bazel +common --experimental_ninja_actions + +# Increase refresh rate of command line UI for improved perceived responsiveness. +common --show_progress_rate_limit=0.05 + +# These are disabled when running under soong_ui (default = auto). Force enable them here. +common --color=yes +common --curses=yes + +# Show the full set of flags for observability and debuggability. +common --announce_rc + +# Run bazel query from the workspace, without cd'ing into out/soong/queryview +# Note that this hardcodes the output dir. It will not work if $OUT_DIR != out. +common:queryview --package_path=%workspace%/out/soong/queryview + +# Run bazel query from the workspace, without cd'ing into out/soong/workspace +# Note that this hardcodes the output dir. It will not work if $OUT_DIR != out. +common:bp2build --package_path=%workspace%/out/soong/workspace + +# Configurations specific to CI builds, generally to improve signal-to-noise ratio in server logs. +common:ci --color=no +common:ci --curses=no +common:ci --show_progress_rate_limit=5 +common:ci --noshow_loading_progress +test:ci --keep_going +test:ci --test_output=errors + +# Support a local user-specific bazelrc file. +try-import %workspace%/user.bazelrc + +build --android_sdk=//prebuilts/sdk:android_sdk +build --experimental_enable_android_migration_apis +build --experimental_google_legacy_api +build --incompatible_java_common_parameters +build --android_databinding_use_v3_4_args +build --experimental_android_databinding_v2 +build --define=android_incremental_dexing_tool=d8_dexbuilder +build --define=android_dexmerger_tool=d8_dexmerger +build --nouse_workers_with_dexbuilder +build --fat_apk_cpu=k8 + +# TODO(b/199038020): Use a python_toolchain when we have Starlark rules_python. +# This also means all python scripts are using py3 runtime. +build --python_top=//prebuilts/build-tools:python3 +build --noincompatible_use_python_toolchains + +# Developer instance for result storage. This only works if you have access +# to the Bazel GCP project. Follow the GCP gcloud client's auth instructions to +# use --google_default_credentials. +build:results --remote_instance_name=projects/bazel-untrusted/instances/default_instance +build:results --project_id=bazel-untrusted +build:results --remote_timeout=600 +build:results --google_default_credentials +build:results --test_summary=detailed +build:results --bes_backend=buildeventservice.googleapis.com +build:results --bes_results_url=https://source.cloud.google.com/results/invocations +build:results --show_progress_rate_limit=5 diff --git a/bazel/darwin.bazelrc b/bazel/darwin.bazelrc new file mode 100644 index 0000000..0f86da1 --- /dev/null +++ b/bazel/darwin.bazelrc @@ -0,0 +1,3 @@ +import %workspace%/build/bazel/common.bazelrc + +build --host_platform //build/bazel/platforms:darwin_x86_64 diff --git a/bazel/docs/concepts.md b/bazel/docs/concepts.md new file mode 100644 index 0000000..b9f8c24 --- /dev/null +++ b/bazel/docs/concepts.md @@ -0,0 +1,198 @@ +# Android Build System Concepts + +This document provides high level explanations and mapping of the internal +build system components and concepts of the Android build system and Bazel, +and how components communicate with each other. + +For implementation concepts, see: +https://android.googlesource.com/platform/build/bazel/+/refs/heads/master/docs/internal_concepts.md. + +## High level components + +This table provides a high level overview of the components in the current +Android Platform build system, and how each component maps to a concept in +Bazel. + +|Android build system component|Description|Mapping to Bazel concepts| +|---|---|---| +|Kati|Make-compatible front-end. Encodes build logic in `.mk` scripts. Declares buildable units in `Android.mk`. Generates Ninja file directly.|Loading and analysis phase. Conceptually similar to `bazel build --nobuild`.| +|Blueprint|Build definition syntax. Build syntax parser. Internal data structures like Modules/Variations/Context/Scope. Ninja file generator.|Starlark.| +|Soong|Bazel-like front-end. Encodes build logic in Go. Declares build units in `Android.bp`, parsed by Blueprint. Uses Blueprint to generate Ninja file. Generates a `.mk` file with prebuilt module stubs to Kati.|Loading and analysis phase. Conceptually similar to `bazel build --nobuild command`.| +|Ninja|Serialized command line action graph executor. Executes Ninja graph generated from Kati and Soong.|Bazel's execution phase.| +|atest|Test executor and orchestrator.|Conceptually similar to `bazel test` command.| +|Blueprint + Kati + Soong + Ninja + atest|The entire build pipeline for Android.|Conceptually similar to `bazel build` or `bazel test` commands.| +|` + + + + +
+

Table of contents

+ +
+
+
+ + + + diff --git a/bazel_common_rules/docs/insert_resource.py b/bazel_common_rules/docs/insert_resource.py new file mode 100755 index 0000000..02260c2 --- /dev/null +++ b/bazel_common_rules/docs/insert_resource.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import argparse +import base64 +import io +import os + +MAGIC = "\n" + + +def main(infile, outfile, resources): + """Embed resources into infile at the line ``.""" + inlines = infile.readlines() + magic = inlines.index(MAGIC) + + outlines = inlines[:magic] + for resource_name in resources: + outlines.append('\n') + outlines += inlines[magic:] + + outfile.writelines(outlines) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=main.__doc__) + parser.add_argument("--infile", required=True, type=argparse.FileType('r'), help="input file") + parser.add_argument("--outfile", required=True, type=argparse.FileType('w'), help="output file") + parser.add_argument("resources", nargs='+', help="resource files") + args = parser.parse_args() + main(**vars(args)) diff --git a/bazel_common_rules/docs/templates/func.vm b/bazel_common_rules/docs/templates/func.vm new file mode 100644 index 0000000..50aafe4 --- /dev/null +++ b/bazel_common_rules/docs/templates/func.vm @@ -0,0 +1,21 @@ +#[[#]]# `${funcInfo.functionName}` + +
+${util.funcSummary($funcInfo)}
+
+ +${funcInfo.docString} + +#[[##]]# Parameters + +#if (!$funcInfo.getParameterList().isEmpty()) + +#foreach ($param in $funcInfo.getParameterList()) + **`${param.name}`** +#if(${param.mandatory}) *Required.* #else *Optional.* #end #if(!${param.getDefaultValue().isEmpty()}) *Default is* `${param.getDefaultValue()}`. #end ${param.docString} + +#end +#else +No parameters. + +#end diff --git a/bazel_common_rules/docs/templates/provider.vm b/bazel_common_rules/docs/templates/provider.vm new file mode 100644 index 0000000..ed1804f --- /dev/null +++ b/bazel_common_rules/docs/templates/provider.vm @@ -0,0 +1,20 @@ +#[[#]]# `${providerName}` + +
+${util.providerSummary($providerName, $providerInfo)}
+
+ +${providerInfo.docString} + +#[[##]]# Fields + +#if (!$providerInfo.fieldInfoList.isEmpty()) + +#foreach ($field in $providerInfo.fieldInfoList) +
**`${field.name}`** +${field.docString} + +#end +#else +No fields. +#end diff --git a/bazel_common_rules/docs/templates/rule.vm b/bazel_common_rules/docs/templates/rule.vm new file mode 100644 index 0000000..7ad7e93 --- /dev/null +++ b/bazel_common_rules/docs/templates/rule.vm @@ -0,0 +1,21 @@ +#[[#]]# `${ruleName}` + +
+${util.ruleSummary($ruleName, $ruleInfo)}
+
+ +${ruleInfo.docString} + +#[[##]]# Attributes + +#if (!$ruleInfo.getAttributeList().isEmpty()) + +#foreach ($attribute in $ruleInfo.getAttributeList()) +
**`${attribute.name}`** +*${util.attributeTypeString($attribute)}.* #if(${attribute.mandatory}) *Required.* #else *Optional.* #end #if(!${attribute.defaultValue.isEmpty()}) *Default is* `${attribute.defaultValue}`. #end $attribute.docString + +#end +#else +No attributes. + +#end diff --git a/blueprint/.github/workflows/build.yml b/blueprint/.github/workflows/build.yml new file mode 100644 index 0000000..deb261d --- /dev/null +++ b/blueprint/.github/workflows/build.yml @@ -0,0 +1,47 @@ +name: build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + + build: + runs-on: ubuntu-latest + strategy: + matrix: + go: [ '1.14', '1.13' ] + name: Build and test on go ${{ matrix.go }} + steps: + + - name: Set up Go ${{ matrix.go }} + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go }} + id: go + + - name: Check out code + uses: actions/checkout@v2 + + - name: Install ninja + run: | + mkdir -p ${GITHUB_WORKSPACE}/ninja-bin; cd ${GITHUB_WORKSPACE}/ninja-bin + wget https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-linux.zip + unzip ninja-linux.zip + rm ninja-linux.zip + echo "${GITHUB_WORKSPACE}/ninja-bin" >> $GITHUB_PATH + + - name: Run gofmt + run: ./.gofmt.sh + + - name: Test + run: go test ./... + + - name: Test with race detector + run: go test -race -short ./... + + - run: ./tests/test.sh + - run: ./tests/test_tree_tests.sh + - run: ./tests/test_tree_tests.sh -t diff --git a/blueprint/.gitignore b/blueprint/.gitignore new file mode 100644 index 0000000..d2cc8ff --- /dev/null +++ b/blueprint/.gitignore @@ -0,0 +1,4 @@ +out.test +src.test +*.iml +.idea/ diff --git a/blueprint/.gofmt.sh b/blueprint/.gofmt.sh new file mode 100755 index 0000000..3aa9374 --- /dev/null +++ b/blueprint/.gofmt.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -n "$(gofmt -l .)" ]; then + echo "Go code is not formatted:" + gofmt -d . + exit 1 +fi diff --git a/blueprint/Android.bp b/blueprint/Android.bp new file mode 100644 index 0000000..c84d04a --- /dev/null +++ b/blueprint/Android.bp @@ -0,0 +1,230 @@ +package { + default_applicable_licenses: ["build_blueprint_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "build_blueprint_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + license_text: [ + "LICENSE", + ], +} + +bootstrap_go_package { + name: "blueprint", + deps: [ + "blueprint-metrics", + "blueprint-parser", + "blueprint-pathtools", + "blueprint-proptools", + ], + pkgPath: "github.com/google/blueprint", + srcs: [ + "context.go", + "glob.go", + "live_tracker.go", + "mangle.go", + "module_ctx.go", + "name_interface.go", + "ninja_defs.go", + "ninja_strings.go", + "ninja_writer.go", + "package_ctx.go", + "provider.go", + "scope.go", + "singleton_ctx.go", + ], + testSrcs: [ + "context_test.go", + "glob_test.go", + "module_ctx_test.go", + "ninja_strings_test.go", + "ninja_writer_test.go", + "provider_test.go", + "splice_modules_test.go", + "visit_test.go", + ], +} + +bootstrap_go_package { + name: "blueprint-parser", + pkgPath: "github.com/google/blueprint/parser", + srcs: [ + "parser/ast.go", + "parser/modify.go", + "parser/parser.go", + "parser/printer.go", + "parser/sort.go", + ], + testSrcs: [ + "parser/modify_test.go", + "parser/parser_test.go", + "parser/printer_test.go", + "parser/sort_test.go", + ], +} + +bootstrap_go_package { + name: "blueprint-deptools", + pkgPath: "github.com/google/blueprint/deptools", + srcs: ["deptools/depfile.go"], +} + +bootstrap_go_package { + name: "blueprint-pathtools", + pkgPath: "github.com/google/blueprint/pathtools", + deps: [ + "blueprint-deptools", + ], + srcs: [ + "pathtools/lists.go", + "pathtools/fs.go", + "pathtools/glob.go", + ], + testSrcs: [ + "pathtools/fs_test.go", + "pathtools/glob_test.go", + "pathtools/lists_test.go", + ], +} + +bootstrap_go_package { + name: "blueprint-proptools", + pkgPath: "github.com/google/blueprint/proptools", + deps: [ + "blueprint-parser", + ], + srcs: [ + "proptools/clone.go", + "proptools/escape.go", + "proptools/extend.go", + "proptools/filter.go", + "proptools/proptools.go", + "proptools/tag.go", + "proptools/typeequal.go", + "proptools/unpack.go", + ], + testSrcs: [ + "proptools/clone_test.go", + "proptools/escape_test.go", + "proptools/extend_test.go", + "proptools/filter_test.go", + "proptools/tag_test.go", + "proptools/typeequal_test.go", + "proptools/unpack_test.go", + ], +} + +bootstrap_go_package { + name: "blueprint-bootstrap", + deps: [ + "blueprint", + "blueprint-deptools", + "blueprint-pathtools", + "blueprint-bootstrap-bpdoc", + ], + pkgPath: "github.com/google/blueprint/bootstrap", + srcs: [ + "bootstrap/bootstrap.go", + "bootstrap/command.go", + "bootstrap/config.go", + "bootstrap/glob.go", + "bootstrap/writedocs.go", + ], +} + +bootstrap_go_package { + name: "blueprint-bootstrap-bpdoc", + deps: [ + "blueprint", + "blueprint-proptools", + ], + pkgPath: "github.com/google/blueprint/bootstrap/bpdoc", + srcs: [ + "bootstrap/bpdoc/bpdoc.go", + "bootstrap/bpdoc/properties.go", + "bootstrap/bpdoc/reader.go", + ], + testSrcs: [ + "bootstrap/bpdoc/bpdoc_test.go", + "bootstrap/bpdoc/properties_test.go", + "bootstrap/bpdoc/reader_test.go", + ], +} + +blueprint_go_binary { + name: "bpglob", + deps: ["blueprint-pathtools"], + srcs: ["bootstrap/bpglob/bpglob.go"], +} + +blueprint_go_binary { + name: "bpfmt", + deps: ["blueprint-parser"], + srcs: ["bpfmt/bpfmt.go"], +} + +blueprint_go_binary { + name: "bpmodify", + deps: ["blueprint-parser"], + srcs: ["bpmodify/bpmodify.go"], +} + +blueprint_go_binary { + name: "gotestmain", + srcs: ["gotestmain/gotestmain.go"], +} + +// gotestmain tests can't be on the gotestmain module because it is an implicit dependency of tests. +// Put the tests in their own package and make it a dependency of minibp to make sure they run. +bootstrap_go_package { + name: "gotestmain-tests", + pkgPath: "github.com/google/blueprint/gotestmain", + srcs: [ + "gotestmain/dummy.go", + ], + testSrcs: [ + "gotestmain/testmain_test.go", + ], +} + +blueprint_go_binary { + name: "gotestrunner", + srcs: ["gotestrunner/gotestrunner.go"], +} + +blueprint_go_binary { + name: "loadplugins", + srcs: ["loadplugins/loadplugins.go"], +} + +blueprint_go_binary { + name: "microfactory", + deps: ["blueprint-microfactory"], + srcs: ["microfactory/main/main.go"], +} + +bootstrap_go_package { + name: "blueprint-microfactory", + pkgPath: "github.com/google/blueprint/microfactory", + srcs: ["microfactory/microfactory.go"], + testSrcs: ["microfactory/microfactory_test.go"], +} diff --git a/blueprint/CODEOWNERS b/blueprint/CODEOWNERS new file mode 100644 index 0000000..8cf6944 --- /dev/null +++ b/blueprint/CODEOWNERS @@ -0,0 +1 @@ +* @google/blueprint diff --git a/blueprint/CONTRIBUTING.md b/blueprint/CONTRIBUTING.md new file mode 100644 index 0000000..1ba8539 --- /dev/null +++ b/blueprint/CONTRIBUTING.md @@ -0,0 +1,24 @@ +Want to contribute? Great! First, read this page (including the small print at the end). + +### Before you contribute +Before we can use your code, you must sign the +[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) +(CLA), which you can do online. The CLA is necessary mainly because you own the +copyright to your changes, even after your contribution becomes part of our +codebase, so we need your permission to use and distribute your code. We also +need to be sure of various other things—for instance that you'll tell us if you +know that your code infringes on other people's patents. You don't have to sign +the CLA until after you've submitted your code for review and a member has +approved it, but you must do it before we can put your code into our codebase. +Before you start working on a larger contribution, you should get in touch with +us first through the issue tracker with your idea so that we can help out and +possibly guide you. Coordinating up front makes it much easier to avoid +frustration later on. + +### Code reviews +All submissions, including submissions by project members, require review. We +use Github pull requests for this purpose. + +### The small print +Contributions made by corporations are covered by a different agreement than +the one above, the Software Grant and Corporate Contributor License Agreement. diff --git a/blueprint/LICENSE b/blueprint/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/blueprint/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/blueprint/OWNERS b/blueprint/OWNERS new file mode 100644 index 0000000..1ee860c --- /dev/null +++ b/blueprint/OWNERS @@ -0,0 +1,2 @@ +include platform/build/soong:/OWNERS + diff --git a/blueprint/PREUPLOAD.cfg b/blueprint/PREUPLOAD.cfg new file mode 100644 index 0000000..317f5c4 --- /dev/null +++ b/blueprint/PREUPLOAD.cfg @@ -0,0 +1,6 @@ +[Builtin Hooks] +gofmt = true +bpfmt = true + +[Hook Scripts] +do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT} diff --git a/blueprint/README.md b/blueprint/README.md new file mode 100644 index 0000000..961bc64 --- /dev/null +++ b/blueprint/README.md @@ -0,0 +1,25 @@ +Blueprint Build System +====================== + +Blueprint is being archived on 2021 May 3. + +On 2021 May 3, we will be archiving the Blueprint project. This means it will +not be possible to file new issues or open new pull requests for this GitHub +project. As the project is being archived, patches -- including security +patches -- will not be applied after May 3. The source tree will remain +available, but changes to Blueprint in AOSP will not be merged here and +Blueprint's source tree in AOSP will eventually stop being usable outside of +Android. + +Whereas there are no meta-build systems one can use as a drop-in replacement for +Blueprint, there are a number of build systems that can be used: + +* [Bazel](https://bazel.build), Google's multi-language build tool to build and + test software of any size, quickly and reliably +* [Soong](https://source.android.com/setup/build), for building the Android + operating system itself +* [CMake](https://cmake.org), an open-source, cross-platform family of tools + designed to build, test and package software +* [Buck](https://buck.build), a fast build system that encourages the creation + of small, reusable modules over a variety of platforms and languages +* The venerable [GNU Make](https://www.gnu.org/software/make/) diff --git a/blueprint/bootstrap/bootstrap.go b/blueprint/bootstrap/bootstrap.go new file mode 100644 index 0000000..ceeee19 --- /dev/null +++ b/blueprint/bootstrap/bootstrap.go @@ -0,0 +1,761 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bootstrap + +import ( + "fmt" + "go/build" + "path/filepath" + "runtime" + "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/pathtools" +) + +var ( + pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap") + + goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join("$ToolDir", "gotestmain")) + goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join("$ToolDir", "gotestrunner")) + pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join("$ToolDir", "loadplugins")) + + parallelCompile = pctx.StaticVariable("parallelCompile", func() string { + // Parallel compilation is only supported on >= go1.9 + for _, r := range build.Default.ReleaseTags { + if r == "go1.9" { + numCpu := runtime.NumCPU() + // This will cause us to recompile all go programs if the + // number of cpus changes. We don't get a lot of benefit from + // higher values, so cap this to make it cheaper to move trees + // between machines. + if numCpu > 8 { + numCpu = 8 + } + return fmt.Sprintf("-c %d", numCpu) + } + } + return "" + }()) + + compile = pctx.StaticRule("compile", + blueprint.RuleParams{ + Command: "GOROOT='$goRoot' $compileCmd $parallelCompile -o $out.tmp " + + "$debugFlags -p $pkgPath -complete $incFlags -pack $in && " + + "if cmp --quiet $out.tmp $out; then rm $out.tmp; else mv -f $out.tmp $out; fi", + CommandDeps: []string{"$compileCmd"}, + Description: "compile $out", + Restat: true, + }, + "pkgPath", "incFlags") + + link = pctx.StaticRule("link", + blueprint.RuleParams{ + Command: "GOROOT='$goRoot' $linkCmd -o $out.tmp $libDirFlags $in && " + + "if cmp --quiet $out.tmp $out; then rm $out.tmp; else mv -f $out.tmp $out; fi", + CommandDeps: []string{"$linkCmd"}, + Description: "link $out", + Restat: true, + }, + "libDirFlags") + + goTestMain = pctx.StaticRule("gotestmain", + blueprint.RuleParams{ + Command: "$goTestMainCmd -o $out -pkg $pkg $in", + CommandDeps: []string{"$goTestMainCmd"}, + Description: "gotestmain $out", + }, + "pkg") + + pluginGenSrc = pctx.StaticRule("pluginGenSrc", + blueprint.RuleParams{ + Command: "$pluginGenSrcCmd -o $out -p $pkg $plugins", + CommandDeps: []string{"$pluginGenSrcCmd"}, + Description: "create $out", + }, + "pkg", "plugins") + + test = pctx.StaticRule("test", + blueprint.RuleParams{ + Command: "$goTestRunnerCmd -p $pkgSrcDir -f $out -- $in -test.short", + CommandDeps: []string{"$goTestRunnerCmd"}, + Description: "test $pkg", + }, + "pkg", "pkgSrcDir") + + cp = pctx.StaticRule("cp", + blueprint.RuleParams{ + Command: "cp $in $out", + Description: "cp $out", + }, + "generator") + + bootstrap = pctx.StaticRule("bootstrap", + blueprint.RuleParams{ + Command: "BUILDDIR=$soongOutDir $bootstrapCmd -i $in", + CommandDeps: []string{"$bootstrapCmd"}, + Description: "bootstrap $in", + Generator: true, + }) + + touch = pctx.StaticRule("touch", + blueprint.RuleParams{ + Command: "touch $out", + Description: "touch $out", + }, + "depfile", "generator") + + generateBuildNinja = pctx.StaticRule("build.ninja", + blueprint.RuleParams{ + // TODO: it's kinda ugly that some parameters are computed from + // environment variables and some from Ninja parameters, but it's probably + // better to not to touch that while Blueprint and Soong are separate + // NOTE: The spaces at EOL are important because otherwise Ninja would + // omit all spaces between the different options. + Command: `cd "$$(dirname "$builder")" && ` + + `BUILDER="$$PWD/$$(basename "$builder")" && ` + + `cd / && ` + + `env -i $env "$$BUILDER" ` + + ` --top "$$TOP" ` + + ` --soong_out "$soongOutDir" ` + + ` --out "$outDir" ` + + ` $extra`, + CommandDeps: []string{"$builder"}, + Description: "$builder $out", + Deps: blueprint.DepsGCC, + Depfile: "$out.d", + Restat: true, + }, + "builder", "env", "extra", "pool") + + // Work around a Ninja issue. See https://github.com/martine/ninja/pull/634 + phony = pctx.StaticRule("phony", + blueprint.RuleParams{ + Command: "# phony $out", + Description: "phony $out", + Generator: true, + }, + "depfile") + + _ = pctx.VariableFunc("ToolDir", func(config interface{}) (string, error) { + return config.(BootstrapConfig).HostToolDir(), nil + }) +) + +type GoBinaryTool interface { + InstallPath() string + + // So that other packages can't implement this interface + isGoBinary() +} + +func pluginDeps(ctx blueprint.BottomUpMutatorContext) { + if pkg, ok := ctx.Module().(*goPackage); ok { + if ctx.PrimaryModule() == ctx.Module() { + for _, plugin := range pkg.properties.PluginFor { + ctx.AddReverseDependency(ctx.Module(), nil, plugin) + } + } + } +} + +type goPackageProducer interface { + GoPkgRoot() string + GoPackageTarget() string + GoTestTargets() []string +} + +func isGoPackageProducer(module blueprint.Module) bool { + _, ok := module.(goPackageProducer) + return ok +} + +type goPluginProvider interface { + GoPkgPath() string + IsPluginFor(string) bool +} + +func isGoPluginFor(name string) func(blueprint.Module) bool { + return func(module blueprint.Module) bool { + if plugin, ok := module.(goPluginProvider); ok { + return plugin.IsPluginFor(name) + } + return false + } +} + +func IsBootstrapModule(module blueprint.Module) bool { + _, isPackage := module.(*goPackage) + _, isBinary := module.(*goBinary) + return isPackage || isBinary +} + +func isBootstrapBinaryModule(module blueprint.Module) bool { + _, isBinary := module.(*goBinary) + return isBinary +} + +// A goPackage is a module for building Go packages. +type goPackage struct { + blueprint.SimpleName + properties struct { + Deps []string + PkgPath string + Srcs []string + TestSrcs []string + PluginFor []string + + Darwin struct { + Srcs []string + TestSrcs []string + } + Linux struct { + Srcs []string + TestSrcs []string + } + } + + // The root dir in which the package .a file is located. The full .a file + // path will be "packageRoot/PkgPath.a" + pkgRoot string + + // The path of the .a file that is to be built. + archiveFile string + + // The path of the test result file. + testResultFile []string +} + +var _ goPackageProducer = (*goPackage)(nil) + +func newGoPackageModuleFactory() func() (blueprint.Module, []interface{}) { + return func() (blueprint.Module, []interface{}) { + module := &goPackage{} + return module, []interface{}{&module.properties, &module.SimpleName.Properties} + } +} + +func (g *goPackage) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string { + if ctx.Module() != ctx.PrimaryModule() { + return nil + } + return g.properties.Deps +} + +func (g *goPackage) GoPkgPath() string { + return g.properties.PkgPath +} + +func (g *goPackage) GoPkgRoot() string { + return g.pkgRoot +} + +func (g *goPackage) GoPackageTarget() string { + return g.archiveFile +} + +func (g *goPackage) GoTestTargets() []string { + return g.testResultFile +} + +func (g *goPackage) IsPluginFor(name string) bool { + for _, plugin := range g.properties.PluginFor { + if plugin == name { + return true + } + } + return false +} + +func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { + // Allow the primary builder to create multiple variants. Any variants after the first + // will copy outputs from the first. + if ctx.Module() != ctx.PrimaryModule() { + primary := ctx.PrimaryModule().(*goPackage) + g.pkgRoot = primary.pkgRoot + g.archiveFile = primary.archiveFile + g.testResultFile = primary.testResultFile + return + } + + var ( + name = ctx.ModuleName() + hasPlugins = false + pluginSrc = "" + genSrcs = []string{} + ) + + if g.properties.PkgPath == "" { + ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name) + return + } + + g.pkgRoot = packageRoot(ctx) + g.archiveFile = filepath.Join(g.pkgRoot, + filepath.FromSlash(g.properties.PkgPath)+".a") + + ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), + func(module blueprint.Module) { hasPlugins = true }) + if hasPlugins { + pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go") + genSrcs = append(genSrcs, pluginSrc) + } + + if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc) { + return + } + + var srcs, testSrcs []string + if runtime.GOOS == "darwin" { + srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...) + testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...) + } else if runtime.GOOS == "linux" { + srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...) + testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...) + } + + if ctx.Config().(BootstrapConfig).RunGoTests() { + testArchiveFile := filepath.Join(testRoot(ctx), + filepath.FromSlash(g.properties.PkgPath)+".a") + g.testResultFile = buildGoTest(ctx, testRoot(ctx), testArchiveFile, + g.properties.PkgPath, srcs, genSrcs, testSrcs) + } + + // Don't build for test-only packages + if len(srcs) == 0 && len(genSrcs) == 0 { + ctx.Build(pctx, blueprint.BuildParams{ + Rule: touch, + Outputs: []string{g.archiveFile}, + Optional: true, + }) + return + } + + buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile, + srcs, genSrcs) +} + +// A goBinary is a module for building executable binaries from Go sources. +type goBinary struct { + blueprint.SimpleName + properties struct { + Deps []string + Srcs []string + TestSrcs []string + PrimaryBuilder bool + Default bool + + Darwin struct { + Srcs []string + TestSrcs []string + } + Linux struct { + Srcs []string + TestSrcs []string + } + } + + installPath string +} + +var _ GoBinaryTool = (*goBinary)(nil) + +func newGoBinaryModuleFactory() func() (blueprint.Module, []interface{}) { + return func() (blueprint.Module, []interface{}) { + module := &goBinary{} + return module, []interface{}{&module.properties, &module.SimpleName.Properties} + } +} + +func (g *goBinary) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string { + if ctx.Module() != ctx.PrimaryModule() { + return nil + } + return g.properties.Deps +} + +func (g *goBinary) isGoBinary() {} +func (g *goBinary) InstallPath() string { + return g.installPath +} + +func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) { + // Allow the primary builder to create multiple variants. Any variants after the first + // will copy outputs from the first. + if ctx.Module() != ctx.PrimaryModule() { + primary := ctx.PrimaryModule().(*goBinary) + g.installPath = primary.installPath + return + } + + var ( + name = ctx.ModuleName() + objDir = moduleObjDir(ctx) + archiveFile = filepath.Join(objDir, name+".a") + testArchiveFile = filepath.Join(testRoot(ctx), name+".a") + aoutFile = filepath.Join(objDir, "a.out") + hasPlugins = false + pluginSrc = "" + genSrcs = []string{} + ) + + g.installPath = filepath.Join(ctx.Config().(BootstrapConfig).HostToolDir(), name) + ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), + func(module blueprint.Module) { hasPlugins = true }) + if hasPlugins { + pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go") + genSrcs = append(genSrcs, pluginSrc) + } + + var testDeps []string + + if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc) { + return + } + + var srcs, testSrcs []string + if runtime.GOOS == "darwin" { + srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...) + testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...) + } else if runtime.GOOS == "linux" { + srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...) + testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...) + } + + if ctx.Config().(BootstrapConfig).RunGoTests() { + testDeps = buildGoTest(ctx, testRoot(ctx), testArchiveFile, + name, srcs, genSrcs, testSrcs) + } + + buildGoPackage(ctx, objDir, "main", archiveFile, srcs, genSrcs) + + var linkDeps []string + var libDirFlags []string + ctx.VisitDepsDepthFirstIf(isGoPackageProducer, + func(module blueprint.Module) { + dep := module.(goPackageProducer) + linkDeps = append(linkDeps, dep.GoPackageTarget()) + libDir := dep.GoPkgRoot() + libDirFlags = append(libDirFlags, "-L "+libDir) + testDeps = append(testDeps, dep.GoTestTargets()...) + }) + + linkArgs := map[string]string{} + if len(libDirFlags) > 0 { + linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ") + } + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: link, + Outputs: []string{aoutFile}, + Inputs: []string{archiveFile}, + Implicits: linkDeps, + Args: linkArgs, + Optional: true, + }) + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: cp, + Outputs: []string{g.installPath}, + Inputs: []string{aoutFile}, + Validations: testDeps, + Optional: !g.properties.Default, + }) +} + +func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string) bool { + ret := true + name := ctx.ModuleName() + + var pluginPaths []string + ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), + func(module blueprint.Module) { + plugin := module.(goPluginProvider) + pluginPaths = append(pluginPaths, plugin.GoPkgPath()) + }) + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: pluginGenSrc, + Outputs: []string{pluginSrc}, + Args: map[string]string{ + "pkg": pkgPath, + "plugins": strings.Join(pluginPaths, " "), + }, + Optional: true, + }) + + return ret +} + +func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string, + pkgPath string, archiveFile string, srcs []string, genSrcs []string) { + + srcDir := moduleSrcDir(ctx) + srcFiles := pathtools.PrefixPaths(srcs, srcDir) + srcFiles = append(srcFiles, genSrcs...) + + var incFlags []string + var deps []string + ctx.VisitDepsDepthFirstIf(isGoPackageProducer, + func(module blueprint.Module) { + dep := module.(goPackageProducer) + incDir := dep.GoPkgRoot() + target := dep.GoPackageTarget() + incFlags = append(incFlags, "-I "+incDir) + deps = append(deps, target) + }) + + compileArgs := map[string]string{ + "pkgPath": pkgPath, + } + + if len(incFlags) > 0 { + compileArgs["incFlags"] = strings.Join(incFlags, " ") + } + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: compile, + Outputs: []string{archiveFile}, + Inputs: srcFiles, + Implicits: deps, + Args: compileArgs, + Optional: true, + }) +} + +func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive, + pkgPath string, srcs, genSrcs, testSrcs []string) []string { + + if len(testSrcs) == 0 { + return nil + } + + srcDir := moduleSrcDir(ctx) + testFiles := pathtools.PrefixPaths(testSrcs, srcDir) + + mainFile := filepath.Join(testRoot, "test.go") + testArchive := filepath.Join(testRoot, "test.a") + testFile := filepath.Join(testRoot, "test") + testPassed := filepath.Join(testRoot, "test.passed") + + buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive, + append(srcs, testSrcs...), genSrcs) + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: goTestMain, + Outputs: []string{mainFile}, + Inputs: testFiles, + Args: map[string]string{ + "pkg": pkgPath, + }, + Optional: true, + }) + + linkDeps := []string{testPkgArchive} + libDirFlags := []string{"-L " + testRoot} + testDeps := []string{} + ctx.VisitDepsDepthFirstIf(isGoPackageProducer, + func(module blueprint.Module) { + dep := module.(goPackageProducer) + linkDeps = append(linkDeps, dep.GoPackageTarget()) + libDir := dep.GoPkgRoot() + libDirFlags = append(libDirFlags, "-L "+libDir) + testDeps = append(testDeps, dep.GoTestTargets()...) + }) + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: compile, + Outputs: []string{testArchive}, + Inputs: []string{mainFile}, + Implicits: []string{testPkgArchive}, + Args: map[string]string{ + "pkgPath": "main", + "incFlags": "-I " + testRoot, + }, + Optional: true, + }) + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: link, + Outputs: []string{testFile}, + Inputs: []string{testArchive}, + Implicits: linkDeps, + Args: map[string]string{ + "libDirFlags": strings.Join(libDirFlags, " "), + }, + Optional: true, + }) + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: test, + Outputs: []string{testPassed}, + Inputs: []string{testFile}, + Validations: testDeps, + Args: map[string]string{ + "pkg": pkgPath, + "pkgSrcDir": filepath.Dir(testFiles[0]), + }, + Optional: true, + }) + + return []string{testPassed} +} + +type singleton struct { +} + +func newSingletonFactory() func() blueprint.Singleton { + return func() blueprint.Singleton { + return &singleton{} + } +} + +func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { + // Find the module that's marked as the "primary builder", which means it's + // creating the binary that we'll use to generate the non-bootstrap + // build.ninja file. + var primaryBuilders []*goBinary + // blueprintTools contains blueprint go binaries that will be built in StageMain + var blueprintTools []string + // blueprintGoPackages contains all blueprint go packages that can be built in StageMain + var blueprintGoPackages []string + ctx.VisitAllModulesIf(IsBootstrapModule, + func(module blueprint.Module) { + if ctx.PrimaryModule(module) == module { + if binaryModule, ok := module.(*goBinary); ok { + blueprintTools = append(blueprintTools, binaryModule.InstallPath()) + if binaryModule.properties.PrimaryBuilder { + primaryBuilders = append(primaryBuilders, binaryModule) + } + } + + if packageModule, ok := module.(*goPackage); ok { + blueprintGoPackages = append(blueprintGoPackages, + packageModule.GoPackageTarget()) + blueprintGoPackages = append(blueprintGoPackages, + packageModule.GoTestTargets()...) + } + } + }) + + var primaryBuilderCmdlinePrefix []string + var primaryBuilderName string + + if len(primaryBuilders) == 0 { + ctx.Errorf("no primary builder module present") + return + } else if len(primaryBuilders) > 1 { + ctx.Errorf("multiple primary builder modules present:") + for _, primaryBuilder := range primaryBuilders { + ctx.ModuleErrorf(primaryBuilder, "<-- module %s", + ctx.ModuleName(primaryBuilder)) + } + return + } else { + primaryBuilderName = ctx.ModuleName(primaryBuilders[0]) + } + + primaryBuilderFile := filepath.Join("$ToolDir", primaryBuilderName) + ctx.SetOutDir(pctx, "${outDir}") + + for _, subninja := range ctx.Config().(BootstrapConfig).Subninjas() { + ctx.AddSubninja(subninja) + } + + for _, i := range ctx.Config().(BootstrapConfig).PrimaryBuilderInvocations() { + flags := make([]string, 0) + flags = append(flags, primaryBuilderCmdlinePrefix...) + flags = append(flags, i.Args...) + + pool := "" + if i.Console { + pool = "console" + } + + envAssignments := "" + for k, v := range i.Env { + // NB: This is rife with quoting issues but we don't care because we trust + // soong_ui to not abuse this facility too much + envAssignments += k + "=" + v + " " + } + + // Build the main build.ninja + ctx.Build(pctx, blueprint.BuildParams{ + Rule: generateBuildNinja, + Outputs: i.Outputs, + Inputs: i.Inputs, + Args: map[string]string{ + "builder": primaryBuilderFile, + "env": envAssignments, + "extra": strings.Join(flags, " "), + "pool": pool, + }, + // soong_ui explicitly requests what it wants to be build. This is + // because the same Ninja file contains instructions to run + // soong_build, run bp2build and to generate the JSON module graph. + Optional: true, + Description: i.Description, + }) + } + + // Add a phony target for building various tools that are part of blueprint + ctx.Build(pctx, blueprint.BuildParams{ + Rule: blueprint.Phony, + Outputs: []string{"blueprint_tools"}, + Inputs: blueprintTools, + }) + + // Add a phony target for running go tests + ctx.Build(pctx, blueprint.BuildParams{ + Rule: blueprint.Phony, + Outputs: []string{"blueprint_go_packages"}, + Inputs: blueprintGoPackages, + Optional: true, + }) +} + +// packageRoot returns the module-specific package root directory path. This +// directory is where the final package .a files are output and where dependant +// modules search for this package via -I arguments. +func packageRoot(ctx blueprint.ModuleContext) string { + toolDir := ctx.Config().(BootstrapConfig).HostToolDir() + return filepath.Join(toolDir, "go", ctx.ModuleName(), "pkg") +} + +// testRoot returns the module-specific package root directory path used for +// building tests. The .a files generated here will include everything from +// packageRoot, plus the test-only code. +func testRoot(ctx blueprint.ModuleContext) string { + toolDir := ctx.Config().(BootstrapConfig).HostToolDir() + return filepath.Join(toolDir, "go", ctx.ModuleName(), "test") +} + +// moduleSrcDir returns the path of the directory that all source file paths are +// specified relative to. +func moduleSrcDir(ctx blueprint.ModuleContext) string { + return ctx.ModuleDir() +} + +// moduleObjDir returns the module-specific object directory path. +func moduleObjDir(ctx blueprint.ModuleContext) string { + toolDir := ctx.Config().(BootstrapConfig).HostToolDir() + return filepath.Join(toolDir, "go", ctx.ModuleName(), "obj") +} + +// moduleGenSrcDir returns the module-specific generated sources path. +func moduleGenSrcDir(ctx blueprint.ModuleContext) string { + toolDir := ctx.Config().(BootstrapConfig).HostToolDir() + return filepath.Join(toolDir, "go", ctx.ModuleName(), "gen") +} diff --git a/blueprint/bootstrap/bpdoc/bpdoc.go b/blueprint/bootstrap/bpdoc/bpdoc.go new file mode 100644 index 0000000..49ed8bc --- /dev/null +++ b/blueprint/bootstrap/bpdoc/bpdoc.go @@ -0,0 +1,337 @@ +package bpdoc + +import ( + "fmt" + "html/template" + "reflect" + "sort" + "strings" + + "github.com/google/blueprint/proptools" +) + +// Package contains the information about a package relevant to generating documentation. +type Package struct { + // Name is the name of the package. + Name string + + // Path is the full package path of the package as used in the primary builder. + Path string + + // Text is the contents of the package comment documenting the module types in the package. + Text string + + // ModuleTypes is a list of ModuleType objects that contain information about each module type that is + // defined by the package. + ModuleTypes []*ModuleType +} + +// ModuleType contains the information about a module type that is relevant to generating documentation. +type ModuleType struct { + // Name is the string that will appear in Blueprints files when defining a new module of + // this type. + Name string + + // PkgPath is the full package path of the package that contains the module type factory. + PkgPath string + + // Text is the contents of the comment documenting the module type. + Text template.HTML + + // PropertyStructs is a list of PropertyStruct objects that contain information about each + // property struct that is used by the module type, containing all properties that are valid + // for the module type. + PropertyStructs []*PropertyStruct +} + +type PropertyStruct struct { + Name string + Text string + Properties []Property +} + +type Property struct { + Name string + OtherNames []string + Type string + Tag reflect.StructTag + Text template.HTML + OtherTexts []template.HTML + Properties []Property + Default string + Anonymous bool +} + +func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value, + moduleTypeNamePropertyStructs map[string][]interface{}) ([]*Package, error) { + // Read basic info from the files to construct a Reader instance. + r := NewReader(pkgFiles) + + pkgMap := map[string]*Package{} + var pkgs []*Package + // Scan through per-module-type property structs map. + for mtName, propertyStructs := range moduleTypeNamePropertyStructs { + // Construct ModuleType with the given info. + mtInfo, err := assembleModuleTypeInfo(r, mtName, moduleTypeNameFactories[mtName], propertyStructs) + if err != nil { + return nil, err + } + // Some pruning work + removeAnonymousProperties(mtInfo) + removeEmptyPropertyStructs(mtInfo) + collapseDuplicatePropertyStructs(mtInfo) + collapseNestedPropertyStructs(mtInfo) + + // Add the ModuleInfo to the corresponding Package map/slice entries. + pkg := pkgMap[mtInfo.PkgPath] + if pkg == nil { + var err error + pkg, err = r.Package(mtInfo.PkgPath) + if err != nil { + return nil, err + } + pkgMap[mtInfo.PkgPath] = pkg + pkgs = append(pkgs, pkg) + } + pkg.ModuleTypes = append(pkg.ModuleTypes, mtInfo) + } + + // Sort ModuleTypes within each package. + for _, pkg := range pkgs { + sort.Slice(pkg.ModuleTypes, func(i, j int) bool { return pkg.ModuleTypes[i].Name < pkg.ModuleTypes[j].Name }) + } + // Sort packages. + sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Path < pkgs[j].Path }) + + return pkgs, nil +} + +func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value, + propertyStructs []interface{}) (*ModuleType, error) { + + mt, err := r.ModuleType(name, factory) + if err != nil { + return nil, err + } + + // Reader.ModuleType only fills basic information such as name and package path. Collect more info + // from property struct data. + for _, s := range propertyStructs { + v := reflect.ValueOf(s).Elem() + t := v.Type() + + ps, err := r.PropertyStruct(t.PkgPath(), t.Name(), v) + + if err != nil { + return nil, err + } + ps.ExcludeByTag("blueprint", "mutated") + for _, nestedProperty := range nestedPropertyStructs(v) { + nestedName := nestedProperty.nestPoint + nestedValue := nestedProperty.value + nestedType := nestedValue.Type() + + // Ignore property structs with unexported or unnamed types + if nestedType.PkgPath() == "" { + continue + } + nested, err := r.PropertyStruct(nestedType.PkgPath(), nestedType.Name(), nestedValue) + if err != nil { + return nil, err + } + nested.ExcludeByTag("blueprint", "mutated") + if nestedName == "" { + ps.Nest(nested) + } else { + nestPoint := ps.GetByName(nestedName) + if nestPoint == nil { + return nil, fmt.Errorf("nesting point %q not found", nestedName) + } + nestPoint.Nest(nested) + } + + if nestedProperty.anonymous { + if nestedName != "" { + nestedName += "." + } + nestedName += proptools.PropertyNameForField(nested.Name) + nestedProp := ps.GetByName(nestedName) + // Anonymous properties may have already been omitted, no need to ensure they are filtered later + if nestedProp != nil { + // Set property to anonymous to allow future filtering + nestedProp.SetAnonymous() + } + } + } + mt.PropertyStructs = append(mt.PropertyStructs, ps) + } + + return mt, nil +} + +type nestedProperty struct { + nestPoint string + value reflect.Value + anonymous bool +} + +func nestedPropertyStructs(s reflect.Value) []nestedProperty { + ret := make([]nestedProperty, 0) + var walk func(structValue reflect.Value, prefix string) + walk = func(structValue reflect.Value, prefix string) { + var nestStruct func(field reflect.StructField, value reflect.Value, fieldName string) + nestStruct = func(field reflect.StructField, value reflect.Value, fieldName string) { + nestPoint := prefix + if field.Anonymous { + nestPoint = strings.TrimSuffix(nestPoint, ".") + } else { + nestPoint = nestPoint + proptools.PropertyNameForField(fieldName) + } + ret = append(ret, nestedProperty{nestPoint: nestPoint, value: value, anonymous: field.Anonymous}) + if nestPoint != "" { + nestPoint += "." + } + walk(value, nestPoint) + } + + typ := structValue.Type() + for i := 0; i < structValue.NumField(); i++ { + field := typ.Field(i) + if field.PkgPath != "" { + // The field is not exported so just skip it. + continue + } + if proptools.HasTag(field, "blueprint", "mutated") { + continue + } + + fieldValue := structValue.Field(i) + + switch fieldValue.Kind() { + case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint: + // Nothing + case reflect.Struct: + nestStruct(field, fieldValue, field.Name) + case reflect.Ptr, reflect.Interface: + + if !fieldValue.IsNil() { + // We leave the pointer intact and zero out the struct that's + // pointed to. + elem := fieldValue.Elem() + if fieldValue.Kind() == reflect.Interface { + if elem.Kind() != reflect.Ptr { + panic(fmt.Errorf("can't get type of field %q: interface "+ + "refers to a non-pointer", field.Name)) + } + elem = elem.Elem() + } + if elem.Kind() == reflect.Struct { + nestStruct(field, elem, field.Name) + } + } + default: + panic(fmt.Errorf("unexpected kind for property struct field %q: %s", + field.Name, fieldValue.Kind())) + } + } + } + + walk(s, "") + return ret +} + +// Remove any property structs that have no exported fields +func removeEmptyPropertyStructs(mt *ModuleType) { + for i := 0; i < len(mt.PropertyStructs); i++ { + if len(mt.PropertyStructs[i].Properties) == 0 { + mt.PropertyStructs = append(mt.PropertyStructs[:i], mt.PropertyStructs[i+1:]...) + i-- + } + } +} + +// Remove any property structs that are anonymous +func removeAnonymousProperties(mt *ModuleType) { + var removeAnonymousProps func(props []Property) []Property + removeAnonymousProps = func(props []Property) []Property { + newProps := make([]Property, 0, len(props)) + for _, p := range props { + if p.Anonymous { + continue + } + if len(p.Properties) > 0 { + p.Properties = removeAnonymousProps(p.Properties) + } + newProps = append(newProps, p) + } + return newProps + } + for _, ps := range mt.PropertyStructs { + ps.Properties = removeAnonymousProps(ps.Properties) + } +} + +// Squashes duplicates of the same property struct into single entries +func collapseDuplicatePropertyStructs(mt *ModuleType) { + var collapsed []*PropertyStruct + +propertyStructLoop: + for _, from := range mt.PropertyStructs { + for _, to := range collapsed { + if from.Name == to.Name { + CollapseDuplicateProperties(&to.Properties, &from.Properties) + continue propertyStructLoop + } + } + collapsed = append(collapsed, from) + } + mt.PropertyStructs = collapsed +} + +func CollapseDuplicateProperties(to, from *[]Property) { +propertyLoop: + for _, f := range *from { + for i := range *to { + t := &(*to)[i] + if f.Name == t.Name { + CollapseDuplicateProperties(&t.Properties, &f.Properties) + continue propertyLoop + } + } + *to = append(*to, f) + } +} + +// Find all property structs that only contain structs, and move their children up one with +// a prefixed name +func collapseNestedPropertyStructs(mt *ModuleType) { + for _, ps := range mt.PropertyStructs { + collapseNestedProperties(&ps.Properties) + } +} + +func collapseNestedProperties(p *[]Property) { + var n []Property + + for _, parent := range *p { + var containsProperty bool + for j := range parent.Properties { + child := &parent.Properties[j] + if len(child.Properties) > 0 { + collapseNestedProperties(&child.Properties) + } else { + containsProperty = true + } + } + if containsProperty || len(parent.Properties) == 0 { + n = append(n, parent) + } else { + for j := range parent.Properties { + child := parent.Properties[j] + child.Name = parent.Name + "." + child.Name + n = append(n, child) + } + } + } + *p = n +} diff --git a/blueprint/bootstrap/bpdoc/bpdoc_test.go b/blueprint/bootstrap/bpdoc/bpdoc_test.go new file mode 100644 index 0000000..67ad783 --- /dev/null +++ b/blueprint/bootstrap/bpdoc/bpdoc_test.go @@ -0,0 +1,173 @@ +package bpdoc + +import ( + "fmt" + "reflect" + "testing" +) + +type propInfo struct { + name string + typ string +} + +type parentProps struct { + A string + + Child *childProps + + Mutated *mutatedProps `blueprint:"mutated"` +} + +type childProps struct { + B int + + Child *grandchildProps +} + +type grandchildProps struct { + C bool +} + +type mutatedProps struct { + D int +} + +func TestNestedPropertyStructs(t *testing.T) { + parent := parentProps{Child: &childProps{Child: &grandchildProps{}}, Mutated: &mutatedProps{}} + + allStructs := nestedPropertyStructs(reflect.ValueOf(parent)) + + // mutated shouldn't be found because it's a mutated property. + expected := []string{"child", "child.child"} + if len(allStructs) != len(expected) { + t.Fatalf("expected %d structs, got %d, all entries: %v", + len(expected), len(allStructs), allStructs) + } + got := []string{} + for _, s := range allStructs { + got = append(got, s.nestPoint) + } + + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected nested properties:\n\t %q,\n but got\n\t %q", expected, got) + } +} + +func TestAllPackages(t *testing.T) { + packages, err := AllPackages(pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs) + if err != nil { + t.Fatalf("expected nil error for AllPackages(%v, %v, %v), got %s", pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs, err) + } + + if numPackages := len(packages); numPackages != 1 { + t.Errorf("Expected %d package, got %d packages %v instead", len(pkgFiles), numPackages, packages) + } + + pkg := packages[0] + + expectedProps := map[string][]propInfo{ + "bar": []propInfo{ + propInfo{ + name: "a", + typ: "string", + }, + propInfo{ + name: "nested", + typ: "", + }, + propInfo{ + name: "nested.c", + typ: "string", + }, + propInfo{ + name: "nested_struct", + typ: "structToNest", + }, + propInfo{ + name: "nested_struct.e", + typ: "string", + }, + propInfo{ + name: "struct_has_embed", + typ: "StructWithEmbedded", + }, + propInfo{ + name: "struct_has_embed.nested_in_embedded", + typ: "structToNest", + }, + propInfo{ + name: "struct_has_embed.nested_in_embedded.e", + typ: "string", + }, + propInfo{ + name: "struct_has_embed.f", + typ: "string", + }, + propInfo{ + name: "list_of_ints", + typ: "list of int", + }, + propInfo{ + name: "list_of_nested", + typ: "list of structToNest", + }, + propInfo{ + name: "nested_in_other_embedded", + typ: "otherStructToNest", + }, + propInfo{ + name: "nested_in_other_embedded.g", + typ: "string", + }, + propInfo{ + name: "h", + typ: "string", + }, + }, + "foo": []propInfo{ + propInfo{ + name: "a", + typ: "string", + }, + }, + } + + for _, m := range pkg.ModuleTypes { + foundProps := []propInfo{} + + for _, p := range m.PropertyStructs { + nestedProps, errs := findAllProperties("", p.Properties) + foundProps = append(foundProps, nestedProps...) + for _, err := range errs { + t.Errorf("%s", err) + } + } + if wanted, ok := expectedProps[m.Name]; ok { + if !reflect.DeepEqual(foundProps, wanted) { + t.Errorf("For %s, expected\n\t %q,\nbut got\n\t %q", m.Name, wanted, foundProps) + } + } + } +} + +func findAllProperties(prefix string, properties []Property) ([]propInfo, []error) { + foundProps := []propInfo{} + errs := []error{} + for _, p := range properties { + prop := propInfo{ + name: prefix + p.Name, + typ: p.Type, + } + foundProps = append(foundProps, prop) + if hasTag(p.Tag, "blueprint", "mutated") { + err := fmt.Errorf("Property %s has `blueprint:\"mutated\" tag but should have been excluded.", p.Name) + errs = append(errs, err) + } + + nestedProps, nestedErrs := findAllProperties(prefix+p.Name+".", p.Properties) + foundProps = append(foundProps, nestedProps...) + errs = append(errs, nestedErrs...) + } + return foundProps, errs +} diff --git a/blueprint/bootstrap/bpdoc/properties.go b/blueprint/bootstrap/bpdoc/properties.go new file mode 100644 index 0000000..31b93b1 --- /dev/null +++ b/blueprint/bootstrap/bpdoc/properties.go @@ -0,0 +1,332 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bpdoc + +import ( + "fmt" + "go/ast" + "go/doc" + "html/template" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/blueprint/proptools" +) + +// +// Utility functions for PropertyStruct and Property +// + +func (ps *PropertyStruct) Clone() *PropertyStruct { + ret := *ps + ret.Properties = append([]Property(nil), ret.Properties...) + for i, prop := range ret.Properties { + ret.Properties[i] = prop.Clone() + } + + return &ret +} + +func (p *Property) Clone() Property { + ret := *p + ret.Properties = append([]Property(nil), ret.Properties...) + for i, prop := range ret.Properties { + ret.Properties[i] = prop.Clone() + } + + return ret +} + +func (p *Property) Equal(other Property) bool { + return p.Name == other.Name && p.Type == other.Type && p.Tag == other.Tag && + p.Text == other.Text && p.Default == other.Default && + stringArrayEqual(p.OtherNames, other.OtherNames) && + htmlArrayEqual(p.OtherTexts, other.OtherTexts) && + p.SameSubProperties(other) +} + +func (ps *PropertyStruct) SetDefaults(defaults reflect.Value) { + setDefaults(ps.Properties, defaults) +} + +func setDefaults(properties []Property, defaults reflect.Value) { + for i := range properties { + prop := &properties[i] + fieldName := proptools.FieldNameForProperty(prop.Name) + f := defaults.FieldByName(fieldName) + if (f == reflect.Value{}) { + panic(fmt.Errorf("property %q does not exist in %q", fieldName, defaults.Type())) + } + + if reflect.DeepEqual(f.Interface(), reflect.Zero(f.Type()).Interface()) { + continue + } + + if f.Kind() == reflect.Interface { + f = f.Elem() + } + + if f.Kind() == reflect.Ptr { + if f.IsNil() { + continue + } + f = f.Elem() + } + + if f.Kind() == reflect.Struct { + setDefaults(prop.Properties, f) + } else { + prop.Default = fmt.Sprintf("%v", f.Interface()) + } + } +} + +func stringArrayEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func htmlArrayEqual(a, b []template.HTML) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func (p *Property) SameSubProperties(other Property) bool { + if len(p.Properties) != len(other.Properties) { + return false + } + + for i := range p.Properties { + if !p.Properties[i].Equal(other.Properties[i]) { + return false + } + } + + return true +} + +func (ps *PropertyStruct) GetByName(name string) *Property { + return getByName(name, "", &ps.Properties) +} + +func (ps *PropertyStruct) Nest(nested *PropertyStruct) { + ps.Properties = nestUnique(ps.Properties, nested.Properties) +} + +// Adds a target element to src if it does not exist in src +func nestUnique(src []Property, target []Property) []Property { + var ret []Property + ret = append(ret, src...) + for _, elem := range target { + isUnique := true + for _, retElement := range ret { + if elem.Equal(retElement) { + isUnique = false + break + } + } + if isUnique { + ret = append(ret, elem) + } + } + return ret +} + +func getByName(name string, prefix string, props *[]Property) *Property { + for i := range *props { + if prefix+(*props)[i].Name == name { + return &(*props)[i] + } else if strings.HasPrefix(name, prefix+(*props)[i].Name+".") { + return getByName(name, prefix+(*props)[i].Name+".", &(*props)[i].Properties) + } + } + return nil +} + +func (p *Property) Nest(nested *PropertyStruct) { + p.Properties = nestUnique(p.Properties, nested.Properties) +} + +func (p *Property) SetAnonymous() { + p.Anonymous = true +} + +func newPropertyStruct(t *doc.Type) (*PropertyStruct, error) { + typeSpec := t.Decl.Specs[0].(*ast.TypeSpec) + ps := PropertyStruct{ + Name: t.Name, + Text: t.Doc, + } + + structType, ok := typeSpec.Type.(*ast.StructType) + if !ok { + return nil, fmt.Errorf("type of %q is not a struct", t.Name) + } + + var err error + ps.Properties, err = structProperties(structType) + if err != nil { + return nil, err + } + + return &ps, nil +} + +func structProperties(structType *ast.StructType) (props []Property, err error) { + for _, f := range structType.Fields.List { + names := f.Names + if names == nil { + // Anonymous fields have no name, use the type as the name + // TODO: hide the name and make the properties show up in the embedding struct + if t, ok := f.Type.(*ast.Ident); ok { + names = append(names, t) + } + } + for _, n := range names { + var name, tag, text string + if n != nil { + name = proptools.PropertyNameForField(n.Name) + } + if f.Doc != nil { + text = f.Doc.Text() + } + if f.Tag != nil { + tag, err = strconv.Unquote(f.Tag.Value) + if err != nil { + return nil, err + } + } + typ, innerProps, err := getType(f.Type) + if err != nil { + return nil, err + } + + props = append(props, Property{ + Name: name, + Type: typ, + Tag: reflect.StructTag(tag), + Text: formatText(text), + Properties: innerProps, + }) + } + } + + return props, nil +} + +func getType(expr ast.Expr) (typ string, innerProps []Property, err error) { + var t ast.Expr + if star, ok := expr.(*ast.StarExpr); ok { + t = star.X + } else { + t = expr + } + switch a := t.(type) { + case *ast.ArrayType: + var elt string + elt, innerProps, err = getType(a.Elt) + if err != nil { + return "", nil, err + } + typ = "list of " + elt + case *ast.InterfaceType: + typ = "interface" + case *ast.Ident: + typ = a.Name + case *ast.StructType: + innerProps, err = structProperties(a) + if err != nil { + return "", nil, err + } + default: + typ = fmt.Sprintf("%T", expr) + } + + return typ, innerProps, nil +} + +func (ps *PropertyStruct) ExcludeByTag(key, value string) { + filterPropsByTag(&ps.Properties, key, value, true) +} + +func (ps *PropertyStruct) IncludeByTag(key, value string) { + filterPropsByTag(&ps.Properties, key, value, false) +} + +func filterPropsByTag(props *[]Property, key, value string, exclude bool) { + // Create a slice that shares the storage of props but has 0 length. Appending up to + // len(props) times to this slice will overwrite the original slice contents + filtered := (*props)[:0] + for _, x := range *props { + if hasTag(x.Tag, key, value) == !exclude { + filterPropsByTag(&x.Properties, key, value, exclude) + filtered = append(filtered, x) + } + } + + *props = filtered +} + +func hasTag(tag reflect.StructTag, key, value string) bool { + for _, entry := range strings.Split(tag.Get(key), ",") { + if entry == value { + return true + } + } + return false +} + +func formatText(text string) template.HTML { + var html template.HTML + lines := strings.Split(text, "\n") + preformatted := false + for _, line := range lines { + r, _ := utf8.DecodeRuneInString(line) + indent := unicode.IsSpace(r) + if indent && !preformatted { + html += "
\n\n"
+			preformatted = true
+		} else if !indent && line != "" && preformatted {
+			html += "
\n" + preformatted = false + } + html += template.HTML(template.HTMLEscapeString(line)) + "\n" + } + if preformatted { + html += "\n" + } + return html +} diff --git a/blueprint/bootstrap/bpdoc/properties_test.go b/blueprint/bootstrap/bpdoc/properties_test.go new file mode 100644 index 0000000..b0b3ae4 --- /dev/null +++ b/blueprint/bootstrap/bpdoc/properties_test.go @@ -0,0 +1,189 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bpdoc + +import ( + "reflect" + "strings" + "testing" +) + +func TestExcludeByTag(t *testing.T) { + r := NewReader(pkgFiles) + ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{})) + if err != nil { + t.Fatal(err) + } + + ps.ExcludeByTag("tag1", "a") + + expected := []string{"c", "d", "g"} + actual := actualProperties(t, ps.Properties) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("unexpected ExcludeByTag result, expected: %q, actual: %q", expected, actual) + } +} + +func TestIncludeByTag(t *testing.T) { + r := NewReader(pkgFiles) + ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{A: "B"})) + if err != nil { + t.Fatal(err) + } + + ps.IncludeByTag("tag1", "c") + + expected := []string{"b", "c", "d", "f", "g"} + actual := actualProperties(t, ps.Properties) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("unexpected IncludeByTag result, expected: %q, actual: %q", expected, actual) + } +} + +func TestPropertiesOfReflectionStructs(t *testing.T) { + testCases := []struct { + fields map[string]interface{} + expectedProperties map[string]Property + description string + }{ + { + fields: map[string]interface{}{ + "A": "A is a string", + "B": 0, //B is an int + }, + expectedProperties: map[string]Property{ + "a": *createProperty("a", "string", ""), + "b": *createProperty("b", "int", ""), + }, + description: "struct is composed of primitive types", + }, + { + fields: map[string]interface{}{ + "A": "A is a string", + "B": 0, //B is an int + "C": props{}, + }, + expectedProperties: map[string]Property{ + "a": *createProperty("a", "string", ""), + "b": *createProperty("b", "int", ""), + "c": *createProperty("c", "props", "props docs."), + }, + description: "struct is composed of primitive types and other structs", + }, + } + + r := NewReader(pkgFiles) + for _, testCase := range testCases { + structType := reflectionStructType(testCase.fields) + ps, err := r.PropertyStruct(structType.PkgPath(), structType.String(), reflect.New(structType).Elem()) + if err != nil { + t.Fatal(err) + } + for _, actualProperty := range ps.Properties { + propName := actualProperty.Name + assertProperties(t, testCase.expectedProperties[propName], actualProperty) + } + } +} + +func TestNestUnique(t *testing.T) { + testCases := []struct { + src []Property + target []Property + expected []Property + description string + }{ + { + src: []Property{}, + target: []Property{}, + expected: []Property{}, + description: "Nest Unique fails for empty slice", + }, + { + src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, + target: []Property{}, + expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, + description: "Nest Unique fails when all elements are unique", + }, + { + src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, + target: []Property{*createProperty("c", "string", "")}, + expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", ""), *createProperty("c", "string", "")}, + description: "Nest Unique fails when all elements are unique", + }, + { + src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, + target: []Property{*createProperty("a", "string", "")}, + expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, + description: "Nest Unique fails when nested elements are duplicate", + }, + } + + errMsgTemplate := "%s. Expected: %q, Actual: %q" + for _, testCase := range testCases { + actual := nestUnique(testCase.src, testCase.target) + if len(actual) != len(testCase.expected) { + t.Errorf(errMsgTemplate, testCase.description, testCase.expected, actual) + } + for i := 0; i < len(actual); i++ { + if !actual[i].Equal(testCase.expected[i]) { + t.Errorf(errMsgTemplate, testCase.description, testCase.expected[i], actual[i]) + } + } + } +} + +// Creates a struct using reflection and return its type +func reflectionStructType(fields map[string]interface{}) reflect.Type { + var structFields []reflect.StructField + for fieldname, obj := range fields { + structField := reflect.StructField{ + Name: fieldname, + Type: reflect.TypeOf(obj), + } + structFields = append(structFields, structField) + } + return reflect.StructOf(structFields) +} + +// Creates a Property object with a subset of its props populated +func createProperty(propName string, propType string, propDocs string) *Property { + return &Property{Name: propName, Type: propType, Text: formatText(propDocs)} +} + +// Asserts that two Property objects are "similar" +// Name, Type and Text properties are checked for similarity +func assertProperties(t *testing.T, expected Property, actual Property) { + assertStrings(t, expected.Name, actual.Name) + assertStrings(t, expected.Type, actual.Type) + assertStrings(t, strings.TrimSpace(string(expected.Text)), strings.TrimSpace(string(actual.Text))) +} + +func assertStrings(t *testing.T, expected string, actual string) { + if expected != actual { + t.Errorf("expected: %s, actual: %s", expected, actual) + } +} + +func actualProperties(t *testing.T, props []Property) []string { + t.Helper() + + actual := []string{} + for _, p := range props { + actual = append(actual, p.Name) + actual = append(actual, actualProperties(t, p.Properties)...) + } + return actual +} diff --git a/blueprint/bootstrap/bpdoc/reader.go b/blueprint/bootstrap/bpdoc/reader.go new file mode 100644 index 0000000..7aa655b --- /dev/null +++ b/blueprint/bootstrap/bpdoc/reader.go @@ -0,0 +1,264 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bpdoc + +import ( + "fmt" + "go/ast" + "go/doc" + "go/parser" + "go/token" + "reflect" + "regexp" + "runtime" + "strings" + "sync" +) + +// Handles parsing and low-level processing of Blueprint module source files. Note that most getter +// functions associated with Reader only fill basic information that can be simply extracted from +// AST parsing results. More sophisticated processing is performed in bpdoc.go +type Reader struct { + pkgFiles map[string][]string // Map of package name to source files, provided by constructor + + mutex sync.Mutex + goPkgs map[string]*doc.Package // Map of package name to parsed Go AST, protected by mutex + ps map[string]*PropertyStruct // Map of module type name to property struct, protected by mutex +} + +func NewReader(pkgFiles map[string][]string) *Reader { + return &Reader{ + pkgFiles: pkgFiles, + goPkgs: make(map[string]*doc.Package), + ps: make(map[string]*PropertyStruct), + } +} + +func (r *Reader) Package(path string) (*Package, error) { + goPkg, err := r.goPkg(path) + if err != nil { + return nil, err + } + + return &Package{ + Name: goPkg.Name, + Path: path, + Text: goPkg.Doc, + }, nil +} + +func (r *Reader) ModuleType(name string, factory reflect.Value) (*ModuleType, error) { + f := runtime.FuncForPC(factory.Pointer()) + + pkgPath, err := funcNameToPkgPath(f.Name()) + if err != nil { + return nil, err + } + + factoryName := strings.TrimPrefix(f.Name(), pkgPath+".") + + text, err := r.getModuleTypeDoc(pkgPath, factoryName) + if err != nil { + return nil, err + } + + return &ModuleType{ + Name: name, + PkgPath: pkgPath, + Text: formatText(text), + }, nil +} + +// Return the PropertyStruct associated with a property struct type. The type should be in the +// format . +func (r *Reader) propertyStruct(pkgPath, name string, defaults reflect.Value) (*PropertyStruct, error) { + ps := r.getPropertyStruct(pkgPath, name) + + if ps == nil { + pkg, err := r.goPkg(pkgPath) + if err != nil { + return nil, err + } + + for _, t := range pkg.Types { + if t.Name == name { + ps, err = newPropertyStruct(t) + if err != nil { + return nil, err + } + ps = r.putPropertyStruct(pkgPath, name, ps) + } + } + } + + if ps == nil { + return nil, fmt.Errorf("package %q type %q not found", pkgPath, name) + } + + ps = ps.Clone() + ps.SetDefaults(defaults) + + return ps, nil +} + +// Return the PropertyStruct associated with a struct type using recursion +// This method is useful since golang structs created using reflection have an empty PkgPath() +func (r *Reader) PropertyStruct(pkgPath, name string, defaults reflect.Value) (*PropertyStruct, error) { + var props []Property + + // Base case: primitive type + if defaults.Kind() != reflect.Struct { + props = append(props, Property{Name: name, + Type: defaults.Type().String()}) + return &PropertyStruct{Properties: props}, nil + } + + // Base case: use r.propertyStruct if struct has a non empty pkgpath + if pkgPath != "" { + return r.propertyStruct(pkgPath, name, defaults) + } + + numFields := defaults.NumField() + for i := 0; i < numFields; i++ { + field := defaults.Type().Field(i) + // Recurse + ps, err := r.PropertyStruct(field.Type.PkgPath(), field.Type.Name(), reflect.New(field.Type).Elem()) + + if err != nil { + return nil, err + } + prop := Property{ + Name: strings.ToLower(field.Name), + Text: formatText(ps.Text), + Type: field.Type.Name(), + Properties: ps.Properties, + } + props = append(props, prop) + } + return &PropertyStruct{Properties: props}, nil +} + +func (r *Reader) getModuleTypeDoc(pkgPath, factoryFuncName string) (string, error) { + goPkg, err := r.goPkg(pkgPath) + if err != nil { + return "", err + } + + for _, fn := range goPkg.Funcs { + if fn.Name == factoryFuncName { + return fn.Doc, nil + } + } + + // The doc package may associate the method with the type it returns, so iterate through those too + for _, typ := range goPkg.Types { + for _, fn := range typ.Funcs { + if fn.Name == factoryFuncName { + return fn.Doc, nil + } + } + } + + return "", nil +} + +func (r *Reader) getPropertyStruct(pkgPath, name string) *PropertyStruct { + r.mutex.Lock() + defer r.mutex.Unlock() + + name = pkgPath + "." + name + + return r.ps[name] +} + +func (r *Reader) putPropertyStruct(pkgPath, name string, ps *PropertyStruct) *PropertyStruct { + r.mutex.Lock() + defer r.mutex.Unlock() + + name = pkgPath + "." + name + + if r.ps[name] != nil { + return r.ps[name] + } else { + r.ps[name] = ps + return ps + } +} + +// Package AST generation and storage +func (r *Reader) goPkg(pkgPath string) (*doc.Package, error) { + pkg := r.getGoPkg(pkgPath) + if pkg == nil { + if files, ok := r.pkgFiles[pkgPath]; ok { + var err error + pkgAST, err := packageAST(files) + if err != nil { + return nil, err + } + pkg = doc.New(pkgAST, pkgPath, doc.AllDecls) + pkg = r.putGoPkg(pkgPath, pkg) + } else { + return nil, fmt.Errorf("unknown package %q", pkgPath) + } + } + return pkg, nil +} + +func (r *Reader) getGoPkg(pkgPath string) *doc.Package { + r.mutex.Lock() + defer r.mutex.Unlock() + + return r.goPkgs[pkgPath] +} + +func (r *Reader) putGoPkg(pkgPath string, pkg *doc.Package) *doc.Package { + r.mutex.Lock() + defer r.mutex.Unlock() + + if r.goPkgs[pkgPath] != nil { + return r.goPkgs[pkgPath] + } else { + r.goPkgs[pkgPath] = pkg + return pkg + } +} + +// A regex to find a package path within a function name. It finds the shortest string that is +// followed by '.' and doesn't have any '/'s left. +var pkgPathRe = regexp.MustCompile("^(.*?)\\.[^/]+$") + +func funcNameToPkgPath(f string) (string, error) { + s := pkgPathRe.FindStringSubmatch(f) + if len(s) < 2 { + return "", fmt.Errorf("failed to extract package path from %q", f) + } + return s[1], nil +} + +func packageAST(files []string) (*ast.Package, error) { + asts := make(map[string]*ast.File) + + fset := token.NewFileSet() + for _, file := range files { + ast, err := parser.ParseFile(fset, file, nil, parser.ParseComments) + if err != nil { + return nil, err + } + asts[file] = ast + } + + pkg, _ := ast.NewPackage(fset, asts, nil, nil) + return pkg, nil +} diff --git a/blueprint/bootstrap/bpdoc/reader_test.go b/blueprint/bootstrap/bpdoc/reader_test.go new file mode 100644 index 0000000..bf324bf --- /dev/null +++ b/blueprint/bootstrap/bpdoc/reader_test.go @@ -0,0 +1,219 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// bpdoc docs. +package bpdoc + +import ( + "html/template" + "reflect" + "runtime" + "testing" + + "github.com/google/blueprint" +) + +type factoryFn func() (blueprint.Module, []interface{}) + +// foo docs. +func fooFactory() (blueprint.Module, []interface{}) { + return nil, []interface{}{&props{}} +} + +// bar docs. +func barFactory() (blueprint.Module, []interface{}) { + return nil, []interface{}{&complexProps{}} +} + +type structToNest struct { + E string +} + +type StructToEmbed struct { + Nested_in_embedded structToNest + + // F string + F string +} + +type otherStructToNest struct { + G string +} + +type OtherStructToEmbed struct { + Nested_in_other_embedded otherStructToNest + + // F string + H string +} + +type StructWithEmbedded struct { + StructToEmbed +} + +// for bpdoc_test.go +type complexProps struct { + A string + B_mutated string `blueprint:"mutated"` + + Nested struct { + C string + D_mutated string `blueprint:"mutated"` + } + + Nested_struct structToNest + + Struct_has_embed StructWithEmbedded + + OtherStructToEmbed + + List_of_ints []int + + List_of_nested []structToNest +} + +// props docs. +type props struct { + // A docs. + A string +} + +// for properties_test.go +type tagTestProps struct { + A string `tag1:"a,b" tag2:"c"` + B string `tag1:"a,c"` + C string `tag1:"b,c"` + + D struct { + E string `tag1:"a,b" tag2:"c"` + F string `tag1:"a,c"` + G string `tag1:"b,c"` + } `tag1:"b,c"` +} + +var pkgPath string +var pkgFiles map[string][]string +var moduleTypeNameFactories map[string]reflect.Value +var moduleTypeNamePropertyStructs map[string][]interface{} + +func init() { + pc, filename, _, _ := runtime.Caller(0) + fn := runtime.FuncForPC(pc) + + var err error + pkgPath, err = funcNameToPkgPath(fn.Name()) + if err != nil { + panic(err) + } + + pkgFiles = map[string][]string{ + pkgPath: {filename}, + } + + factories := map[string]factoryFn{"foo": fooFactory, "bar": barFactory} + + moduleTypeNameFactories = make(map[string]reflect.Value, len(factories)) + moduleTypeNamePropertyStructs = make(map[string][]interface{}, len(factories)) + for name, factory := range factories { + moduleTypeNameFactories[name] = reflect.ValueOf(factory) + _, structs := factory() + moduleTypeNamePropertyStructs[name] = structs + } +} + +func TestModuleTypeDocs(t *testing.T) { + r := NewReader(pkgFiles) + for m := range moduleTypeNameFactories { + mt, err := r.ModuleType(m+"_module", moduleTypeNameFactories[m]) + if err != nil { + t.Fatal(err) + } + + expectedText := template.HTML(m + " docs.\n\n") + if mt.Text != expectedText { + t.Errorf("unexpected docs %q", mt.Text) + } + + if mt.PkgPath != pkgPath { + t.Errorf("expected pkgpath %q, got %q", pkgPath, mt.PkgPath) + } + } +} + +func TestPropertyStruct(t *testing.T) { + r := NewReader(pkgFiles) + ps, err := r.PropertyStruct(pkgPath, "props", reflect.ValueOf(props{A: "B"})) + if err != nil { + t.Fatal(err) + } + + if ps.Text != "props docs.\n" { + t.Errorf("unexpected docs %q", ps.Text) + } + if len(ps.Properties) != 1 { + t.Fatalf("want 1 property, got %d", len(ps.Properties)) + } + + if ps.Properties[0].Name != "a" || ps.Properties[0].Text != "A docs.\n\n" || ps.Properties[0].Default != "B" { + t.Errorf("unexpected property docs %q %q %q", + ps.Properties[0].Name, ps.Properties[0].Text, ps.Properties[0].Default) + } +} + +func TestPackage(t *testing.T) { + r := NewReader(pkgFiles) + pkg, err := r.Package(pkgPath) + if err != nil { + t.Fatal(err) + } + + if pkg.Text != "bpdoc docs.\n" { + t.Errorf("unexpected docs %q", pkg.Text) + } +} + +func TestFuncToPkgPath(t *testing.T) { + tests := []struct { + f string + want string + }{ + { + f: "github.com/google/blueprint/bootstrap.Main", + want: "github.com/google/blueprint/bootstrap", + }, + { + f: "android/soong/android.GenruleFactory", + want: "android/soong/android", + }, + { + f: "android/soong/android.ModuleFactoryAdapter.func1", + want: "android/soong/android", + }, + { + f: "main.Main", + want: "main", + }, + } + for _, tt := range tests { + t.Run(tt.f, func(t *testing.T) { + got, err := funcNameToPkgPath(tt.f) + if err != nil { + t.Fatal(err) + } + if got != tt.want { + t.Errorf("funcNameToPkgPath(%v) = %v, want %v", tt.f, got, tt.want) + } + }) + } +} diff --git a/blueprint/bootstrap/bpglob/bpglob.go b/blueprint/bootstrap/bpglob/bpglob.go new file mode 100644 index 0000000..1e6d25b --- /dev/null +++ b/blueprint/bootstrap/bpglob/bpglob.go @@ -0,0 +1,148 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// bpglob is the command line tool that checks if the list of files matching a glob has +// changed, and only updates the output file list if it has changed. It is used to optimize +// out build.ninja regenerations when non-matching files are added. See +// github.com/google/blueprint/bootstrap/glob.go for a longer description. +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "time" + + "github.com/google/blueprint/deptools" + "github.com/google/blueprint/pathtools" +) + +var ( + out = flag.String("o", "", "file to write list of files that match glob") + + globs []globArg +) + +func init() { + flag.Var((*patternsArgs)(&globs), "p", "pattern to include in results") + flag.Var((*excludeArgs)(&globs), "e", "pattern to exclude from results from the most recent pattern") +} + +// A glob arg holds a single -p argument with zero or more following -e arguments. +type globArg struct { + pattern string + excludes []string +} + +// patternsArgs implements flag.Value to handle -p arguments by adding a new globArg to the list. +type patternsArgs []globArg + +func (p *patternsArgs) String() string { return `""` } + +func (p *patternsArgs) Set(s string) error { + globs = append(globs, globArg{ + pattern: s, + }) + return nil +} + +// excludeArgs implements flag.Value to handle -e arguments by adding to the last globArg in the +// list. +type excludeArgs []globArg + +func (e *excludeArgs) String() string { return `""` } + +func (e *excludeArgs) Set(s string) error { + if len(*e) == 0 { + return fmt.Errorf("-p argument is required before the first -e argument") + } + + glob := &(*e)[len(*e)-1] + glob.excludes = append(glob.excludes, s) + return nil +} + +func usage() { + fmt.Fprintln(os.Stderr, "usage: bpglob -o out -p glob [-e excludes ...] [-p glob ...]") + flag.PrintDefaults() + os.Exit(2) +} + +func main() { + flag.Parse() + + if *out == "" { + fmt.Fprintln(os.Stderr, "error: -o is required") + usage() + } + + if flag.NArg() > 0 { + usage() + } + + err := globsWithDepFile(*out, *out+".d", globs) + if err != nil { + // Globs here were already run in the primary builder without error. The only errors here should be if the glob + // pattern was made invalid by a change in the pathtools glob implementation, in which case the primary builder + // needs to be rerun anyways. Update the output file with something that will always cause the primary builder + // to rerun. + writeErrorOutput(*out, err) + } +} + +// writeErrorOutput writes an error to the output file with a timestamp to ensure that it is +// considered dirty by ninja. +func writeErrorOutput(path string, globErr error) { + s := fmt.Sprintf("%s: error: %s\n", time.Now().Format(time.StampNano), globErr.Error()) + err := ioutil.WriteFile(path, []byte(s), 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) + os.Exit(1) + } +} + +// globsWithDepFile finds all files and directories that match glob. Directories +// will have a trailing '/'. It compares the list of matches against the +// contents of fileListFile, and rewrites fileListFile if it has changed. It +// also writes all of the directories it traversed as dependencies on fileListFile +// to depFile. +// +// The format of glob is either path/*.ext for a single directory glob, or +// path/**/*.ext for a recursive glob. +func globsWithDepFile(fileListFile, depFile string, globs []globArg) error { + var results pathtools.MultipleGlobResults + for _, glob := range globs { + result, err := pathtools.Glob(glob.pattern, glob.excludes, pathtools.FollowSymlinks) + if err != nil { + return err + } + results = append(results, result) + } + + // Only write the output file if it has changed. + err := pathtools.WriteFileIfChanged(fileListFile, results.FileList(), 0666) + if err != nil { + return fmt.Errorf("failed to write file list to %q: %w", fileListFile, err) + } + + // The depfile can be written unconditionally as its timestamp doesn't affect ninja's restat + // feature. + err = deptools.WriteDepFile(depFile, fileListFile, results.Deps()) + if err != nil { + return fmt.Errorf("failed to write dep file to %q: %w", depFile, err) + } + + return nil +} diff --git a/blueprint/bootstrap/command.go b/blueprint/bootstrap/command.go new file mode 100644 index 0000000..64bcce0 --- /dev/null +++ b/blueprint/bootstrap/command.go @@ -0,0 +1,210 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bootstrap + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "runtime/debug" + "runtime/pprof" + "runtime/trace" + + "github.com/google/blueprint" +) + +type Args struct { + ModuleListFile string + OutFile string + + EmptyNinjaFile bool + + NoGC bool + Cpuprofile string + Memprofile string + TraceFile string +} + +// Returns the list of dependencies the emitted Ninja files has. These can be +// written to the .d file for the output so that it is correctly rebuilt when +// needed in case Blueprint is itself invoked from Ninja +func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, config interface{}) []string { + runtime.GOMAXPROCS(runtime.NumCPU()) + + if args.NoGC { + debug.SetGCPercent(-1) + } + + if args.Cpuprofile != "" { + f, err := os.Create(joinPath(ctx.SrcDir(), args.Cpuprofile)) + if err != nil { + fatalf("error opening cpuprofile: %s", err) + } + pprof.StartCPUProfile(f) + defer f.Close() + defer pprof.StopCPUProfile() + } + + if args.TraceFile != "" { + f, err := os.Create(joinPath(ctx.SrcDir(), args.TraceFile)) + if err != nil { + fatalf("error opening trace: %s", err) + } + trace.Start(f) + defer f.Close() + defer trace.Stop() + } + + srcDir := "." + + ninjaDeps := make([]string, 0) + + if args.ModuleListFile != "" { + ctx.SetModuleListFile(args.ModuleListFile) + ninjaDeps = append(ninjaDeps, args.ModuleListFile) + } else { + fatalf("-l is required and must be nonempty") + } + ctx.BeginEvent("list_modules") + filesToParse, err := ctx.ListModulePaths(srcDir) + ctx.EndEvent("list_modules") + if err != nil { + fatalf("could not enumerate files: %v\n", err.Error()) + } + + ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps) + ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory()) + ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory()) + ctx.RegisterSingletonType("bootstrap", newSingletonFactory()) + blueprint.RegisterPackageIncludesModuleType(ctx) + + ctx.BeginEvent("parse_bp") + blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config) + if len(errs) > 0 { + fatalErrors(errs) + } + ctx.EndEvent("parse_bp") + + // Add extra ninja file dependencies + ninjaDeps = append(ninjaDeps, blueprintFiles...) + + extraDeps, errs := ctx.ResolveDependencies(config) + if len(errs) > 0 { + fatalErrors(errs) + } + ninjaDeps = append(ninjaDeps, extraDeps...) + + if stopBefore == StopBeforePrepareBuildActions { + return ninjaDeps + } + + extraDeps, errs = ctx.PrepareBuildActions(config) + if len(errs) > 0 { + fatalErrors(errs) + } + ninjaDeps = append(ninjaDeps, extraDeps...) + + if stopBefore == StopBeforeWriteNinja { + return ninjaDeps + } + + const outFilePermissions = 0666 + var out io.StringWriter + var f *os.File + var buf *bufio.Writer + + ctx.BeginEvent("write_files") + defer ctx.EndEvent("write_files") + if args.EmptyNinjaFile { + if err := ioutil.WriteFile(joinPath(ctx.SrcDir(), args.OutFile), []byte(nil), outFilePermissions); err != nil { + fatalf("error writing empty Ninja file: %s", err) + } + } + + if !args.EmptyNinjaFile { + f, err = os.OpenFile(joinPath(ctx.SrcDir(), args.OutFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions) + if err != nil { + fatalf("error opening Ninja file: %s", err) + } + buf = bufio.NewWriterSize(f, 16*1024*1024) + out = buf + } else { + out = ioutil.Discard.(io.StringWriter) + } + + err = ctx.WriteBuildFile(out) + if err != nil { + fatalf("error writing Ninja file contents: %s", err) + } + + if buf != nil { + err = buf.Flush() + if err != nil { + fatalf("error flushing Ninja file contents: %s", err) + } + } + + if f != nil { + err = f.Close() + if err != nil { + fatalf("error closing Ninja file: %s", err) + } + } + + if args.Memprofile != "" { + f, err := os.Create(joinPath(ctx.SrcDir(), args.Memprofile)) + if err != nil { + fatalf("error opening memprofile: %s", err) + } + defer f.Close() + pprof.WriteHeapProfile(f) + } + + return ninjaDeps +} + +func fatalf(format string, args ...interface{}) { + fmt.Printf(format, args...) + fmt.Print("\n") + os.Exit(1) +} + +func fatalErrors(errs []error) { + red := "\x1b[31m" + unred := "\x1b[0m" + + for _, err := range errs { + switch err := err.(type) { + case *blueprint.BlueprintError, + *blueprint.ModuleError, + *blueprint.PropertyError: + fmt.Printf("%serror:%s %s\n", red, unred, err.Error()) + default: + fmt.Printf("%sinternal error:%s %s\n", red, unred, err) + } + } + os.Exit(1) +} + +func joinPath(base, path string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(base, path) +} diff --git a/blueprint/bootstrap/config.go b/blueprint/bootstrap/config.go new file mode 100644 index 0000000..9972b5d --- /dev/null +++ b/blueprint/bootstrap/config.go @@ -0,0 +1,114 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bootstrap + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/google/blueprint" +) + +func bootstrapVariable(name string, value func(BootstrapConfig) string) blueprint.Variable { + return pctx.VariableFunc(name, func(config interface{}) (string, error) { + c, ok := config.(BootstrapConfig) + if !ok { + panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q", + config)) + } + return value(c), nil + }) +} + +var ( + // These variables are the only configuration needed by the bootstrap + // modules. + srcDirVariable = bootstrapVariable("srcDir", func(c BootstrapConfig) string { + return "." + }) + soongOutDirVariable = bootstrapVariable("soongOutDir", func(c BootstrapConfig) string { + return c.SoongOutDir() + }) + outDirVariable = bootstrapVariable("outDir", func(c BootstrapConfig) string { + return c.OutDir() + }) + goRootVariable = bootstrapVariable("goRoot", func(c BootstrapConfig) string { + goroot := runtime.GOROOT() + // Prefer to omit absolute paths from the ninja file + if cwd, err := os.Getwd(); err == nil { + if relpath, err := filepath.Rel(cwd, goroot); err == nil { + if !strings.HasPrefix(relpath, "../") { + goroot = relpath + } + } + } + return goroot + }) + compileCmdVariable = bootstrapVariable("compileCmd", func(c BootstrapConfig) string { + return "$goRoot/pkg/tool/" + runtime.GOOS + "_" + runtime.GOARCH + "/compile" + }) + linkCmdVariable = bootstrapVariable("linkCmd", func(c BootstrapConfig) string { + return "$goRoot/pkg/tool/" + runtime.GOOS + "_" + runtime.GOARCH + "/link" + }) + debugFlagsVariable = bootstrapVariable("debugFlags", func(c BootstrapConfig) string { + if c.DebugCompilation() { + // -N: disable optimizations, -l: disable inlining + return "-N -l" + } else { + return "" + } + }) +) + +type BootstrapConfig interface { + // The directory where tools run during the build are located. + HostToolDir() string + + // The directory where files emitted during bootstrapping are located. + // Usually OutDir() + "/soong". + SoongOutDir() string + + // The output directory for the build. + OutDir() string + + // Whether to compile Go code in such a way that it can be debugged + DebugCompilation() bool + + // Whether to run tests for Go code + RunGoTests() bool + + Subninjas() []string + PrimaryBuilderInvocations() []PrimaryBuilderInvocation +} + +type StopBefore int + +const ( + DoEverything StopBefore = iota + StopBeforePrepareBuildActions + StopBeforeWriteNinja +) + +type PrimaryBuilderInvocation struct { + Inputs []string + Outputs []string + Args []string + Console bool + Description string + Env map[string]string +} diff --git a/blueprint/bootstrap/glob.go b/blueprint/bootstrap/glob.go new file mode 100644 index 0000000..70495dc --- /dev/null +++ b/blueprint/bootstrap/glob.go @@ -0,0 +1,281 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bootstrap + +import ( + "bytes" + "fmt" + "hash/fnv" + "io" + "io/ioutil" + "path/filepath" + "strconv" + "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/pathtools" +) + +// This file supports globbing source files in Blueprints files. +// +// The build.ninja file needs to be regenerated any time a file matching the glob is added +// or removed. The naive solution is to have the build.ninja file depend on all the +// traversed directories, but this will cause the regeneration step to run every time a +// non-matching file is added to a traversed directory, including backup files created by +// editors. +// +// The solution implemented here optimizes out regenerations when the directory modifications +// don't match the glob by having the build.ninja file depend on an intermedate file that +// is only updated when a file matching the glob is added or removed. The intermediate file +// depends on the traversed directories via a depfile. The depfile is used to avoid build +// errors if a directory is deleted - a direct dependency on the deleted directory would result +// in a build failure with a "missing and no known rule to make it" error. + +var ( + _ = pctx.VariableFunc("globCmd", func(config interface{}) (string, error) { + return filepath.Join(config.(BootstrapConfig).SoongOutDir(), "bpglob"), nil + }) + + // globRule rule traverses directories to produce a list of files that match $glob + // and writes it to $out if it has changed, and writes the directories to $out.d + GlobRule = pctx.StaticRule("GlobRule", + blueprint.RuleParams{ + Command: "$globCmd -o $out $args", + CommandDeps: []string{"$globCmd"}, + Description: "glob", + + Restat: true, + Deps: blueprint.DepsGCC, + Depfile: "$out.d", + }, + "args") +) + +// GlobFileContext is the subset of ModuleContext and SingletonContext needed by GlobFile +type GlobFileContext interface { + Config() interface{} + Build(pctx blueprint.PackageContext, params blueprint.BuildParams) +} + +// GlobFile creates a rule to write to fileListFile a list of the files that match the specified +// pattern but do not match any of the patterns specified in excludes. The file will include +// appropriate dependencies to regenerate the file if and only if the list of matching files has +// changed. +func GlobFile(ctx GlobFileContext, pattern string, excludes []string, fileListFile string) { + args := `-p "` + pattern + `"` + if len(excludes) > 0 { + args += " " + joinWithPrefixAndQuote(excludes, "-e ") + } + ctx.Build(pctx, blueprint.BuildParams{ + Rule: GlobRule, + Outputs: []string{fileListFile}, + Args: map[string]string{ + "args": args, + }, + Description: "glob " + pattern, + }) +} + +// multipleGlobFilesRule creates a rule to write to fileListFile a list of the files that match the specified +// pattern but do not match any of the patterns specified in excludes. The file will include +// appropriate dependencies to regenerate the file if and only if the list of matching files has +// changed. +func multipleGlobFilesRule(ctx GlobFileContext, fileListFile string, shard int, globs pathtools.MultipleGlobResults) { + args := strings.Builder{} + + for i, glob := range globs { + if i != 0 { + args.WriteString(" ") + } + args.WriteString(`-p "`) + args.WriteString(glob.Pattern) + args.WriteString(`"`) + for _, exclude := range glob.Excludes { + args.WriteString(` -e "`) + args.WriteString(exclude) + args.WriteString(`"`) + } + } + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: GlobRule, + Outputs: []string{fileListFile}, + Args: map[string]string{ + "args": args.String(), + }, + Description: fmt.Sprintf("regenerate globs shard %d of %d", shard, numGlobBuckets), + }) +} + +func joinWithPrefixAndQuote(strs []string, prefix string) string { + if len(strs) == 0 { + return "" + } + + if len(strs) == 1 { + return prefix + `"` + strs[0] + `"` + } + + n := len(" ") * (len(strs) - 1) + for _, s := range strs { + n += len(prefix) + len(s) + len(`""`) + } + + ret := make([]byte, 0, n) + for i, s := range strs { + if i != 0 { + ret = append(ret, ' ') + } + ret = append(ret, prefix...) + ret = append(ret, '"') + ret = append(ret, s...) + ret = append(ret, '"') + } + return string(ret) +} + +// GlobSingleton collects any glob patterns that were seen by Context and writes out rules to +// re-evaluate them whenever the contents of the searched directories change, and retrigger the +// primary builder if the results change. +type GlobSingleton struct { + // A function that returns the glob results of individual glob buckets + GlobLister func() pathtools.MultipleGlobResults + + // Ninja file that contains instructions for validating the glob list files + GlobFile string + + // Directory containing the glob list files + GlobDir string + + // The source directory + SrcDir string +} + +func globBucketName(globDir string, globBucket int) string { + return filepath.Join(globDir, strconv.Itoa(globBucket)) +} + +// Returns the directory where glob list files live +func GlobDirectory(buildDir, globListDir string) string { + return filepath.Join(buildDir, "globs", globListDir) +} + +func (s *GlobSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) { + // Sort the list of globs into buckets. A hash function is used instead of sharding so that + // adding a new glob doesn't force rerunning all the buckets by shifting them all by 1. + globBuckets := make([]pathtools.MultipleGlobResults, numGlobBuckets) + for _, g := range s.GlobLister() { + bucket := globToBucket(g) + globBuckets[bucket] = append(globBuckets[bucket], g) + } + + for i, globs := range globBuckets { + fileListFile := globBucketName(s.GlobDir, i) + + // Called from generateGlobNinjaFile. Write out the file list to disk, and add a ninja + // rule to run bpglob if any of the dependencies (usually directories that contain + // globbed files) have changed. The file list produced by bpglob should match exactly + // with the file written here so that restat can prevent rerunning the primary builder. + // + // We need to write the file list here so that it has an older modified date + // than the build.ninja (otherwise we'd run the primary builder twice on + // every new glob) + // + // We don't need to write the depfile because we're guaranteed that ninja + // will run the command at least once (to record it into the ninja_log), so + // the depfile will be loaded from that execution. + absoluteFileListFile := joinPath(s.SrcDir, fileListFile) + err := pathtools.WriteFileIfChanged(absoluteFileListFile, globs.FileList(), 0666) + if err != nil { + panic(fmt.Errorf("error writing %s: %s", fileListFile, err)) + } + + // Write out the ninja rule to run bpglob. + multipleGlobFilesRule(ctx, fileListFile, i, globs) + } +} + +// Writes a .ninja file that contains instructions for regenerating the glob +// files that contain the results of every glob that was run. The list of files +// is available as the result of GlobFileListFiles(). +func WriteBuildGlobsNinjaFile(glob *GlobSingleton, config interface{}) { + buffer, errs := generateGlobNinjaFile(glob, config) + if len(errs) > 0 { + fatalErrors(errs) + } + + const outFilePermissions = 0666 + err := ioutil.WriteFile(joinPath(glob.SrcDir, glob.GlobFile), buffer, outFilePermissions) + if err != nil { + fatalf("error writing %s: %s", glob.GlobFile, err) + } +} +func generateGlobNinjaFile(glob *GlobSingleton, config interface{}) ([]byte, []error) { + + ctx := blueprint.NewContext() + ctx.RegisterSingletonType("glob", func() blueprint.Singleton { + return glob + }) + + extraDeps, errs := ctx.ResolveDependencies(config) + if len(extraDeps) > 0 { + return nil, []error{fmt.Errorf("shouldn't have extra deps")} + } + if len(errs) > 0 { + return nil, errs + } + + extraDeps, errs = ctx.PrepareBuildActions(config) + if len(extraDeps) > 0 { + return nil, []error{fmt.Errorf("shouldn't have extra deps")} + } + if len(errs) > 0 { + return nil, errs + } + + buf := bytes.NewBuffer(nil) + err := ctx.WriteBuildFile(buf) + if err != nil { + return nil, []error{err} + } + + return buf.Bytes(), nil +} + +// GlobFileListFiles returns the list of files that contain the result of globs +// in the build. It is suitable for inclusion in build.ninja.d (so that +// build.ninja is regenerated if the globs change). The instructions to +// regenerate these files are written by WriteBuildGlobsNinjaFile(). +func GlobFileListFiles(globDir string) []string { + var fileListFiles []string + for i := 0; i < numGlobBuckets; i++ { + fileListFile := globBucketName(globDir, i) + fileListFiles = append(fileListFiles, fileListFile) + } + return fileListFiles +} + +const numGlobBuckets = 1024 + +// globToBucket converts a pathtools.GlobResult into a hashed bucket number in the range +// [0, numGlobBuckets). +func globToBucket(g pathtools.GlobResult) int { + hash := fnv.New32a() + io.WriteString(hash, g.Pattern) + for _, e := range g.Excludes { + io.WriteString(hash, e) + } + return int(hash.Sum32() % numGlobBuckets) +} diff --git a/blueprint/bootstrap/writedocs.go b/blueprint/bootstrap/writedocs.go new file mode 100644 index 0000000..f7314f7 --- /dev/null +++ b/blueprint/bootstrap/writedocs.go @@ -0,0 +1,63 @@ +package bootstrap + +import ( + "fmt" + "path/filepath" + "reflect" + + "github.com/google/blueprint" + "github.com/google/blueprint/bootstrap/bpdoc" + "github.com/google/blueprint/pathtools" +) + +// ModuleTypeDocs returns a list of bpdoc.ModuleType objects that contain information relevant +// to generating documentation for module types supported by the primary builder. +func ModuleTypeDocs(ctx *blueprint.Context, factories map[string]reflect.Value) ([]*bpdoc.Package, error) { + // Find the module that's marked as the "primary builder", which means it's + // creating the binary that we'll use to generate the non-bootstrap + // build.ninja file. + var primaryBuilders []*goBinary + ctx.VisitAllModulesIf(isBootstrapBinaryModule, + func(module blueprint.Module) { + binaryModule := module.(*goBinary) + if binaryModule.properties.PrimaryBuilder { + primaryBuilders = append(primaryBuilders, binaryModule) + } + }) + + var primaryBuilder *goBinary + switch len(primaryBuilders) { + case 0: + return nil, fmt.Errorf("no primary builder module present") + + case 1: + primaryBuilder = primaryBuilders[0] + + default: + return nil, fmt.Errorf("multiple primary builder modules present") + } + + pkgFiles := make(map[string][]string) + ctx.VisitDepsDepthFirst(primaryBuilder, func(module blueprint.Module) { + switch m := module.(type) { + case (*goPackage): + pkgFiles[m.properties.PkgPath] = pathtools.PrefixPaths(m.properties.Srcs, + filepath.Join(ctx.SrcDir(), ctx.ModuleDir(m))) + default: + panic(fmt.Errorf("unknown dependency type %T", module)) + } + }) + + mergedFactories := make(map[string]reflect.Value) + for moduleType, factory := range factories { + mergedFactories[moduleType] = factory + } + + for moduleType, factory := range ctx.ModuleTypeFactories() { + if _, exists := mergedFactories[moduleType]; !exists { + mergedFactories[moduleType] = reflect.ValueOf(factory) + } + } + + return bpdoc.AllPackages(pkgFiles, mergedFactories, ctx.ModuleTypePropertyStructs()) +} diff --git a/blueprint/bpfmt/bpfmt.go b/blueprint/bpfmt/bpfmt.go new file mode 100644 index 0000000..4e6bd1e --- /dev/null +++ b/blueprint/bpfmt/bpfmt.go @@ -0,0 +1,190 @@ +// Mostly copied from Go's src/cmd/gofmt: +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + + "github.com/google/blueprint/parser" +) + +var ( + // main operation modes + list = flag.Bool("l", false, "list files whose formatting differs from bpfmt's") + overwriteSourceFile = flag.Bool("w", false, "write result to (source) file") + writeToStout = flag.Bool("o", false, "write result to stdout") + doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") + sortLists = flag.Bool("s", false, "sort arrays") +) + +var ( + exitCode = 0 +) + +func report(err error) { + fmt.Fprintln(os.Stderr, err) + exitCode = 2 +} + +func usage() { + usageViolation("") +} + +func usageViolation(violation string) { + fmt.Fprintln(os.Stderr, violation) + fmt.Fprintln(os.Stderr, "usage: bpfmt [flags] [path ...]") + flag.PrintDefaults() + os.Exit(2) +} + +func processFile(filename string, out io.Writer) error { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + + return processReader(filename, f, out) +} + +func processReader(filename string, in io.Reader, out io.Writer) error { + src, err := ioutil.ReadAll(in) + if err != nil { + return err + } + + r := bytes.NewBuffer(src) + + file, errs := parser.Parse(filename, r, parser.NewScope(nil)) + if len(errs) > 0 { + for _, err := range errs { + fmt.Fprintln(os.Stderr, err) + } + return fmt.Errorf("%d parsing errors", len(errs)) + } + + if *sortLists { + parser.SortLists(file) + } + + res, err := parser.Print(file) + if err != nil { + return err + } + + if !bytes.Equal(src, res) { + // formatting has changed + if *list { + fmt.Fprintln(out, filename) + } + if *overwriteSourceFile { + err = ioutil.WriteFile(filename, res, 0644) + if err != nil { + return err + } + } + if *doDiff { + data, err := diff(src, res) + if err != nil { + return fmt.Errorf("computing diff: %s", err) + } + fmt.Printf("diff %s bpfmt/%s\n", filename, filename) + out.Write(data) + } + } + + if !*list && !*overwriteSourceFile && !*doDiff { + _, err = out.Write(res) + } + + return err +} + +func walkDir(path string) { + visitFile := func(path string, f os.FileInfo, err error) error { + if err == nil && f.Name() == "Blueprints" { + err = processFile(path, os.Stdout) + } + if err != nil { + report(err) + } + return nil + } + + filepath.Walk(path, visitFile) +} + +func main() { + flag.Usage = usage + flag.Parse() + + if !*writeToStout && !*overwriteSourceFile && !*doDiff && !*list { + usageViolation("one of -d, -l, -o, or -w is required") + } + + if flag.NArg() == 0 { + // file to parse is stdin + if *overwriteSourceFile { + fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input") + os.Exit(2) + } + if err := processReader("", os.Stdin, os.Stdout); err != nil { + report(err) + } + os.Exit(exitCode) + } + + for i := 0; i < flag.NArg(); i++ { + path := flag.Arg(i) + switch dir, err := os.Stat(path); { + case err != nil: + report(err) + case dir.IsDir(): + walkDir(path) + default: + if err := processFile(path, os.Stdout); err != nil { + report(err) + } + } + } + + os.Exit(exitCode) +} + +func diff(b1, b2 []byte) (data []byte, err error) { + f1, err := ioutil.TempFile("", "bpfmt") + if err != nil { + return + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "bpfmt") + if err != nil { + return + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return + +} diff --git a/blueprint/bpmodify/bpmodify.go b/blueprint/bpmodify/bpmodify.go new file mode 100644 index 0000000..431eb83 --- /dev/null +++ b/blueprint/bpmodify/bpmodify.go @@ -0,0 +1,472 @@ +// Mostly copied from Go's src/cmd/gofmt: +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "unicode" + + "github.com/google/blueprint/parser" +) + +var ( + // main operation modes + list = flag.Bool("l", false, "list files that would be modified by bpmodify") + write = flag.Bool("w", false, "write result to (source) file instead of stdout") + doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") + sortLists = flag.Bool("s", false, "sort touched lists, even if they were unsorted") + targetedModules = new(identSet) + targetedProperty = new(qualifiedProperty) + addIdents = new(identSet) + removeIdents = new(identSet) + removeProperty = flag.Bool("remove-property", false, "remove the property") + setString *string + addLiteral *string +) + +func init() { + flag.Var(targetedModules, "m", "comma or whitespace separated list of modules on which to operate") + flag.Var(targetedProperty, "parameter", "alias to -property=`name`") + flag.Var(targetedProperty, "property", "fully qualified `name` of property to modify (default \"deps\")") + flag.Var(addIdents, "a", "comma or whitespace separated list of identifiers to add") + flag.Var(stringPtrFlag{&addLiteral}, "add-literal", "a literal to add") + flag.Var(removeIdents, "r", "comma or whitespace separated list of identifiers to remove") + flag.Var(stringPtrFlag{&setString}, "str", "set a string property") + flag.Usage = usage +} + +var ( + exitCode = 0 +) + +func report(err error) { + fmt.Fprintln(os.Stderr, err) + exitCode = 2 +} + +func usage() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [flags] [path ...]\n", os.Args[0]) + flag.PrintDefaults() +} + +// If in == nil, the source is the contents of the file with the given filename. +func processFile(filename string, in io.Reader, out io.Writer) error { + if in == nil { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + in = f + } + + src, err := ioutil.ReadAll(in) + if err != nil { + return err + } + + r := bytes.NewBuffer(src) + + file, errs := parser.Parse(filename, r, parser.NewScope(nil)) + if len(errs) > 0 { + for _, err := range errs { + fmt.Fprintln(os.Stderr, err) + } + return fmt.Errorf("%d parsing errors", len(errs)) + } + + modified, errs := findModules(file) + if len(errs) > 0 { + for _, err := range errs { + fmt.Fprintln(os.Stderr, err) + } + fmt.Fprintln(os.Stderr, "continuing...") + } + + if modified { + res, err := parser.Print(file) + if err != nil { + return err + } + + if *list { + fmt.Fprintln(out, filename) + } + if *write { + err = ioutil.WriteFile(filename, res, 0644) + if err != nil { + return err + } + } + if *doDiff { + data, err := diff(src, res) + if err != nil { + return fmt.Errorf("computing diff: %s", err) + } + fmt.Printf("diff %s bpfmt/%s\n", filename, filename) + out.Write(data) + } + + if !*list && !*write && !*doDiff { + _, err = out.Write(res) + } + } + + return err +} + +func findModules(file *parser.File) (modified bool, errs []error) { + + for _, def := range file.Defs { + if module, ok := def.(*parser.Module); ok { + for _, prop := range module.Properties { + if prop.Name == "name" && prop.Value.Type() == parser.StringType { + if targetedModule(prop.Value.Eval().(*parser.String).Value) { + m, newErrs := processModule(module, prop.Name, file) + errs = append(errs, newErrs...) + modified = modified || m + } + } + } + } + } + + return modified, errs +} + +func processModule(module *parser.Module, moduleName string, + file *parser.File) (modified bool, errs []error) { + prop, parent, err := getRecursiveProperty(module, targetedProperty.name(), targetedProperty.prefixes()) + if err != nil { + return false, []error{err} + } + if prop == nil { + if len(addIdents.idents) > 0 || addLiteral != nil { + // We are adding something to a non-existing list prop, so we need to create it first. + prop, modified, err = createRecursiveProperty(module, targetedProperty.name(), targetedProperty.prefixes(), &parser.List{}) + } else if setString != nil { + // We setting a non-existent string property, so we need to create it first. + prop, modified, err = createRecursiveProperty(module, targetedProperty.name(), targetedProperty.prefixes(), &parser.String{}) + } else { + // We cannot find an existing prop, and we aren't adding anything to the prop, + // which means we must be removing something from a non-existing prop, + // which means this is a noop. + return false, nil + } + if err != nil { + // Here should be unreachable, but still handle it for completeness. + return false, []error{err} + } + } else if *removeProperty { + // remove-property is used solely, so return here. + return parent.RemoveProperty(prop.Name), nil + } + m, errs := processParameter(prop.Value, targetedProperty.String(), moduleName, file) + modified = modified || m + return modified, errs +} + +func getRecursiveProperty(module *parser.Module, name string, prefixes []string) (prop *parser.Property, parent *parser.Map, err error) { + prop, parent, _, err = getOrCreateRecursiveProperty(module, name, prefixes, nil) + return prop, parent, err +} + +func createRecursiveProperty(module *parser.Module, name string, prefixes []string, + empty parser.Expression) (prop *parser.Property, modified bool, err error) { + prop, _, modified, err = getOrCreateRecursiveProperty(module, name, prefixes, empty) + return prop, modified, err +} + +func getOrCreateRecursiveProperty(module *parser.Module, name string, prefixes []string, + empty parser.Expression) (prop *parser.Property, parent *parser.Map, modified bool, err error) { + m := &module.Map + for i, prefix := range prefixes { + if prop, found := m.GetProperty(prefix); found { + if mm, ok := prop.Value.Eval().(*parser.Map); ok { + m = mm + } else { + // We've found a property in the AST and such property is not of type + // *parser.Map, which must mean we didn't modify the AST. + return nil, nil, false, fmt.Errorf("Expected property %q to be a map, found %s", + strings.Join(prefixes[:i+1], "."), prop.Value.Type()) + } + } else if empty != nil { + mm := &parser.Map{} + m.Properties = append(m.Properties, &parser.Property{Name: prefix, Value: mm}) + m = mm + // We've created a new node in the AST. This means the m.GetProperty(name) + // check after this for loop must fail, because the node we inserted is an + // empty parser.Map, thus this function will return |modified| is true. + } else { + return nil, nil, false, nil + } + } + if prop, found := m.GetProperty(name); found { + // We've found a property in the AST, which must mean we didn't modify the AST. + return prop, m, false, nil + } else if empty != nil { + prop = &parser.Property{Name: name, Value: empty} + m.Properties = append(m.Properties, prop) + return prop, m, true, nil + } else { + return nil, nil, false, nil + } +} + +func processParameter(value parser.Expression, paramName, moduleName string, + file *parser.File) (modified bool, errs []error) { + if _, ok := value.(*parser.Variable); ok { + return false, []error{fmt.Errorf("parameter %s in module %s is a variable, unsupported", + paramName, moduleName)} + } + + if _, ok := value.(*parser.Operator); ok { + return false, []error{fmt.Errorf("parameter %s in module %s is an expression, unsupported", + paramName, moduleName)} + } + + if len(addIdents.idents) > 0 || len(removeIdents.idents) > 0 { + list, ok := value.(*parser.List) + if !ok { + return false, []error{fmt.Errorf("expected parameter %s in module %s to be list, found %s", + paramName, moduleName, value.Type().String())} + } + + wasSorted := parser.ListIsSorted(list) + + for _, a := range addIdents.idents { + m := parser.AddStringToList(list, a) + modified = modified || m + } + + for _, r := range removeIdents.idents { + m := parser.RemoveStringFromList(list, r) + modified = modified || m + } + + if (wasSorted || *sortLists) && modified { + parser.SortList(file, list) + } + } else if addLiteral != nil { + if *sortLists { + return false, []error{fmt.Errorf("sorting not supported when adding a literal")} + } + list, ok := value.(*parser.List) + if !ok { + return false, []error{fmt.Errorf("expected parameter %s in module %s to be list, found %s", + paramName, moduleName, value.Type().String())} + } + value, errs := parser.ParseExpression(strings.NewReader(*addLiteral)) + if errs != nil { + return false, errs + } + list.Values = append(list.Values, value) + modified = true + } else if setString != nil { + str, ok := value.(*parser.String) + if !ok { + return false, []error{fmt.Errorf("expected parameter %s in module %s to be string, found %s", + paramName, moduleName, value.Type().String())} + } + + str.Value = *setString + modified = true + } + + return modified, nil +} + +func targetedModule(name string) bool { + if targetedModules.all { + return true + } + for _, m := range targetedModules.idents { + if m == name { + return true + } + } + + return false +} + +func visitFile(path string, f os.FileInfo, err error) error { + if err == nil && f.Name() == "Blueprints" { + err = processFile(path, nil, os.Stdout) + } + if err != nil { + report(err) + } + return nil +} + +func walkDir(path string) { + filepath.Walk(path, visitFile) +} + +func main() { + defer func() { + if err := recover(); err != nil { + report(fmt.Errorf("error: %s", err)) + } + os.Exit(exitCode) + }() + + flag.Parse() + + if len(targetedProperty.parts) == 0 { + targetedProperty.Set("deps") + } + + if flag.NArg() == 0 { + if *write { + report(fmt.Errorf("error: cannot use -w with standard input")) + return + } + if err := processFile("", os.Stdin, os.Stdout); err != nil { + report(err) + } + return + } + + if len(targetedModules.idents) == 0 { + report(fmt.Errorf("-m parameter is required")) + return + } + + if len(addIdents.idents) == 0 && len(removeIdents.idents) == 0 && setString == nil && addLiteral == nil && !*removeProperty { + report(fmt.Errorf("-a, -add-literal, -r, -remove-property or -str parameter is required")) + return + } + + if *removeProperty && (len(addIdents.idents) > 0 || len(removeIdents.idents) > 0 || setString != nil || addLiteral != nil) { + report(fmt.Errorf("-remove-property cannot be used with other parameter(s)")) + return + } + + for i := 0; i < flag.NArg(); i++ { + path := flag.Arg(i) + switch dir, err := os.Stat(path); { + case err != nil: + report(err) + case dir.IsDir(): + walkDir(path) + default: + if err := processFile(path, nil, os.Stdout); err != nil { + report(err) + } + } + } +} + +func diff(b1, b2 []byte) (data []byte, err error) { + f1, err := ioutil.TempFile("", "bpfmt") + if err != nil { + return + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "bpfmt") + if err != nil { + return + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + data, err = exec.Command("diff", "-uw", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return + +} + +type stringPtrFlag struct { + s **string +} + +func (f stringPtrFlag) Set(s string) error { + *f.s = &s + return nil +} + +func (f stringPtrFlag) String() string { + if f.s == nil || *f.s == nil { + return "" + } + return **f.s +} + +type identSet struct { + idents []string + all bool +} + +func (m *identSet) String() string { + return strings.Join(m.idents, ",") +} + +func (m *identSet) Set(s string) error { + m.idents = strings.FieldsFunc(s, func(c rune) bool { + return unicode.IsSpace(c) || c == ',' + }) + if len(m.idents) == 1 && m.idents[0] == "*" { + m.all = true + } + return nil +} + +func (m *identSet) Get() interface{} { + return m.idents +} + +type qualifiedProperty struct { + parts []string +} + +var _ flag.Getter = (*qualifiedProperty)(nil) + +func (p *qualifiedProperty) name() string { + return p.parts[len(p.parts)-1] +} + +func (p *qualifiedProperty) prefixes() []string { + return p.parts[:len(p.parts)-1] +} + +func (p *qualifiedProperty) String() string { + return strings.Join(p.parts, ".") +} + +func (p *qualifiedProperty) Set(s string) error { + p.parts = strings.Split(s, ".") + if len(p.parts) == 0 { + return fmt.Errorf("%q is not a valid property name", s) + } + for _, part := range p.parts { + if part == "" { + return fmt.Errorf("%q is not a valid property name", s) + } + } + return nil +} + +func (p *qualifiedProperty) Get() interface{} { + return p.parts +} diff --git a/blueprint/bpmodify/bpmodify_test.go b/blueprint/bpmodify/bpmodify_test.go new file mode 100644 index 0000000..4340edb --- /dev/null +++ b/blueprint/bpmodify/bpmodify_test.go @@ -0,0 +1,411 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "strings" + "testing" + + "github.com/google/blueprint/parser" + "github.com/google/blueprint/proptools" +) + +var testCases = []struct { + name string + input string + output string + property string + addSet string + removeSet string + addLiteral *string + setString *string + removeProperty bool +}{ + { + name: "add", + input: ` + cc_foo { + name: "foo", + } + `, + output: ` + cc_foo { + name: "foo", + deps: ["bar"], + } + `, + property: "deps", + addSet: "bar", + }, + { + name: "remove", + input: ` + cc_foo { + name: "foo", + deps: ["bar"], + } + `, + output: ` + cc_foo { + name: "foo", + deps: [], + } + `, + property: "deps", + removeSet: "bar", + }, + { + name: "nested add", + input: ` + cc_foo { + name: "foo", + } + `, + output: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + "dep2", + "nested_dep",], + }, + }, + } + `, + property: "arch.arm.deps", + addSet: "nested_dep,dep2", + }, + { + name: "nested remove", + input: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + "dep2", + "nested_dep", + ], + }, + }, + } + `, + output: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + ], + }, + }, + } + `, + property: "arch.arm.deps", + removeSet: "nested_dep,dep2", + }, + { + name: "add existing", + input: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + "nested_dep", + "dep2", + ], + }, + }, + } + `, + output: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + "nested_dep", + "dep2", + ], + }, + }, + } + `, + property: "arch.arm.deps", + addSet: "dep2,dep2", + }, + { + name: "remove missing", + input: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + "nested_dep", + "dep2", + ], + }, + }, + } + `, + output: ` + cc_foo { + name: "foo", + arch: { + arm: { + deps: [ + "nested_dep", + "dep2", + ], + }, + }, + } + `, + property: "arch.arm.deps", + removeSet: "dep3,dep4", + }, + { + name: "remove non existent", + input: ` + cc_foo { + name: "foo", + } + `, + output: ` + cc_foo { + name: "foo", + } + `, + property: "deps", + removeSet: "bar", + }, + { + name: "remove non existent nested", + input: ` + cc_foo { + name: "foo", + arch: {}, + } + `, + output: ` + cc_foo { + name: "foo", + arch: {}, + } + `, + property: "arch.arm.deps", + removeSet: "dep3,dep4", + }, + { + name: "add numeric sorted", + input: ` + cc_foo { + name: "foo", + versions: ["1", "2"], + } + `, + output: ` + cc_foo { + name: "foo", + versions: [ + "1", + "2", + "10", + ], + } + `, + property: "versions", + addSet: "10", + }, + { + name: "add mixed sorted", + input: ` + cc_foo { + name: "foo", + deps: ["bar-v1-bar", "bar-v2-bar"], + } + `, + output: ` + cc_foo { + name: "foo", + deps: [ + "bar-v1-bar", + "bar-v2-bar", + "bar-v10-bar", + ], + } + `, + property: "deps", + addSet: "bar-v10-bar", + }, + { + name: "add a struct with literal", + input: `cc_foo {name: "foo"}`, + output: `cc_foo { + name: "foo", + structs: [ + { + version: "1", + imports: [ + "bar1", + "bar2", + ], + }, + ], +} +`, + property: "structs", + addLiteral: proptools.StringPtr(`{version: "1", imports: ["bar1", "bar2"]}`), + }, + { + name: "set string", + input: ` + cc_foo { + name: "foo", + } + `, + output: ` + cc_foo { + name: "foo", + foo: "bar", + } + `, + property: "foo", + setString: proptools.StringPtr("bar"), + }, + { + name: "set existing string", + input: ` + cc_foo { + name: "foo", + foo: "baz", + } + `, + output: ` + cc_foo { + name: "foo", + foo: "bar", + } + `, + property: "foo", + setString: proptools.StringPtr("bar"), + }, + { + name: "remove existing property", + input: ` + cc_foo { + name: "foo", + foo: "baz", + } + `, + output: ` + cc_foo { + name: "foo", + } + `, + property: "foo", + removeProperty: true, + }, { + name: "remove nested property", + input: ` + cc_foo { + name: "foo", + foo: { + bar: "baz", + }, + } + `, + output: ` + cc_foo { + name: "foo", + foo: {}, + } + `, + property: "foo.bar", + removeProperty: true, + }, { + name: "remove non-existing property", + input: ` + cc_foo { + name: "foo", + foo: "baz", + } + `, + output: ` + cc_foo { + name: "foo", + foo: "baz", + } + `, + property: "bar", + removeProperty: true, + }, +} + +func simplifyModuleDefinition(def string) string { + var result string + for _, line := range strings.Split(def, "\n") { + result += strings.TrimSpace(line) + } + return result +} + +func TestProcessModule(t *testing.T) { + for i, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + targetedProperty.Set(testCase.property) + addIdents.Set(testCase.addSet) + removeIdents.Set(testCase.removeSet) + removeProperty = &testCase.removeProperty + setString = testCase.setString + addLiteral = testCase.addLiteral + + inAst, errs := parser.ParseAndEval("", strings.NewReader(testCase.input), parser.NewScope(nil)) + if len(errs) > 0 { + for _, err := range errs { + t.Errorf(" %s", err) + } + t.Errorf("failed to parse:") + t.Errorf("%+v", testCase) + t.FailNow() + } + + if inModule, ok := inAst.Defs[0].(*parser.Module); !ok { + t.Fatalf(" input must only contain a single module definition: %s", testCase.input) + } else { + _, errs := processModule(inModule, "", inAst) + if len(errs) > 0 { + t.Errorf("test case %d:", i) + for _, err := range errs { + t.Errorf(" %s", err) + } + } + inModuleText, _ := parser.Print(inAst) + inModuleString := string(inModuleText) + if simplifyModuleDefinition(inModuleString) != simplifyModuleDefinition(testCase.output) { + t.Errorf("test case %d:", i) + t.Errorf("expected module definition:") + t.Errorf(" %s", testCase.output) + t.Errorf("actual module definition:") + t.Errorf(" %s", inModuleString) + } + } + }) + } + +} diff --git a/blueprint/context.go b/blueprint/context.go new file mode 100644 index 0000000..1296eed --- /dev/null +++ b/blueprint/context.go @@ -0,0 +1,4340 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "runtime" + "runtime/pprof" + "sort" + "strings" + "sync" + "sync/atomic" + "text/scanner" + "text/template" + + "github.com/google/blueprint/metrics" + "github.com/google/blueprint/parser" + "github.com/google/blueprint/pathtools" + "github.com/google/blueprint/proptools" +) + +var ErrBuildActionsNotReady = errors.New("build actions are not ready") + +const maxErrors = 10 +const MockModuleListFile = "bplist" + +// A Context contains all the state needed to parse a set of Blueprints files +// and generate a Ninja file. The process of generating a Ninja file proceeds +// through a series of four phases. Each phase corresponds with a some methods +// on the Context object +// +// Phase Methods +// ------------ ------------------------------------------- +// 1. Registration RegisterModuleType, RegisterSingletonType +// +// 2. Parse ParseBlueprintsFiles, Parse +// +// 3. Generate ResolveDependencies, PrepareBuildActions +// +// 4. Write WriteBuildFile +// +// The registration phase prepares the context to process Blueprints files +// containing various types of modules. The parse phase reads in one or more +// Blueprints files and validates their contents against the module types that +// have been registered. The generate phase then analyzes the parsed Blueprints +// contents to create an internal representation for the build actions that must +// be performed. This phase also performs validation of the module dependencies +// and property values defined in the parsed Blueprints files. Finally, the +// write phase generates the Ninja manifest text based on the generated build +// actions. +type Context struct { + context.Context + + // Used for metrics-related event logging. + EventHandler *metrics.EventHandler + + moduleFactories map[string]ModuleFactory + nameInterface NameInterface + moduleGroups []*moduleGroup + moduleInfo map[Module]*moduleInfo + modulesSorted []*moduleInfo + preSingletonInfo []*singletonInfo + singletonInfo []*singletonInfo + mutatorInfo []*mutatorInfo + variantMutatorNames []string + + depsModified uint32 // positive if a mutator modified the dependencies + + dependenciesReady bool // set to true on a successful ResolveDependencies + buildActionsReady bool // set to true on a successful PrepareBuildActions + + // set by SetIgnoreUnknownModuleTypes + ignoreUnknownModuleTypes bool + + // set by SetAllowMissingDependencies + allowMissingDependencies bool + + // set during PrepareBuildActions + pkgNames map[*packageContext]string + liveGlobals *liveTracker + globalVariables map[Variable]ninjaString + globalPools map[Pool]*poolDef + globalRules map[Rule]*ruleDef + + // set during PrepareBuildActions + outDir ninjaString // The builddir special Ninja variable + requiredNinjaMajor int // For the ninja_required_version variable + requiredNinjaMinor int // For the ninja_required_version variable + requiredNinjaMicro int // For the ninja_required_version variable + + subninjas []string + + // set lazily by sortedModuleGroups + cachedSortedModuleGroups []*moduleGroup + // cache deps modified to determine whether cachedSortedModuleGroups needs to be recalculated + cachedDepsModified bool + + globs map[globKey]pathtools.GlobResult + globLock sync.Mutex + + srcDir string + fs pathtools.FileSystem + moduleListFile string + + // Mutators indexed by the ID of the provider associated with them. Not all mutators will + // have providers, and not all providers will have a mutator, or if they do the mutator may + // not be registered in this Context. + providerMutators []*mutatorInfo + + // The currently running mutator + startedMutator *mutatorInfo + // True for any mutators that have already run over all modules + finishedMutators map[*mutatorInfo]bool + + // Can be set by tests to avoid invalidating Module values after mutators. + skipCloneModulesAfterMutators bool + + // String values that can be used to gate build graph traversal + includeTags *IncludeTags +} + +// A container for String keys. The keys can be used to gate build graph traversal +type IncludeTags map[string]bool + +func (tags *IncludeTags) Add(names ...string) { + for _, name := range names { + (*tags)[name] = true + } +} + +func (tags *IncludeTags) Contains(tag string) bool { + _, exists := (*tags)[tag] + return exists +} + +func (c *Context) AddIncludeTags(names ...string) { + c.includeTags.Add(names...) +} + +func (c *Context) ContainsIncludeTag(name string) bool { + return c.includeTags.Contains(name) +} + +// An Error describes a problem that was encountered that is related to a +// particular location in a Blueprints file. +type BlueprintError struct { + Err error // the error that occurred + Pos scanner.Position // the relevant Blueprints file location +} + +// A ModuleError describes a problem that was encountered that is related to a +// particular module in a Blueprints file +type ModuleError struct { + BlueprintError + module *moduleInfo +} + +// A PropertyError describes a problem that was encountered that is related to a +// particular property in a Blueprints file +type PropertyError struct { + ModuleError + property string +} + +func (e *BlueprintError) Error() string { + return fmt.Sprintf("%s: %s", e.Pos, e.Err) +} + +func (e *ModuleError) Error() string { + return fmt.Sprintf("%s: %s: %s", e.Pos, e.module, e.Err) +} + +func (e *PropertyError) Error() string { + return fmt.Sprintf("%s: %s: %s: %s", e.Pos, e.module, e.property, e.Err) +} + +type localBuildActions struct { + variables []*localVariable + rules []*localRule + buildDefs []*buildDef +} + +type moduleAlias struct { + variant variant + target *moduleInfo +} + +func (m *moduleAlias) alias() *moduleAlias { return m } +func (m *moduleAlias) module() *moduleInfo { return nil } +func (m *moduleAlias) moduleOrAliasTarget() *moduleInfo { return m.target } +func (m *moduleAlias) moduleOrAliasVariant() variant { return m.variant } + +func (m *moduleInfo) alias() *moduleAlias { return nil } +func (m *moduleInfo) module() *moduleInfo { return m } +func (m *moduleInfo) moduleOrAliasTarget() *moduleInfo { return m } +func (m *moduleInfo) moduleOrAliasVariant() variant { return m.variant } + +type moduleOrAlias interface { + alias() *moduleAlias + module() *moduleInfo + moduleOrAliasTarget() *moduleInfo + moduleOrAliasVariant() variant +} + +type modulesOrAliases []moduleOrAlias + +func (l modulesOrAliases) firstModule() *moduleInfo { + for _, moduleOrAlias := range l { + if m := moduleOrAlias.module(); m != nil { + return m + } + } + panic(fmt.Errorf("no first module!")) +} + +func (l modulesOrAliases) lastModule() *moduleInfo { + for i := range l { + if m := l[len(l)-1-i].module(); m != nil { + return m + } + } + panic(fmt.Errorf("no last module!")) +} + +type moduleGroup struct { + name string + ninjaName string + + modules modulesOrAliases + + namespace Namespace +} + +func (group *moduleGroup) moduleOrAliasByVariantName(name string) moduleOrAlias { + for _, module := range group.modules { + if module.moduleOrAliasVariant().name == name { + return module + } + } + return nil +} + +func (group *moduleGroup) moduleByVariantName(name string) *moduleInfo { + return group.moduleOrAliasByVariantName(name).module() +} + +type moduleInfo struct { + // set during Parse + typeName string + factory ModuleFactory + relBlueprintsFile string + pos scanner.Position + propertyPos map[string]scanner.Position + createdBy *moduleInfo + + variant variant + + logicModule Module + group *moduleGroup + properties []interface{} + + // set during ResolveDependencies + missingDeps []string + newDirectDeps []depInfo + + // set during updateDependencies + reverseDeps []*moduleInfo + forwardDeps []*moduleInfo + directDeps []depInfo + + // used by parallelVisit + waitingCount int + + // set during each runMutator + splitModules modulesOrAliases + + // set during PrepareBuildActions + actionDefs localBuildActions + + providers []interface{} + + startedMutator *mutatorInfo + finishedMutator *mutatorInfo + + startedGenerateBuildActions bool + finishedGenerateBuildActions bool +} + +type variant struct { + name string + variations variationMap + dependencyVariations variationMap +} + +type depInfo struct { + module *moduleInfo + tag DependencyTag +} + +func (module *moduleInfo) Name() string { + // If this is called from a LoadHook (which is run before the module has been registered) + // then group will not be set and so the name is retrieved from logicModule.Name(). + // Usually, using that method is not safe as it does not track renames (group.name does). + // However, when called from LoadHook it is safe as there is no way to rename a module + // until after the LoadHook has run and the module has been registered. + if module.group != nil { + return module.group.name + } else { + return module.logicModule.Name() + } +} + +func (module *moduleInfo) String() string { + s := fmt.Sprintf("module %q", module.Name()) + if module.variant.name != "" { + s += fmt.Sprintf(" variant %q", module.variant.name) + } + if module.createdBy != nil { + s += fmt.Sprintf(" (created by %s)", module.createdBy) + } + + return s +} + +func (module *moduleInfo) namespace() Namespace { + return module.group.namespace +} + +// A Variation is a way that a variant of a module differs from other variants of the same module. +// For example, two variants of the same module might have Variation{"arch","arm"} and +// Variation{"arch","arm64"} +type Variation struct { + // Mutator is the axis on which this variation applies, i.e. "arch" or "link" + Mutator string + // Variation is the name of the variation on the axis, i.e. "arm" or "arm64" for arch, or + // "shared" or "static" for link. + Variation string +} + +// A variationMap stores a map of Mutator to Variation to specify a variant of a module. +type variationMap map[string]string + +func (vm variationMap) clone() variationMap { + if vm == nil { + return nil + } + newVm := make(variationMap) + for k, v := range vm { + newVm[k] = v + } + + return newVm +} + +// Compare this variationMap to another one. Returns true if the every entry in this map +// exists and has the same value in the other map. +func (vm variationMap) subsetOf(other variationMap) bool { + for k, v1 := range vm { + if v2, ok := other[k]; !ok || v1 != v2 { + return false + } + } + return true +} + +func (vm variationMap) equal(other variationMap) bool { + return reflect.DeepEqual(vm, other) +} + +type singletonInfo struct { + // set during RegisterSingletonType + factory SingletonFactory + singleton Singleton + name string + + // set during PrepareBuildActions + actionDefs localBuildActions +} + +type mutatorInfo struct { + // set during RegisterMutator + topDownMutator TopDownMutator + bottomUpMutator BottomUpMutator + name string + parallel bool +} + +func newContext() *Context { + eventHandler := metrics.EventHandler{} + return &Context{ + Context: context.Background(), + EventHandler: &eventHandler, + moduleFactories: make(map[string]ModuleFactory), + nameInterface: NewSimpleNameInterface(), + moduleInfo: make(map[Module]*moduleInfo), + globs: make(map[globKey]pathtools.GlobResult), + fs: pathtools.OsFs, + finishedMutators: make(map[*mutatorInfo]bool), + includeTags: &IncludeTags{}, + outDir: nil, + requiredNinjaMajor: 1, + requiredNinjaMinor: 7, + requiredNinjaMicro: 0, + } +} + +// NewContext creates a new Context object. The created context initially has +// no module or singleton factories registered, so the RegisterModuleFactory and +// RegisterSingletonFactory methods must be called before it can do anything +// useful. +func NewContext() *Context { + ctx := newContext() + + ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator) + + return ctx +} + +// A ModuleFactory function creates a new Module object. See the +// Context.RegisterModuleType method for details about how a registered +// ModuleFactory is used by a Context. +type ModuleFactory func() (m Module, propertyStructs []interface{}) + +// RegisterModuleType associates a module type name (which can appear in a +// Blueprints file) with a Module factory function. When the given module type +// name is encountered in a Blueprints file during parsing, the Module factory +// is invoked to instantiate a new Module object to handle the build action +// generation for the module. If a Mutator splits a module into multiple variants, +// the factory is invoked again to create a new Module for each variant. +// +// The module type names given here must be unique for the context. The factory +// function should be a named function so that its package and name can be +// included in the generated Ninja file for debugging purposes. +// +// The factory function returns two values. The first is the newly created +// Module object. The second is a slice of pointers to that Module object's +// properties structs. Each properties struct is examined when parsing a module +// definition of this type in a Blueprints file. Exported fields of the +// properties structs are automatically set to the property values specified in +// the Blueprints file. The properties struct field names determine the name of +// the Blueprints file properties that are used - the Blueprints property name +// matches that of the properties struct field name with the first letter +// converted to lower-case. +// +// The fields of the properties struct must be either []string, a string, or +// bool. The Context will panic if a Module gets instantiated with a properties +// struct containing a field that is not one these supported types. +// +// Any properties that appear in the Blueprints files that are not built-in +// module properties (such as "name" and "deps") and do not have a corresponding +// field in the returned module properties struct result in an error during the +// Context's parse phase. +// +// As an example, the follow code: +// +// type myModule struct { +// properties struct { +// Foo string +// Bar []string +// } +// } +// +// func NewMyModule() (blueprint.Module, []interface{}) { +// module := new(myModule) +// properties := &module.properties +// return module, []interface{}{properties} +// } +// +// func main() { +// ctx := blueprint.NewContext() +// ctx.RegisterModuleType("my_module", NewMyModule) +// // ... +// } +// +// would support parsing a module defined in a Blueprints file as follows: +// +// my_module { +// name: "myName", +// foo: "my foo string", +// bar: ["my", "bar", "strings"], +// } +// +// The factory function may be called from multiple goroutines. Any accesses +// to global variables must be synchronized. +func (c *Context) RegisterModuleType(name string, factory ModuleFactory) { + if _, present := c.moduleFactories[name]; present { + panic(errors.New("module type name is already registered")) + } + c.moduleFactories[name] = factory +} + +// A SingletonFactory function creates a new Singleton object. See the +// Context.RegisterSingletonType method for details about how a registered +// SingletonFactory is used by a Context. +type SingletonFactory func() Singleton + +// RegisterSingletonType registers a singleton type that will be invoked to +// generate build actions. Each registered singleton type is instantiated +// and invoked exactly once as part of the generate phase. Each registered +// singleton is invoked in registration order. +// +// The singleton type names given here must be unique for the context. The +// factory function should be a named function so that its package and name can +// be included in the generated Ninja file for debugging purposes. +func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) { + for _, s := range c.singletonInfo { + if s.name == name { + panic(errors.New("singleton name is already registered")) + } + } + + c.singletonInfo = append(c.singletonInfo, &singletonInfo{ + factory: factory, + singleton: factory(), + name: name, + }) +} + +// RegisterPreSingletonType registers a presingleton type that will be invoked to +// generate build actions before any Blueprint files have been read. Each registered +// presingleton type is instantiated and invoked exactly once at the beginning of the +// parse phase. Each registered presingleton is invoked in registration order. +// +// The presingleton type names given here must be unique for the context. The +// factory function should be a named function so that its package and name can +// be included in the generated Ninja file for debugging purposes. +func (c *Context) RegisterPreSingletonType(name string, factory SingletonFactory) { + for _, s := range c.preSingletonInfo { + if s.name == name { + panic(errors.New("presingleton name is already registered")) + } + } + + c.preSingletonInfo = append(c.preSingletonInfo, &singletonInfo{ + factory: factory, + singleton: factory(), + name: name, + }) +} + +func (c *Context) SetNameInterface(i NameInterface) { + c.nameInterface = i +} + +func (c *Context) SetSrcDir(path string) { + c.srcDir = path + c.fs = pathtools.NewOsFs(path) +} + +func (c *Context) SrcDir() string { + return c.srcDir +} + +func singletonPkgPath(singleton Singleton) string { + typ := reflect.TypeOf(singleton) + for typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + return typ.PkgPath() +} + +func singletonTypeName(singleton Singleton) string { + typ := reflect.TypeOf(singleton) + for typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + return typ.PkgPath() + "." + typ.Name() +} + +// RegisterTopDownMutator registers a mutator that will be invoked to propagate dependency info +// top-down between Modules. Each registered mutator is invoked in registration order (mixing +// TopDownMutators and BottomUpMutators) once per Module, and the invocation on any module will +// have returned before it is in invoked on any of its dependencies. +// +// The mutator type names given here must be unique to all top down mutators in +// the Context. +// +// Returns a MutatorHandle, on which Parallel can be called to set the mutator to visit modules in +// parallel while maintaining ordering. +func (c *Context) RegisterTopDownMutator(name string, mutator TopDownMutator) MutatorHandle { + for _, m := range c.mutatorInfo { + if m.name == name && m.topDownMutator != nil { + panic(fmt.Errorf("mutator name %s is already registered", name)) + } + } + + info := &mutatorInfo{ + topDownMutator: mutator, + name: name, + } + + c.mutatorInfo = append(c.mutatorInfo, info) + + return info +} + +// RegisterBottomUpMutator registers a mutator that will be invoked to split Modules into variants. +// Each registered mutator is invoked in registration order (mixing TopDownMutators and +// BottomUpMutators) once per Module, will not be invoked on a module until the invocations on all +// of the modules dependencies have returned. +// +// The mutator type names given here must be unique to all bottom up or early +// mutators in the Context. +// +// Returns a MutatorHandle, on which Parallel can be called to set the mutator to visit modules in +// parallel while maintaining ordering. +func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) MutatorHandle { + for _, m := range c.variantMutatorNames { + if m == name { + panic(fmt.Errorf("mutator name %s is already registered", name)) + } + } + + info := &mutatorInfo{ + bottomUpMutator: mutator, + name: name, + } + c.mutatorInfo = append(c.mutatorInfo, info) + + c.variantMutatorNames = append(c.variantMutatorNames, name) + + return info +} + +type MutatorHandle interface { + // Set the mutator to visit modules in parallel while maintaining ordering. Calling any + // method on the mutator context is thread-safe, but the mutator must handle synchronization + // for any modifications to global state or any modules outside the one it was invoked on. + Parallel() MutatorHandle +} + +func (mutator *mutatorInfo) Parallel() MutatorHandle { + mutator.parallel = true + return mutator +} + +// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case +// where it encounters an unknown module type while parsing Blueprints files. By +// default, the context will report unknown module types as an error. If this +// method is called with ignoreUnknownModuleTypes set to true then the context +// will silently ignore unknown module types. +// +// This method should generally not be used. It exists to facilitate the +// bootstrapping process. +func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) { + c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes +} + +// SetAllowMissingDependencies changes the behavior of Blueprint to ignore +// unresolved dependencies. If the module's GenerateBuildActions calls +// ModuleContext.GetMissingDependencies Blueprint will not emit any errors +// for missing dependencies. +func (c *Context) SetAllowMissingDependencies(allowMissingDependencies bool) { + c.allowMissingDependencies = allowMissingDependencies +} + +func (c *Context) SetModuleListFile(listFile string) { + c.moduleListFile = listFile +} + +func (c *Context) ListModulePaths(baseDir string) (paths []string, err error) { + reader, err := c.fs.Open(c.moduleListFile) + if err != nil { + return nil, err + } + defer reader.Close() + bytes, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + text := string(bytes) + + text = strings.Trim(text, "\n") + lines := strings.Split(text, "\n") + for i := range lines { + lines[i] = filepath.Join(baseDir, lines[i]) + } + + return lines, nil +} + +// a fileParseContext tells the status of parsing a particular file +type fileParseContext struct { + // name of file + fileName string + + // scope to use when resolving variables + Scope *parser.Scope + + // pointer to the one in the parent directory + parent *fileParseContext + + // is closed once FileHandler has completed for this file + doneVisiting chan struct{} +} + +// ParseBlueprintsFiles parses a set of Blueprints files starting with the file +// at rootFile. When it encounters a Blueprints file with a set of subdirs +// listed it recursively parses any Blueprints files found in those +// subdirectories. +// +// If no errors are encountered while parsing the files, the list of paths on +// which the future output will depend is returned. This list will include both +// Blueprints file paths as well as directory paths for cases where wildcard +// subdirs are found. +func (c *Context) ParseBlueprintsFiles(rootFile string, + config interface{}) (deps []string, errs []error) { + + baseDir := filepath.Dir(rootFile) + pathsToParse, err := c.ListModulePaths(baseDir) + if err != nil { + return nil, []error{err} + } + return c.ParseFileList(baseDir, pathsToParse, config) +} + +// Returns a boolean for whether this file should be analyzed +// Evaluates to true if the file either +// 1. does not contain a blueprint_package_includes +// 2. contains a blueprint_package_includes and all requested tags are set +// This should be processed before adding any modules to the build graph +func shouldVisitFile(c *Context, file *parser.File) (bool, []error) { + for _, def := range file.Defs { + switch def := def.(type) { + case *parser.Module: + if def.Type != "blueprint_package_includes" { + continue + } + module, errs := processModuleDef(def, file.Name, c.moduleFactories, nil, c.ignoreUnknownModuleTypes) + if len(errs) > 0 { + // This file contains errors in blueprint_package_includes + // Visit anyways so that we can report errors on other modules in the file + return true, errs + } + logicModule, _ := c.cloneLogicModule(module) + pi := logicModule.(*PackageIncludes) + return pi.MatchesIncludeTags(c), []error{} + } + } + return true, []error{} +} + + +func (c *Context) ParseFileList(rootDir string, filePaths []string, + config interface{}) (deps []string, errs []error) { + + if len(filePaths) < 1 { + return nil, []error{fmt.Errorf("no paths provided to parse")} + } + + c.dependenciesReady = false + + type newModuleInfo struct { + *moduleInfo + deps []string + added chan<- struct{} + } + + moduleCh := make(chan newModuleInfo) + errsCh := make(chan []error) + doneCh := make(chan struct{}) + var numErrs uint32 + var numGoroutines int32 + + // handler must be reentrant + handleOneFile := func(file *parser.File) { + if atomic.LoadUint32(&numErrs) > maxErrors { + return + } + + addedCh := make(chan struct{}) + + var scopedModuleFactories map[string]ModuleFactory + + var addModule func(module *moduleInfo) []error + addModule = func(module *moduleInfo) []error { + // Run any load hooks immediately before it is sent to the moduleCh and is + // registered by name. This allows load hooks to set and/or modify any aspect + // of the module (including names) using information that is not available when + // the module factory is called. + newModules, newDeps, errs := runAndRemoveLoadHooks(c, config, module, &scopedModuleFactories) + if len(errs) > 0 { + return errs + } + + moduleCh <- newModuleInfo{module, newDeps, addedCh} + <-addedCh + for _, n := range newModules { + errs = addModule(n) + if len(errs) > 0 { + return errs + } + } + return nil + } + shouldVisit, errs := shouldVisitFile(c, file) + if len(errs) > 0 { + atomic.AddUint32(&numErrs, uint32(len(errs))) + errsCh <- errs + } + if !shouldVisit { + // TODO: Write a file that lists the skipped bp files + return + } + + for _, def := range file.Defs { + switch def := def.(type) { + case *parser.Module: + module, errs := processModuleDef(def, file.Name, c.moduleFactories, scopedModuleFactories, c.ignoreUnknownModuleTypes) + if len(errs) == 0 && module != nil { + errs = addModule(module) + } + + if len(errs) > 0 { + atomic.AddUint32(&numErrs, uint32(len(errs))) + errsCh <- errs + } + + case *parser.Assignment: + // Already handled via Scope object + default: + panic("unknown definition type") + } + + } + } + + atomic.AddInt32(&numGoroutines, 1) + go func() { + var errs []error + deps, errs = c.WalkBlueprintsFiles(rootDir, filePaths, handleOneFile) + if len(errs) > 0 { + errsCh <- errs + } + doneCh <- struct{}{} + }() + + var hookDeps []string +loop: + for { + select { + case newErrs := <-errsCh: + errs = append(errs, newErrs...) + case module := <-moduleCh: + newErrs := c.addModule(module.moduleInfo) + hookDeps = append(hookDeps, module.deps...) + if module.added != nil { + module.added <- struct{}{} + } + if len(newErrs) > 0 { + errs = append(errs, newErrs...) + } + case <-doneCh: + n := atomic.AddInt32(&numGoroutines, -1) + if n == 0 { + break loop + } + } + } + + deps = append(deps, hookDeps...) + return deps, errs +} + +type FileHandler func(*parser.File) + +// WalkBlueprintsFiles walks a set of Blueprints files starting with the given filepaths, +// calling the given file handler on each +// +// When WalkBlueprintsFiles encounters a Blueprints file with a set of subdirs listed, +// it recursively parses any Blueprints files found in those subdirectories. +// +// If any of the file paths is an ancestor directory of any other of file path, the ancestor +// will be parsed and visited first. +// +// the file handler will be called from a goroutine, so it must be reentrant. +// +// If no errors are encountered while parsing the files, the list of paths on +// which the future output will depend is returned. This list will include both +// Blueprints file paths as well as directory paths for cases where wildcard +// subdirs are found. +// +// visitor will be called asynchronously, and will only be called once visitor for each +// ancestor directory has completed. +// +// WalkBlueprintsFiles will not return until all calls to visitor have returned. +func (c *Context) WalkBlueprintsFiles(rootDir string, filePaths []string, + visitor FileHandler) (deps []string, errs []error) { + + // make a mapping from ancestors to their descendants to facilitate parsing ancestors first + descendantsMap, err := findBlueprintDescendants(filePaths) + if err != nil { + panic(err.Error()) + } + blueprintsSet := make(map[string]bool) + + // Channels to receive data back from openAndParse goroutines + blueprintsCh := make(chan fileParseContext) + errsCh := make(chan []error) + depsCh := make(chan string) + + // Channel to notify main loop that a openAndParse goroutine has finished + doneParsingCh := make(chan fileParseContext) + + // Number of outstanding goroutines to wait for + activeCount := 0 + var pending []fileParseContext + tooManyErrors := false + + // Limit concurrent calls to parseBlueprintFiles to 200 + // Darwin has a default limit of 256 open files + maxActiveCount := 200 + + // count the number of pending calls to visitor() + visitorWaitGroup := sync.WaitGroup{} + + startParseBlueprintsFile := func(blueprint fileParseContext) { + if blueprintsSet[blueprint.fileName] { + return + } + blueprintsSet[blueprint.fileName] = true + activeCount++ + deps = append(deps, blueprint.fileName) + visitorWaitGroup.Add(1) + go func() { + file, blueprints, deps, errs := c.openAndParse(blueprint.fileName, blueprint.Scope, rootDir, + &blueprint) + if len(errs) > 0 { + errsCh <- errs + } + for _, blueprint := range blueprints { + blueprintsCh <- blueprint + } + for _, dep := range deps { + depsCh <- dep + } + doneParsingCh <- blueprint + + if blueprint.parent != nil && blueprint.parent.doneVisiting != nil { + // wait for visitor() of parent to complete + <-blueprint.parent.doneVisiting + } + + if len(errs) == 0 { + // process this file + visitor(file) + } + if blueprint.doneVisiting != nil { + close(blueprint.doneVisiting) + } + visitorWaitGroup.Done() + }() + } + + foundParseableBlueprint := func(blueprint fileParseContext) { + if activeCount >= maxActiveCount { + pending = append(pending, blueprint) + } else { + startParseBlueprintsFile(blueprint) + } + } + + startParseDescendants := func(blueprint fileParseContext) { + descendants, hasDescendants := descendantsMap[blueprint.fileName] + if hasDescendants { + for _, descendant := range descendants { + foundParseableBlueprint(fileParseContext{descendant, parser.NewScope(blueprint.Scope), &blueprint, make(chan struct{})}) + } + } + } + + // begin parsing any files that have no ancestors + startParseDescendants(fileParseContext{"", parser.NewScope(nil), nil, nil}) + +loop: + for { + if len(errs) > maxErrors { + tooManyErrors = true + } + + select { + case newErrs := <-errsCh: + errs = append(errs, newErrs...) + case dep := <-depsCh: + deps = append(deps, dep) + case blueprint := <-blueprintsCh: + if tooManyErrors { + continue + } + foundParseableBlueprint(blueprint) + case blueprint := <-doneParsingCh: + activeCount-- + if !tooManyErrors { + startParseDescendants(blueprint) + } + if activeCount < maxActiveCount && len(pending) > 0 { + // start to process the next one from the queue + next := pending[len(pending)-1] + pending = pending[:len(pending)-1] + startParseBlueprintsFile(next) + } + if activeCount == 0 { + break loop + } + } + } + + sort.Strings(deps) + + // wait for every visitor() to complete + visitorWaitGroup.Wait() + + return +} + +// MockFileSystem causes the Context to replace all reads with accesses to the provided map of +// filenames to contents stored as a byte slice. +func (c *Context) MockFileSystem(files map[string][]byte) { + // look for a module list file + _, ok := files[MockModuleListFile] + if !ok { + // no module list file specified; find every file named Blueprints + pathsToParse := []string{} + for candidate := range files { + if filepath.Base(candidate) == "Android.bp" { + pathsToParse = append(pathsToParse, candidate) + } + } + if len(pathsToParse) < 1 { + panic(fmt.Sprintf("No Blueprints files found in mock filesystem: %v\n", files)) + } + // put the list of Blueprints files into a list file + files[MockModuleListFile] = []byte(strings.Join(pathsToParse, "\n")) + } + c.SetModuleListFile(MockModuleListFile) + + // mock the filesystem + c.fs = pathtools.MockFs(files) +} + +func (c *Context) SetFs(fs pathtools.FileSystem) { + c.fs = fs +} + +// openAndParse opens and parses a single Blueprints file, and returns the results +func (c *Context) openAndParse(filename string, scope *parser.Scope, rootDir string, + parent *fileParseContext) (file *parser.File, + subBlueprints []fileParseContext, deps []string, errs []error) { + + f, err := c.fs.Open(filename) + if err != nil { + // couldn't open the file; see if we can provide a clearer error than "could not open file" + stats, statErr := c.fs.Lstat(filename) + if statErr == nil { + isSymlink := stats.Mode()&os.ModeSymlink != 0 + if isSymlink { + err = fmt.Errorf("could not open symlink %v : %v", filename, err) + target, readlinkErr := os.Readlink(filename) + if readlinkErr == nil { + _, targetStatsErr := c.fs.Lstat(target) + if targetStatsErr != nil { + err = fmt.Errorf("could not open symlink %v; its target (%v) cannot be opened", filename, target) + } + } + } else { + err = fmt.Errorf("%v exists but could not be opened: %v", filename, err) + } + } + return nil, nil, nil, []error{err} + } + + func() { + defer func() { + err = f.Close() + if err != nil { + errs = append(errs, err) + } + }() + file, subBlueprints, errs = c.parseOne(rootDir, filename, f, scope, parent) + }() + + if len(errs) > 0 { + return nil, nil, nil, errs + } + + for _, b := range subBlueprints { + deps = append(deps, b.fileName) + } + + return file, subBlueprints, deps, nil +} + +// parseOne parses a single Blueprints file from the given reader, creating Module +// objects for each of the module definitions encountered. If the Blueprints +// file contains an assignment to the "subdirs" variable, then the +// subdirectories listed are searched for Blueprints files returned in the +// subBlueprints return value. If the Blueprints file contains an assignment +// to the "build" variable, then the file listed are returned in the +// subBlueprints return value. +// +// rootDir specifies the path to the root directory of the source tree, while +// filename specifies the path to the Blueprints file. These paths are used for +// error reporting and for determining the module's directory. +func (c *Context) parseOne(rootDir, filename string, reader io.Reader, + scope *parser.Scope, parent *fileParseContext) (file *parser.File, subBlueprints []fileParseContext, errs []error) { + + relBlueprintsFile, err := filepath.Rel(rootDir, filename) + if err != nil { + return nil, nil, []error{err} + } + + scope.Remove("subdirs") + scope.Remove("optional_subdirs") + scope.Remove("build") + file, errs = parser.ParseAndEval(filename, reader, scope) + if len(errs) > 0 { + for i, err := range errs { + if parseErr, ok := err.(*parser.ParseError); ok { + err = &BlueprintError{ + Err: parseErr.Err, + Pos: parseErr.Pos, + } + errs[i] = err + } + } + + // If there were any parse errors don't bother trying to interpret the + // result. + return nil, nil, errs + } + file.Name = relBlueprintsFile + + build, buildPos, err := getLocalStringListFromScope(scope, "build") + if err != nil { + errs = append(errs, err) + } + for _, buildEntry := range build { + if strings.Contains(buildEntry, "/") { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("illegal value %v. The '/' character is not permitted", buildEntry), + Pos: buildPos, + }) + } + } + + if err != nil { + errs = append(errs, err) + } + + var blueprints []string + + newBlueprints, newErrs := c.findBuildBlueprints(filepath.Dir(filename), build, buildPos) + blueprints = append(blueprints, newBlueprints...) + errs = append(errs, newErrs...) + + subBlueprintsAndScope := make([]fileParseContext, len(blueprints)) + for i, b := range blueprints { + subBlueprintsAndScope[i] = fileParseContext{b, parser.NewScope(scope), parent, make(chan struct{})} + } + return file, subBlueprintsAndScope, errs +} + +func (c *Context) findBuildBlueprints(dir string, build []string, + buildPos scanner.Position) ([]string, []error) { + + var blueprints []string + var errs []error + + for _, file := range build { + pattern := filepath.Join(dir, file) + var matches []string + var err error + + matches, err = c.glob(pattern, nil) + + if err != nil { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("%q: %s", pattern, err.Error()), + Pos: buildPos, + }) + continue + } + + if len(matches) == 0 { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("%q: not found", pattern), + Pos: buildPos, + }) + } + + for _, foundBlueprints := range matches { + if strings.HasSuffix(foundBlueprints, "/") { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("%q: is a directory", foundBlueprints), + Pos: buildPos, + }) + } + blueprints = append(blueprints, foundBlueprints) + } + } + + return blueprints, errs +} + +func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos scanner.Position, + subBlueprintsName string, optional bool) ([]string, []error) { + + var blueprints []string + var errs []error + + for _, subdir := range subdirs { + pattern := filepath.Join(dir, subdir, subBlueprintsName) + var matches []string + var err error + + matches, err = c.glob(pattern, nil) + + if err != nil { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("%q: %s", pattern, err.Error()), + Pos: subdirsPos, + }) + continue + } + + if len(matches) == 0 && !optional { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("%q: not found", pattern), + Pos: subdirsPos, + }) + } + + for _, subBlueprints := range matches { + if strings.HasSuffix(subBlueprints, "/") { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("%q: is a directory", subBlueprints), + Pos: subdirsPos, + }) + } + blueprints = append(blueprints, subBlueprints) + } + } + + return blueprints, errs +} + +func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) { + if assignment, local := scope.Get(v); assignment == nil || !local { + return nil, scanner.Position{}, nil + } else { + switch value := assignment.Value.Eval().(type) { + case *parser.List: + ret := make([]string, 0, len(value.Values)) + + for _, listValue := range value.Values { + s, ok := listValue.(*parser.String) + if !ok { + // The parser should not produce this. + panic("non-string value found in list") + } + + ret = append(ret, s.Value) + } + + return ret, assignment.EqualsPos, nil + case *parser.Bool, *parser.String: + return nil, scanner.Position{}, &BlueprintError{ + Err: fmt.Errorf("%q must be a list of strings", v), + Pos: assignment.EqualsPos, + } + default: + panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type())) + } + } +} + +func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position, error) { + if assignment, _ := scope.Get(v); assignment == nil { + return "", scanner.Position{}, nil + } else { + switch value := assignment.Value.Eval().(type) { + case *parser.String: + return value.Value, assignment.EqualsPos, nil + case *parser.Bool, *parser.List: + return "", scanner.Position{}, &BlueprintError{ + Err: fmt.Errorf("%q must be a string", v), + Pos: assignment.EqualsPos, + } + default: + panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type())) + } + } +} + +// Clones a build logic module by calling the factory method for its module type, and then cloning +// property values. Any values stored in the module object that are not stored in properties +// structs will be lost. +func (c *Context) cloneLogicModule(origModule *moduleInfo) (Module, []interface{}) { + newLogicModule, newProperties := origModule.factory() + + if len(newProperties) != len(origModule.properties) { + panic("mismatched properties array length in " + origModule.Name()) + } + + for i := range newProperties { + dst := reflect.ValueOf(newProperties[i]) + src := reflect.ValueOf(origModule.properties[i]) + + proptools.CopyProperties(dst, src) + } + + return newLogicModule, newProperties +} + +func newVariant(module *moduleInfo, mutatorName string, variationName string, + local bool) variant { + + newVariantName := module.variant.name + if variationName != "" { + if newVariantName == "" { + newVariantName = variationName + } else { + newVariantName += "_" + variationName + } + } + + newVariations := module.variant.variations.clone() + if newVariations == nil { + newVariations = make(variationMap) + } + newVariations[mutatorName] = variationName + + newDependencyVariations := module.variant.dependencyVariations.clone() + if !local { + if newDependencyVariations == nil { + newDependencyVariations = make(variationMap) + } + newDependencyVariations[mutatorName] = variationName + } + + return variant{newVariantName, newVariations, newDependencyVariations} +} + +func (c *Context) createVariations(origModule *moduleInfo, mutatorName string, + defaultVariationName *string, variationNames []string, local bool) (modulesOrAliases, []error) { + + if len(variationNames) == 0 { + panic(fmt.Errorf("mutator %q passed zero-length variation list for module %q", + mutatorName, origModule.Name())) + } + + var newModules modulesOrAliases + + var errs []error + + for i, variationName := range variationNames { + var newLogicModule Module + var newProperties []interface{} + + if i == 0 { + // Reuse the existing module for the first new variant + // This both saves creating a new module, and causes the insertion in c.moduleInfo below + // with logicModule as the key to replace the original entry in c.moduleInfo + newLogicModule, newProperties = origModule.logicModule, origModule.properties + } else { + newLogicModule, newProperties = c.cloneLogicModule(origModule) + } + + m := *origModule + newModule := &m + newModule.directDeps = append([]depInfo(nil), origModule.directDeps...) + newModule.reverseDeps = nil + newModule.forwardDeps = nil + newModule.logicModule = newLogicModule + newModule.variant = newVariant(origModule, mutatorName, variationName, local) + newModule.properties = newProperties + newModule.providers = append([]interface{}(nil), origModule.providers...) + + newModules = append(newModules, newModule) + + newErrs := c.convertDepsToVariation(newModule, mutatorName, variationName, defaultVariationName) + if len(newErrs) > 0 { + errs = append(errs, newErrs...) + } + } + + // Mark original variant as invalid. Modules that depend on this module will still + // depend on origModule, but we'll fix it when the mutator is called on them. + origModule.logicModule = nil + origModule.splitModules = newModules + + atomic.AddUint32(&c.depsModified, 1) + + return newModules, errs +} + +func (c *Context) convertDepsToVariation(module *moduleInfo, + mutatorName, variationName string, defaultVariationName *string) (errs []error) { + + for i, dep := range module.directDeps { + if dep.module.logicModule == nil { + var newDep *moduleInfo + for _, m := range dep.module.splitModules { + if m.moduleOrAliasVariant().variations[mutatorName] == variationName { + newDep = m.moduleOrAliasTarget() + break + } + } + if newDep == nil && defaultVariationName != nil { + // give it a second chance; match with defaultVariationName + for _, m := range dep.module.splitModules { + if m.moduleOrAliasVariant().variations[mutatorName] == *defaultVariationName { + newDep = m.moduleOrAliasTarget() + break + } + } + } + if newDep == nil { + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("failed to find variation %q for module %q needed by %q", + variationName, dep.module.Name(), module.Name()), + Pos: module.pos, + }) + continue + } + module.directDeps[i].module = newDep + } + } + + return errs +} + +func (c *Context) prettyPrintVariant(variations variationMap) string { + names := make([]string, 0, len(variations)) + for _, m := range c.variantMutatorNames { + if v, ok := variations[m]; ok { + names = append(names, m+":"+v) + } + } + + return strings.Join(names, ",") +} + +func (c *Context) prettyPrintGroupVariants(group *moduleGroup) string { + var variants []string + for _, moduleOrAlias := range group.modules { + if mod := moduleOrAlias.module(); mod != nil { + variants = append(variants, c.prettyPrintVariant(mod.variant.variations)) + } else if alias := moduleOrAlias.alias(); alias != nil { + variants = append(variants, c.prettyPrintVariant(alias.variant.variations)+ + " (alias to "+c.prettyPrintVariant(alias.target.variant.variations)+")") + } + } + return strings.Join(variants, "\n ") +} + +func newModule(factory ModuleFactory) *moduleInfo { + logicModule, properties := factory() + + return &moduleInfo{ + logicModule: logicModule, + factory: factory, + properties: properties, + } +} + +func processModuleDef(moduleDef *parser.Module, + relBlueprintsFile string, moduleFactories, scopedModuleFactories map[string]ModuleFactory, ignoreUnknownModuleTypes bool) (*moduleInfo, []error) { + + factory, ok := moduleFactories[moduleDef.Type] + if !ok && scopedModuleFactories != nil { + factory, ok = scopedModuleFactories[moduleDef.Type] + } + if !ok { + if ignoreUnknownModuleTypes { + return nil, nil + } + + return nil, []error{ + &BlueprintError{ + Err: fmt.Errorf("unrecognized module type %q", moduleDef.Type), + Pos: moduleDef.TypePos, + }, + } + } + + module := newModule(factory) + module.typeName = moduleDef.Type + + module.relBlueprintsFile = relBlueprintsFile + + propertyMap, errs := proptools.UnpackProperties(moduleDef.Properties, module.properties...) + if len(errs) > 0 { + for i, err := range errs { + if unpackErr, ok := err.(*proptools.UnpackError); ok { + err = &BlueprintError{ + Err: unpackErr.Err, + Pos: unpackErr.Pos, + } + errs[i] = err + } + } + return nil, errs + } + + module.pos = moduleDef.TypePos + module.propertyPos = make(map[string]scanner.Position) + for name, propertyDef := range propertyMap { + module.propertyPos[name] = propertyDef.ColonPos + } + + return module, nil +} + +func (c *Context) addModule(module *moduleInfo) []error { + name := module.logicModule.Name() + if name == "" { + return []error{ + &BlueprintError{ + Err: fmt.Errorf("property 'name' is missing from a module"), + Pos: module.pos, + }, + } + } + c.moduleInfo[module.logicModule] = module + + group := &moduleGroup{ + name: name, + modules: modulesOrAliases{module}, + } + module.group = group + namespace, errs := c.nameInterface.NewModule( + newNamespaceContext(module), + ModuleGroup{moduleGroup: group}, + module.logicModule) + if len(errs) > 0 { + for i := range errs { + errs[i] = &BlueprintError{Err: errs[i], Pos: module.pos} + } + return errs + } + group.namespace = namespace + + c.moduleGroups = append(c.moduleGroups, group) + + return nil +} + +// ResolveDependencies checks that the dependencies specified by all of the +// modules defined in the parsed Blueprints files are valid. This means that +// the modules depended upon are defined and that no circular dependencies +// exist. +func (c *Context) ResolveDependencies(config interface{}) (deps []string, errs []error) { + c.BeginEvent("resolve_deps") + defer c.EndEvent("resolve_deps") + return c.resolveDependencies(c.Context, config) +} + +func (c *Context) resolveDependencies(ctx context.Context, config interface{}) (deps []string, errs []error) { + pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) { + c.initProviders() + + c.liveGlobals = newLiveTracker(config) + + deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals) + if len(errs) > 0 { + return + } + + errs = c.updateDependencies() + if len(errs) > 0 { + return + } + + var mutatorDeps []string + mutatorDeps, errs = c.runMutators(ctx, config) + if len(errs) > 0 { + return + } + deps = append(deps, mutatorDeps...) + + if !c.skipCloneModulesAfterMutators { + c.cloneModules() + } + + c.dependenciesReady = true + }) + + if len(errs) > 0 { + return nil, errs + } + + return deps, nil +} + +// Default dependencies handling. If the module implements the (deprecated) +// DynamicDependerModule interface then this set consists of the union of those +// module names returned by its DynamicDependencies method and those added by calling +// AddDependencies or AddVariationDependencies on DynamicDependencyModuleContext. +func blueprintDepsMutator(ctx BottomUpMutatorContext) { + if dynamicDepender, ok := ctx.Module().(DynamicDependerModule); ok { + func() { + defer func() { + if r := recover(); r != nil { + ctx.error(newPanicErrorf(r, "DynamicDependencies for %s", ctx.moduleInfo())) + } + }() + dynamicDeps := dynamicDepender.DynamicDependencies(ctx) + + if ctx.Failed() { + return + } + + ctx.AddDependency(ctx.Module(), nil, dynamicDeps...) + }() + } +} + +// findExactVariantOrSingle searches the moduleGroup for a module with the same variant as module, +// and returns the matching module, or nil if one is not found. A group with exactly one module +// is always considered matching. +func findExactVariantOrSingle(module *moduleInfo, possible *moduleGroup, reverse bool) *moduleInfo { + found, _ := findVariant(module, possible, nil, false, reverse) + if found == nil { + for _, moduleOrAlias := range possible.modules { + if m := moduleOrAlias.module(); m != nil { + if found != nil { + // more than one possible match, give up + return nil + } + found = m + } + } + } + return found +} + +func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName string) (*moduleInfo, []error) { + if _, ok := tag.(BaseDependencyTag); ok { + panic("BaseDependencyTag is not allowed to be used directly!") + } + + if depName == module.Name() { + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("%q depends on itself", depName), + Pos: module.pos, + }} + } + + possibleDeps := c.moduleGroupFromName(depName, module.namespace()) + if possibleDeps == nil { + return nil, c.discoveredMissingDependencies(module, depName, nil) + } + + if m := findExactVariantOrSingle(module, possibleDeps, false); m != nil { + module.newDirectDeps = append(module.newDirectDeps, depInfo{m, tag}) + atomic.AddUint32(&c.depsModified, 1) + return m, nil + } + + if c.allowMissingDependencies { + // Allow missing variants. + return nil, c.discoveredMissingDependencies(module, depName, module.variant.dependencyVariations) + } + + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("dependency %q of %q missing variant:\n %s\navailable variants:\n %s", + depName, module.Name(), + c.prettyPrintVariant(module.variant.dependencyVariations), + c.prettyPrintGroupVariants(possibleDeps)), + Pos: module.pos, + }} +} + +func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*moduleInfo, []error) { + if destName == module.Name() { + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("%q depends on itself", destName), + Pos: module.pos, + }} + } + + possibleDeps := c.moduleGroupFromName(destName, module.namespace()) + if possibleDeps == nil { + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("%q has a reverse dependency on undefined module %q", + module.Name(), destName), + Pos: module.pos, + }} + } + + if m := findExactVariantOrSingle(module, possibleDeps, true); m != nil { + return m, nil + } + + if c.allowMissingDependencies { + // Allow missing variants. + return module, c.discoveredMissingDependencies(module, destName, module.variant.dependencyVariations) + } + + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("reverse dependency %q of %q missing variant:\n %s\navailable variants:\n %s", + destName, module.Name(), + c.prettyPrintVariant(module.variant.dependencyVariations), + c.prettyPrintGroupVariants(possibleDeps)), + Pos: module.pos, + }} +} + +func findVariant(module *moduleInfo, possibleDeps *moduleGroup, variations []Variation, far bool, reverse bool) (*moduleInfo, variationMap) { + // We can't just append variant.Variant to module.dependencyVariant.variantName and + // compare the strings because the result won't be in mutator registration order. + // Create a new map instead, and then deep compare the maps. + var newVariant variationMap + if !far { + if !reverse { + // For forward dependency, ignore local variants by matching against + // dependencyVariant which doesn't have the local variants + newVariant = module.variant.dependencyVariations.clone() + } else { + // For reverse dependency, use all the variants + newVariant = module.variant.variations.clone() + } + } + for _, v := range variations { + if newVariant == nil { + newVariant = make(variationMap) + } + newVariant[v.Mutator] = v.Variation + } + + check := func(variant variationMap) bool { + if far { + return newVariant.subsetOf(variant) + } else { + return variant.equal(newVariant) + } + } + + var foundDep *moduleInfo + for _, m := range possibleDeps.modules { + if check(m.moduleOrAliasVariant().variations) { + foundDep = m.moduleOrAliasTarget() + break + } + } + + return foundDep, newVariant +} + +func (c *Context) addVariationDependency(module *moduleInfo, variations []Variation, + tag DependencyTag, depName string, far bool) (*moduleInfo, []error) { + if _, ok := tag.(BaseDependencyTag); ok { + panic("BaseDependencyTag is not allowed to be used directly!") + } + + possibleDeps := c.moduleGroupFromName(depName, module.namespace()) + if possibleDeps == nil { + return nil, c.discoveredMissingDependencies(module, depName, nil) + } + + foundDep, newVariant := findVariant(module, possibleDeps, variations, far, false) + + if foundDep == nil { + if c.allowMissingDependencies { + // Allow missing variants. + return nil, c.discoveredMissingDependencies(module, depName, newVariant) + } + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("dependency %q of %q missing variant:\n %s\navailable variants:\n %s", + depName, module.Name(), + c.prettyPrintVariant(newVariant), + c.prettyPrintGroupVariants(possibleDeps)), + Pos: module.pos, + }} + } + + if module == foundDep { + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("%q depends on itself", depName), + Pos: module.pos, + }} + } + // AddVariationDependency allows adding a dependency on itself, but only if + // that module is earlier in the module list than this one, since we always + // run GenerateBuildActions in order for the variants of a module + if foundDep.group == module.group && beforeInModuleList(module, foundDep, module.group.modules) { + return nil, []error{&BlueprintError{ + Err: fmt.Errorf("%q depends on later version of itself", depName), + Pos: module.pos, + }} + } + module.newDirectDeps = append(module.newDirectDeps, depInfo{foundDep, tag}) + atomic.AddUint32(&c.depsModified, 1) + return foundDep, nil +} + +func (c *Context) addInterVariantDependency(origModule *moduleInfo, tag DependencyTag, + from, to Module) *moduleInfo { + if _, ok := tag.(BaseDependencyTag); ok { + panic("BaseDependencyTag is not allowed to be used directly!") + } + + var fromInfo, toInfo *moduleInfo + for _, moduleOrAlias := range origModule.splitModules { + if m := moduleOrAlias.module(); m != nil { + if m.logicModule == from { + fromInfo = m + } + if m.logicModule == to { + toInfo = m + if fromInfo != nil { + panic(fmt.Errorf("%q depends on later version of itself", origModule.Name())) + } + } + } + } + + if fromInfo == nil || toInfo == nil { + panic(fmt.Errorf("AddInterVariantDependency called for module %q on invalid variant", + origModule.Name())) + } + + fromInfo.newDirectDeps = append(fromInfo.newDirectDeps, depInfo{toInfo, tag}) + atomic.AddUint32(&c.depsModified, 1) + return toInfo +} + +// findBlueprintDescendants returns a map linking parent Blueprint files to child Blueprints files +// For example, if paths = []string{"a/b/c/Android.bp", "a/Android.bp"}, +// then descendants = {"":[]string{"a/Android.bp"}, "a/Android.bp":[]string{"a/b/c/Android.bp"}} +func findBlueprintDescendants(paths []string) (descendants map[string][]string, err error) { + // make mapping from dir path to file path + filesByDir := make(map[string]string, len(paths)) + for _, path := range paths { + dir := filepath.Dir(path) + _, alreadyFound := filesByDir[dir] + if alreadyFound { + return nil, fmt.Errorf("Found two Blueprint files in directory %v : %v and %v", dir, filesByDir[dir], path) + } + filesByDir[dir] = path + } + + findAncestor := func(childFile string) (ancestor string) { + prevAncestorDir := filepath.Dir(childFile) + for { + ancestorDir := filepath.Dir(prevAncestorDir) + if ancestorDir == prevAncestorDir { + // reached the root dir without any matches; assign this as a descendant of "" + return "" + } + + ancestorFile, ancestorExists := filesByDir[ancestorDir] + if ancestorExists { + return ancestorFile + } + prevAncestorDir = ancestorDir + } + } + // generate the descendants map + descendants = make(map[string][]string, len(filesByDir)) + for _, childFile := range filesByDir { + ancestorFile := findAncestor(childFile) + descendants[ancestorFile] = append(descendants[ancestorFile], childFile) + } + return descendants, nil +} + +type visitOrderer interface { + // returns the number of modules that this module needs to wait for + waitCount(module *moduleInfo) int + // returns the list of modules that are waiting for this module + propagate(module *moduleInfo) []*moduleInfo + // visit modules in order + visit(modules []*moduleInfo, visit func(*moduleInfo, chan<- pauseSpec) bool) +} + +type unorderedVisitorImpl struct{} + +func (unorderedVisitorImpl) waitCount(module *moduleInfo) int { + return 0 +} + +func (unorderedVisitorImpl) propagate(module *moduleInfo) []*moduleInfo { + return nil +} + +func (unorderedVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo, chan<- pauseSpec) bool) { + for _, module := range modules { + if visit(module, nil) { + return + } + } +} + +type bottomUpVisitorImpl struct{} + +func (bottomUpVisitorImpl) waitCount(module *moduleInfo) int { + return len(module.forwardDeps) +} + +func (bottomUpVisitorImpl) propagate(module *moduleInfo) []*moduleInfo { + return module.reverseDeps +} + +func (bottomUpVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo, chan<- pauseSpec) bool) { + for _, module := range modules { + if visit(module, nil) { + return + } + } +} + +type topDownVisitorImpl struct{} + +func (topDownVisitorImpl) waitCount(module *moduleInfo) int { + return len(module.reverseDeps) +} + +func (topDownVisitorImpl) propagate(module *moduleInfo) []*moduleInfo { + return module.forwardDeps +} + +func (topDownVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo, chan<- pauseSpec) bool) { + for i := 0; i < len(modules); i++ { + module := modules[len(modules)-1-i] + if visit(module, nil) { + return + } + } +} + +var ( + bottomUpVisitor bottomUpVisitorImpl + topDownVisitor topDownVisitorImpl +) + +// pauseSpec describes a pause that a module needs to occur until another module has been visited, +// at which point the unpause channel will be closed. +type pauseSpec struct { + paused *moduleInfo + until *moduleInfo + unpause unpause +} + +type unpause chan struct{} + +const parallelVisitLimit = 1000 + +// Calls visit on each module, guaranteeing that visit is not called on a module until visit on all +// of its dependencies has finished. A visit function can write a pauseSpec to the pause channel +// to wait for another dependency to be visited. If a visit function returns true to cancel +// while another visitor is paused, the paused visitor will never be resumed and its goroutine +// will stay paused forever. +func parallelVisit(modules []*moduleInfo, order visitOrderer, limit int, + visit func(module *moduleInfo, pause chan<- pauseSpec) bool) []error { + + doneCh := make(chan *moduleInfo) + cancelCh := make(chan bool) + pauseCh := make(chan pauseSpec) + cancel := false + + var backlog []*moduleInfo // Visitors that are ready to start but backlogged due to limit. + var unpauseBacklog []pauseSpec // Visitors that are ready to unpause but backlogged due to limit. + + active := 0 // Number of visitors running, not counting paused visitors. + visited := 0 // Number of finished visitors. + + pauseMap := make(map[*moduleInfo][]pauseSpec) + + for _, module := range modules { + module.waitingCount = order.waitCount(module) + } + + // Call the visitor on a module if there are fewer active visitors than the parallelism + // limit, otherwise add it to the backlog. + startOrBacklog := func(module *moduleInfo) { + if active < limit { + active++ + go func() { + ret := visit(module, pauseCh) + if ret { + cancelCh <- true + } + doneCh <- module + }() + } else { + backlog = append(backlog, module) + } + } + + // Unpause the already-started but paused visitor on a module if there are fewer active + // visitors than the parallelism limit, otherwise add it to the backlog. + unpauseOrBacklog := func(pauseSpec pauseSpec) { + if active < limit { + active++ + close(pauseSpec.unpause) + } else { + unpauseBacklog = append(unpauseBacklog, pauseSpec) + } + } + + // Start any modules in the backlog up to the parallelism limit. Unpause paused modules first + // since they may already be holding resources. + unpauseOrStartFromBacklog := func() { + for active < limit && len(unpauseBacklog) > 0 { + unpause := unpauseBacklog[0] + unpauseBacklog = unpauseBacklog[1:] + unpauseOrBacklog(unpause) + } + for active < limit && len(backlog) > 0 { + toVisit := backlog[0] + backlog = backlog[1:] + startOrBacklog(toVisit) + } + } + + toVisit := len(modules) + + // Start or backlog any modules that are not waiting for any other modules. + for _, module := range modules { + if module.waitingCount == 0 { + startOrBacklog(module) + } + } + + for active > 0 { + select { + case <-cancelCh: + cancel = true + backlog = nil + case doneModule := <-doneCh: + active-- + if !cancel { + // Mark this module as done. + doneModule.waitingCount = -1 + visited++ + + // Unpause or backlog any modules that were waiting for this one. + if unpauses, ok := pauseMap[doneModule]; ok { + delete(pauseMap, doneModule) + for _, unpause := range unpauses { + unpauseOrBacklog(unpause) + } + } + + // Start any backlogged modules up to limit. + unpauseOrStartFromBacklog() + + // Decrement waitingCount on the next modules in the tree based + // on propagation order, and start or backlog them if they are + // ready to start. + for _, module := range order.propagate(doneModule) { + module.waitingCount-- + if module.waitingCount == 0 { + startOrBacklog(module) + } + } + } + case pauseSpec := <-pauseCh: + if pauseSpec.until.waitingCount == -1 { + // Module being paused for is already finished, resume immediately. + close(pauseSpec.unpause) + } else { + // Register for unpausing. + pauseMap[pauseSpec.until] = append(pauseMap[pauseSpec.until], pauseSpec) + + // Don't count paused visitors as active so that this can't deadlock + // if 1000 visitors are paused simultaneously. + active-- + unpauseOrStartFromBacklog() + } + } + } + + if !cancel { + // Invariant check: no backlogged modules, these weren't waiting on anything except + // the parallelism limit so they should have run. + if len(backlog) > 0 { + panic(fmt.Errorf("parallelVisit finished with %d backlogged visitors", len(backlog))) + } + + // Invariant check: no backlogged paused modules, these weren't waiting on anything + // except the parallelism limit so they should have run. + if len(unpauseBacklog) > 0 { + panic(fmt.Errorf("parallelVisit finished with %d backlogged unpaused visitors", len(unpauseBacklog))) + } + + if len(pauseMap) > 0 { + // Probably a deadlock due to a newly added dependency cycle. Start from each module in + // the order of the input modules list and perform a depth-first search for the module + // it is paused on, ignoring modules that are marked as done. Note this traverses from + // modules to the modules that would have been unblocked when that module finished, i.e + // the reverse of the visitOrderer. + + // In order to reduce duplicated work, once a module has been checked and determined + // not to be part of a cycle add it and everything that depends on it to the checked + // map. + checked := make(map[*moduleInfo]struct{}) + + var check func(module, end *moduleInfo) []*moduleInfo + check = func(module, end *moduleInfo) []*moduleInfo { + if module.waitingCount == -1 { + // This module was finished, it can't be part of a loop. + return nil + } + if module == end { + // This module is the end of the loop, start rolling up the cycle. + return []*moduleInfo{module} + } + + if _, alreadyChecked := checked[module]; alreadyChecked { + return nil + } + + for _, dep := range order.propagate(module) { + cycle := check(dep, end) + if cycle != nil { + return append([]*moduleInfo{module}, cycle...) + } + } + for _, depPauseSpec := range pauseMap[module] { + cycle := check(depPauseSpec.paused, end) + if cycle != nil { + return append([]*moduleInfo{module}, cycle...) + } + } + + checked[module] = struct{}{} + return nil + } + + // Iterate over the modules list instead of pauseMap to provide deterministic ordering. + for _, module := range modules { + for _, pauseSpec := range pauseMap[module] { + cycle := check(pauseSpec.paused, pauseSpec.until) + if len(cycle) > 0 { + return cycleError(cycle) + } + } + } + } + + // Invariant check: if there was no deadlock and no cancellation every module + // should have been visited. + if visited != toVisit { + panic(fmt.Errorf("parallelVisit ran %d visitors, expected %d", visited, toVisit)) + } + + // Invariant check: if there was no deadlock and no cancellation every module + // should have been visited, so there is nothing left to be paused on. + if len(pauseMap) > 0 { + panic(fmt.Errorf("parallelVisit finished with %d paused visitors", len(pauseMap))) + } + } + + return nil +} + +func cycleError(cycle []*moduleInfo) (errs []error) { + // The cycle list is in reverse order because all the 'check' calls append + // their own module to the list. + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf("encountered dependency cycle:"), + Pos: cycle[len(cycle)-1].pos, + }) + + // Iterate backwards through the cycle list. + curModule := cycle[0] + for i := len(cycle) - 1; i >= 0; i-- { + nextModule := cycle[i] + errs = append(errs, &BlueprintError{ + Err: fmt.Errorf(" %s depends on %s", + curModule, nextModule), + Pos: curModule.pos, + }) + curModule = nextModule + } + + return errs +} + +// updateDependencies recursively walks the module dependency graph and updates +// additional fields based on the dependencies. It builds a sorted list of modules +// such that dependencies of a module always appear first, and populates reverse +// dependency links and counts of total dependencies. It also reports errors when +// it encounters dependency cycles. This should be called after resolveDependencies, +// as well as after any mutator pass has called addDependency +func (c *Context) updateDependencies() (errs []error) { + c.cachedDepsModified = true + visited := make(map[*moduleInfo]bool) // modules that were already checked + checking := make(map[*moduleInfo]bool) // modules actively being checked + + sorted := make([]*moduleInfo, 0, len(c.moduleInfo)) + + var check func(group *moduleInfo) []*moduleInfo + + check = func(module *moduleInfo) []*moduleInfo { + visited[module] = true + checking[module] = true + defer delete(checking, module) + + // Reset the forward and reverse deps without reducing their capacity to avoid reallocation. + module.reverseDeps = module.reverseDeps[:0] + module.forwardDeps = module.forwardDeps[:0] + + // Add an implicit dependency ordering on all earlier modules in the same module group + for _, dep := range module.group.modules { + if dep == module { + break + } + if depModule := dep.module(); depModule != nil { + module.forwardDeps = append(module.forwardDeps, depModule) + } + } + + outer: + for _, dep := range module.directDeps { + // use a loop to check for duplicates, average number of directDeps measured to be 9.5. + for _, exists := range module.forwardDeps { + if dep.module == exists { + continue outer + } + } + module.forwardDeps = append(module.forwardDeps, dep.module) + } + + for _, dep := range module.forwardDeps { + if checking[dep] { + // This is a cycle. + return []*moduleInfo{dep, module} + } + + if !visited[dep] { + cycle := check(dep) + if cycle != nil { + if cycle[0] == module { + // We are the "start" of the cycle, so we're responsible + // for generating the errors. + errs = append(errs, cycleError(cycle)...) + + // We can continue processing this module's children to + // find more cycles. Since all the modules that were + // part of the found cycle were marked as visited we + // won't run into that cycle again. + } else { + // We're not the "start" of the cycle, so we just append + // our module to the list and return it. + return append(cycle, module) + } + } + } + + dep.reverseDeps = append(dep.reverseDeps, module) + } + + sorted = append(sorted, module) + + return nil + } + + for _, module := range c.moduleInfo { + if !visited[module] { + cycle := check(module) + if cycle != nil { + if cycle[len(cycle)-1] != module { + panic("inconceivable!") + } + errs = append(errs, cycleError(cycle)...) + } + } + } + + c.modulesSorted = sorted + + return +} + +type jsonVariationMap []Variation + +type jsonModuleName struct { + Name string + Variations jsonVariationMap + DependencyVariations jsonVariationMap +} + +type jsonDep struct { + jsonModuleName + Tag string +} + +type JsonModule struct { + jsonModuleName + Deps []jsonDep + Type string + Blueprint string + Module map[string]interface{} +} + +func toJsonVariationMap(vm variationMap) jsonVariationMap { + m := make(jsonVariationMap, 0, len(vm)) + for k, v := range vm { + m = append(m, Variation{k, v}) + } + sort.Slice(m, func(i, j int) bool { + if m[i].Mutator != m[j].Mutator { + return m[i].Mutator < m[j].Mutator + } + return m[i].Variation < m[j].Variation + }) + return m +} + +func jsonModuleNameFromModuleInfo(m *moduleInfo) *jsonModuleName { + return &jsonModuleName{ + Name: m.Name(), + Variations: toJsonVariationMap(m.variant.variations), + DependencyVariations: toJsonVariationMap(m.variant.dependencyVariations), + } +} + +type JSONDataSupplier interface { + AddJSONData(d *map[string]interface{}) +} + +func jsonModuleFromModuleInfo(m *moduleInfo) *JsonModule { + result := &JsonModule{ + jsonModuleName: *jsonModuleNameFromModuleInfo(m), + Deps: make([]jsonDep, 0), + Type: m.typeName, + Blueprint: m.relBlueprintsFile, + Module: make(map[string]interface{}), + } + if j, ok := m.logicModule.(JSONDataSupplier); ok { + j.AddJSONData(&result.Module) + } + for _, p := range m.providers { + if j, ok := p.(JSONDataSupplier); ok { + j.AddJSONData(&result.Module) + } + } + return result +} + +func jsonModuleWithActionsFromModuleInfo(m *moduleInfo) *JsonModule { + result := &JsonModule{ + jsonModuleName: jsonModuleName{ + Name: m.Name(), + }, + Deps: make([]jsonDep, 0), + Type: m.typeName, + Blueprint: m.relBlueprintsFile, + Module: make(map[string]interface{}), + } + var actions []map[string]interface{} + for _, bDef := range m.actionDefs.buildDefs { + actions = append(actions, map[string]interface{}{ + "Inputs": append( + getNinjaStringsWithNilPkgNames(bDef.Inputs), + getNinjaStringsWithNilPkgNames(bDef.Implicits)...), + "Outputs": append( + getNinjaStringsWithNilPkgNames(bDef.Outputs), + getNinjaStringsWithNilPkgNames(bDef.ImplicitOutputs)...), + }) + } + result.Module["Actions"] = actions + return result +} + +// Gets a list of strings from the given list of ninjaStrings by invoking ninjaString.Value with +// nil pkgNames on each of the input ninjaStrings. +func getNinjaStringsWithNilPkgNames(nStrs []ninjaString) []string { + var strs []string + for _, nstr := range nStrs { + strs = append(strs, nstr.Value(nil)) + } + return strs +} + +// PrintJSONGraph prints info of modules in a JSON file. +func (c *Context) PrintJSONGraphAndActions(wGraph io.Writer, wActions io.Writer) { + modulesToGraph := make([]*JsonModule, 0) + modulesToActions := make([]*JsonModule, 0) + for _, m := range c.modulesSorted { + jm := jsonModuleFromModuleInfo(m) + jmWithActions := jsonModuleWithActionsFromModuleInfo(m) + for _, d := range m.directDeps { + jm.Deps = append(jm.Deps, jsonDep{ + jsonModuleName: *jsonModuleNameFromModuleInfo(d.module), + Tag: fmt.Sprintf("%T %+v", d.tag, d.tag), + }) + jmWithActions.Deps = append(jmWithActions.Deps, jsonDep{ + jsonModuleName: jsonModuleName{ + Name: d.module.Name(), + }, + }) + + } + modulesToGraph = append(modulesToGraph, jm) + modulesToActions = append(modulesToActions, jmWithActions) + } + writeJson(wGraph, modulesToGraph) + writeJson(wActions, modulesToActions) +} + +func writeJson(w io.Writer, modules []*JsonModule) { + e := json.NewEncoder(w) + e.SetIndent("", "\t") + e.Encode(modules) +} + +// PrepareBuildActions generates an internal representation of all the build +// actions that need to be performed. This process involves invoking the +// GenerateBuildActions method on each of the Module objects created during the +// parse phase and then on each of the registered Singleton objects. +// +// If the ResolveDependencies method has not already been called it is called +// automatically by this method. +// +// The config argument is made available to all of the Module and Singleton +// objects via the Config method on the ModuleContext and SingletonContext +// objects passed to GenerateBuildActions. It is also passed to the functions +// specified via PoolFunc, RuleFunc, and VariableFunc so that they can compute +// config-specific values. +// +// The returned deps is a list of the ninja files dependencies that were added +// by the modules and singletons via the ModuleContext.AddNinjaFileDeps(), +// SingletonContext.AddNinjaFileDeps(), and PackageContext.AddNinjaFileDeps() +// methods. + +func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) { + c.BeginEvent("prepare_build_actions") + defer c.EndEvent("prepare_build_actions") + pprof.Do(c.Context, pprof.Labels("blueprint", "PrepareBuildActions"), func(ctx context.Context) { + c.buildActionsReady = false + + if !c.dependenciesReady { + var extraDeps []string + extraDeps, errs = c.resolveDependencies(ctx, config) + if len(errs) > 0 { + return + } + deps = append(deps, extraDeps...) + } + + var depsModules []string + depsModules, errs = c.generateModuleBuildActions(config, c.liveGlobals) + if len(errs) > 0 { + return + } + + var depsSingletons []string + depsSingletons, errs = c.generateSingletonBuildActions(config, c.singletonInfo, c.liveGlobals) + if len(errs) > 0 { + return + } + + deps = append(deps, depsModules...) + deps = append(deps, depsSingletons...) + + if c.outDir != nil { + err := c.liveGlobals.addNinjaStringDeps(c.outDir) + if err != nil { + errs = []error{err} + return + } + } + + pkgNames, depsPackages := c.makeUniquePackageNames(c.liveGlobals) + + deps = append(deps, depsPackages...) + + c.memoizeFullNames(c.liveGlobals, pkgNames) + + // This will panic if it finds a problem since it's a programming error. + c.checkForVariableReferenceCycles(c.liveGlobals.variables, pkgNames) + + c.pkgNames = pkgNames + c.globalVariables = c.liveGlobals.variables + c.globalPools = c.liveGlobals.pools + c.globalRules = c.liveGlobals.rules + + c.buildActionsReady = true + }) + + if len(errs) > 0 { + return nil, errs + } + + return deps, nil +} + +func (c *Context) runMutators(ctx context.Context, config interface{}) (deps []string, errs []error) { + pprof.Do(ctx, pprof.Labels("blueprint", "runMutators"), func(ctx context.Context) { + for _, mutator := range c.mutatorInfo { + pprof.Do(ctx, pprof.Labels("mutator", mutator.name), func(context.Context) { + var newDeps []string + if mutator.topDownMutator != nil { + newDeps, errs = c.runMutator(config, mutator, topDownMutator) + } else if mutator.bottomUpMutator != nil { + newDeps, errs = c.runMutator(config, mutator, bottomUpMutator) + } else { + panic("no mutator set on " + mutator.name) + } + if len(errs) > 0 { + return + } + deps = append(deps, newDeps...) + }) + if len(errs) > 0 { + return + } + } + }) + + if len(errs) > 0 { + return nil, errs + } + + return deps, nil +} + +type mutatorDirection interface { + run(mutator *mutatorInfo, ctx *mutatorContext) + orderer() visitOrderer + fmt.Stringer +} + +type bottomUpMutatorImpl struct{} + +func (bottomUpMutatorImpl) run(mutator *mutatorInfo, ctx *mutatorContext) { + mutator.bottomUpMutator(ctx) +} + +func (bottomUpMutatorImpl) orderer() visitOrderer { + return bottomUpVisitor +} + +func (bottomUpMutatorImpl) String() string { + return "bottom up mutator" +} + +type topDownMutatorImpl struct{} + +func (topDownMutatorImpl) run(mutator *mutatorInfo, ctx *mutatorContext) { + mutator.topDownMutator(ctx) +} + +func (topDownMutatorImpl) orderer() visitOrderer { + return topDownVisitor +} + +func (topDownMutatorImpl) String() string { + return "top down mutator" +} + +var ( + topDownMutator topDownMutatorImpl + bottomUpMutator bottomUpMutatorImpl +) + +type reverseDep struct { + module *moduleInfo + dep depInfo +} + +func (c *Context) runMutator(config interface{}, mutator *mutatorInfo, + direction mutatorDirection) (deps []string, errs []error) { + + newModuleInfo := make(map[Module]*moduleInfo) + for k, v := range c.moduleInfo { + newModuleInfo[k] = v + } + + type globalStateChange struct { + reverse []reverseDep + rename []rename + replace []replace + newModules []*moduleInfo + deps []string + } + + reverseDeps := make(map[*moduleInfo][]depInfo) + var rename []rename + var replace []replace + var newModules []*moduleInfo + + errsCh := make(chan []error) + globalStateCh := make(chan globalStateChange) + newVariationsCh := make(chan modulesOrAliases) + done := make(chan bool) + + c.depsModified = 0 + + visit := func(module *moduleInfo, pause chan<- pauseSpec) bool { + if module.splitModules != nil { + panic("split module found in sorted module list") + } + + mctx := &mutatorContext{ + baseModuleContext: baseModuleContext{ + context: c, + config: config, + module: module, + }, + name: mutator.name, + pauseCh: pause, + } + + module.startedMutator = mutator + + func() { + defer func() { + if r := recover(); r != nil { + in := fmt.Sprintf("%s %q for %s", direction, mutator.name, module) + if err, ok := r.(panicError); ok { + err.addIn(in) + mctx.error(err) + } else { + mctx.error(newPanicErrorf(r, in)) + } + } + }() + direction.run(mutator, mctx) + }() + + module.finishedMutator = mutator + + if len(mctx.errs) > 0 { + errsCh <- mctx.errs + return true + } + + if len(mctx.newVariations) > 0 { + newVariationsCh <- mctx.newVariations + } + + if len(mctx.reverseDeps) > 0 || len(mctx.replace) > 0 || len(mctx.rename) > 0 || len(mctx.newModules) > 0 || len(mctx.ninjaFileDeps) > 0 { + globalStateCh <- globalStateChange{ + reverse: mctx.reverseDeps, + replace: mctx.replace, + rename: mctx.rename, + newModules: mctx.newModules, + deps: mctx.ninjaFileDeps, + } + } + + return false + } + + // Process errs and reverseDeps in a single goroutine + go func() { + for { + select { + case newErrs := <-errsCh: + errs = append(errs, newErrs...) + case globalStateChange := <-globalStateCh: + for _, r := range globalStateChange.reverse { + reverseDeps[r.module] = append(reverseDeps[r.module], r.dep) + } + replace = append(replace, globalStateChange.replace...) + rename = append(rename, globalStateChange.rename...) + newModules = append(newModules, globalStateChange.newModules...) + deps = append(deps, globalStateChange.deps...) + case newVariations := <-newVariationsCh: + for _, moduleOrAlias := range newVariations { + if m := moduleOrAlias.module(); m != nil { + newModuleInfo[m.logicModule] = m + } + } + case <-done: + return + } + } + }() + + c.startedMutator = mutator + + var visitErrs []error + if mutator.parallel { + visitErrs = parallelVisit(c.modulesSorted, direction.orderer(), parallelVisitLimit, visit) + } else { + direction.orderer().visit(c.modulesSorted, visit) + } + + if len(visitErrs) > 0 { + return nil, visitErrs + } + + c.finishedMutators[mutator] = true + + done <- true + + if len(errs) > 0 { + return nil, errs + } + + c.moduleInfo = newModuleInfo + + for _, group := range c.moduleGroups { + for i := 0; i < len(group.modules); i++ { + module := group.modules[i].module() + if module == nil { + // Existing alias, skip it + continue + } + + // Update module group to contain newly split variants + if module.splitModules != nil { + group.modules, i = spliceModules(group.modules, i, module.splitModules) + } + + // Fix up any remaining dependencies on modules that were split into variants + // by replacing them with the first variant + for j, dep := range module.directDeps { + if dep.module.logicModule == nil { + module.directDeps[j].module = dep.module.splitModules.firstModule() + } + } + + if module.createdBy != nil && module.createdBy.logicModule == nil { + module.createdBy = module.createdBy.splitModules.firstModule() + } + + // Add in any new direct dependencies that were added by the mutator + module.directDeps = append(module.directDeps, module.newDirectDeps...) + module.newDirectDeps = nil + } + + findAliasTarget := func(variant variant) *moduleInfo { + for _, moduleOrAlias := range group.modules { + if alias := moduleOrAlias.alias(); alias != nil { + if alias.variant.variations.equal(variant.variations) { + return alias.target + } + } + } + return nil + } + + // Forward or delete any dangling aliases. + // Use a manual loop instead of range because len(group.modules) can + // change inside the loop + for i := 0; i < len(group.modules); i++ { + if alias := group.modules[i].alias(); alias != nil { + if alias.target.logicModule == nil { + newTarget := findAliasTarget(alias.target.variant) + if newTarget != nil { + alias.target = newTarget + } else { + // The alias was left dangling, remove it. + group.modules = append(group.modules[:i], group.modules[i+1:]...) + i-- + } + } + } + } + } + + // Add in any new reverse dependencies that were added by the mutator + for module, deps := range reverseDeps { + sort.Sort(depSorter(deps)) + module.directDeps = append(module.directDeps, deps...) + c.depsModified++ + } + + for _, module := range newModules { + errs = c.addModule(module) + if len(errs) > 0 { + return nil, errs + } + atomic.AddUint32(&c.depsModified, 1) + } + + errs = c.handleRenames(rename) + if len(errs) > 0 { + return nil, errs + } + + errs = c.handleReplacements(replace) + if len(errs) > 0 { + return nil, errs + } + + if c.depsModified > 0 { + errs = c.updateDependencies() + if len(errs) > 0 { + return nil, errs + } + } + + return deps, errs +} + +// Replaces every build logic module with a clone of itself. Prevents introducing problems where +// a mutator sets a non-property member variable on a module, which works until a later mutator +// creates variants of that module. +func (c *Context) cloneModules() { + type update struct { + orig Module + clone *moduleInfo + } + ch := make(chan update) + doneCh := make(chan bool) + go func() { + errs := parallelVisit(c.modulesSorted, unorderedVisitorImpl{}, parallelVisitLimit, + func(m *moduleInfo, pause chan<- pauseSpec) bool { + origLogicModule := m.logicModule + m.logicModule, m.properties = c.cloneLogicModule(m) + ch <- update{origLogicModule, m} + return false + }) + if len(errs) > 0 { + panic(errs) + } + doneCh <- true + }() + + done := false + for !done { + select { + case <-doneCh: + done = true + case update := <-ch: + delete(c.moduleInfo, update.orig) + c.moduleInfo[update.clone.logicModule] = update.clone + } + } +} + +// Removes modules[i] from the list and inserts newModules... where it was located, returning +// the new slice and the index of the last inserted element +func spliceModules(modules modulesOrAliases, i int, newModules modulesOrAliases) (modulesOrAliases, int) { + spliceSize := len(newModules) + newLen := len(modules) + spliceSize - 1 + var dest modulesOrAliases + if cap(modules) >= len(modules)-1+len(newModules) { + // We can fit the splice in the existing capacity, do everything in place + dest = modules[:newLen] + } else { + dest = make(modulesOrAliases, newLen) + copy(dest, modules[:i]) + } + + // Move the end of the slice over by spliceSize-1 + copy(dest[i+spliceSize:], modules[i+1:]) + + // Copy the new modules into the slice + copy(dest[i:], newModules) + + return dest, i + spliceSize - 1 +} + +func (c *Context) generateModuleBuildActions(config interface{}, + liveGlobals *liveTracker) ([]string, []error) { + + var deps []string + var errs []error + + cancelCh := make(chan struct{}) + errsCh := make(chan []error) + depsCh := make(chan []string) + + go func() { + for { + select { + case <-cancelCh: + close(cancelCh) + return + case newErrs := <-errsCh: + errs = append(errs, newErrs...) + case newDeps := <-depsCh: + deps = append(deps, newDeps...) + + } + } + }() + + visitErrs := parallelVisit(c.modulesSorted, bottomUpVisitor, parallelVisitLimit, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + uniqueName := c.nameInterface.UniqueName(newNamespaceContext(module), module.group.name) + sanitizedName := toNinjaName(uniqueName) + sanitizedVariant := toNinjaName(module.variant.name) + + prefix := moduleNamespacePrefix(sanitizedName + "_" + sanitizedVariant) + + // The parent scope of the moduleContext's local scope gets overridden to be that of the + // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we + // just set it to nil. + scope := newLocalScope(nil, prefix) + + mctx := &moduleContext{ + baseModuleContext: baseModuleContext{ + context: c, + config: config, + module: module, + }, + scope: scope, + handledMissingDeps: module.missingDeps == nil, + } + + mctx.module.startedGenerateBuildActions = true + + func() { + defer func() { + if r := recover(); r != nil { + in := fmt.Sprintf("GenerateBuildActions for %s", module) + if err, ok := r.(panicError); ok { + err.addIn(in) + mctx.error(err) + } else { + mctx.error(newPanicErrorf(r, in)) + } + } + }() + mctx.module.logicModule.GenerateBuildActions(mctx) + }() + + mctx.module.finishedGenerateBuildActions = true + + if len(mctx.errs) > 0 { + errsCh <- mctx.errs + return true + } + + if module.missingDeps != nil && !mctx.handledMissingDeps { + var errs []error + for _, depName := range module.missingDeps { + errs = append(errs, c.missingDependencyError(module, depName)) + } + errsCh <- errs + return true + } + + depsCh <- mctx.ninjaFileDeps + + newErrs := c.processLocalBuildActions(&module.actionDefs, + &mctx.actionDefs, liveGlobals) + if len(newErrs) > 0 { + errsCh <- newErrs + return true + } + return false + }) + + cancelCh <- struct{}{} + <-cancelCh + + errs = append(errs, visitErrs...) + + return deps, errs +} + +func (c *Context) generateSingletonBuildActions(config interface{}, + singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) { + + var deps []string + var errs []error + + for _, info := range singletons { + // The parent scope of the singletonContext's local scope gets overridden to be that of the + // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we + // just set it to nil. + scope := newLocalScope(nil, singletonNamespacePrefix(info.name)) + + sctx := &singletonContext{ + name: info.name, + context: c, + config: config, + scope: scope, + globals: liveGlobals, + } + + func() { + defer func() { + if r := recover(); r != nil { + in := fmt.Sprintf("GenerateBuildActions for singleton %s", info.name) + if err, ok := r.(panicError); ok { + err.addIn(in) + sctx.error(err) + } else { + sctx.error(newPanicErrorf(r, in)) + } + } + }() + info.singleton.GenerateBuildActions(sctx) + }() + + if len(sctx.errs) > 0 { + errs = append(errs, sctx.errs...) + if len(errs) > maxErrors { + break + } + continue + } + + deps = append(deps, sctx.ninjaFileDeps...) + + newErrs := c.processLocalBuildActions(&info.actionDefs, + &sctx.actionDefs, liveGlobals) + errs = append(errs, newErrs...) + if len(errs) > maxErrors { + break + } + } + + return deps, errs +} + +func (c *Context) processLocalBuildActions(out, in *localBuildActions, + liveGlobals *liveTracker) []error { + + var errs []error + + // First we go through and add everything referenced by the module's + // buildDefs to the live globals set. This will end up adding the live + // locals to the set as well, but we'll take them out after. + for _, def := range in.buildDefs { + err := liveGlobals.AddBuildDefDeps(def) + if err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + return errs + } + + out.buildDefs = append(out.buildDefs, in.buildDefs...) + + // We use the now-incorrect set of live "globals" to determine which local + // definitions are live. As we go through copying those live locals to the + // moduleGroup we remove them from the live globals set. + for _, v := range in.variables { + isLive := liveGlobals.RemoveVariableIfLive(v) + if isLive { + out.variables = append(out.variables, v) + } + } + + for _, r := range in.rules { + isLive := liveGlobals.RemoveRuleIfLive(r) + if isLive { + out.rules = append(out.rules, r) + } + } + + return nil +} + +func (c *Context) walkDeps(topModule *moduleInfo, allowDuplicates bool, + visitDown func(depInfo, *moduleInfo) bool, visitUp func(depInfo, *moduleInfo)) { + + visited := make(map[*moduleInfo]bool) + var visiting *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "WalkDeps(%s, %s, %s) for dependency %s", + topModule, funcName(visitDown), funcName(visitUp), visiting)) + } + }() + + var walk func(module *moduleInfo) + walk = func(module *moduleInfo) { + for _, dep := range module.directDeps { + if allowDuplicates || !visited[dep.module] { + visiting = dep.module + recurse := true + if visitDown != nil { + recurse = visitDown(dep, module) + } + if recurse && !visited[dep.module] { + walk(dep.module) + visited[dep.module] = true + } + if visitUp != nil { + visitUp(dep, module) + } + } + } + } + + walk(topModule) +} + +type replace struct { + from, to *moduleInfo + predicate ReplaceDependencyPredicate +} + +type rename struct { + group *moduleGroup + name string +} + +func (c *Context) moduleMatchingVariant(module *moduleInfo, name string) *moduleInfo { + group := c.moduleGroupFromName(name, module.namespace()) + + if group == nil { + return nil + } + + for _, m := range group.modules { + if module.variant.name == m.moduleOrAliasVariant().name { + return m.moduleOrAliasTarget() + } + } + + return nil +} + +func (c *Context) handleRenames(renames []rename) []error { + var errs []error + for _, rename := range renames { + group, name := rename.group, rename.name + if name == group.name || len(group.modules) < 1 { + continue + } + + errs = append(errs, c.nameInterface.Rename(group.name, rename.name, group.namespace)...) + } + + return errs +} + +func (c *Context) handleReplacements(replacements []replace) []error { + var errs []error + changedDeps := false + for _, replace := range replacements { + for _, m := range replace.from.reverseDeps { + for i, d := range m.directDeps { + if d.module == replace.from { + // If the replacement has a predicate then check it. + if replace.predicate == nil || replace.predicate(m.logicModule, d.tag, d.module.logicModule) { + m.directDeps[i].module = replace.to + changedDeps = true + } + } + } + } + + } + + if changedDeps { + atomic.AddUint32(&c.depsModified, 1) + } + return errs +} + +func (c *Context) discoveredMissingDependencies(module *moduleInfo, depName string, depVariations variationMap) (errs []error) { + if depVariations != nil { + depName = depName + "{" + c.prettyPrintVariant(depVariations) + "}" + } + if c.allowMissingDependencies { + module.missingDeps = append(module.missingDeps, depName) + return nil + } + return []error{c.missingDependencyError(module, depName)} +} + +func (c *Context) missingDependencyError(module *moduleInfo, depName string) (errs error) { + err := c.nameInterface.MissingDependencyError(module.Name(), module.namespace(), depName) + + return &BlueprintError{ + Err: err, + Pos: module.pos, + } +} + +func (c *Context) moduleGroupFromName(name string, namespace Namespace) *moduleGroup { + group, exists := c.nameInterface.ModuleFromName(name, namespace) + if exists { + return group.moduleGroup + } + return nil +} + +func (c *Context) sortedModuleGroups() []*moduleGroup { + if c.cachedSortedModuleGroups == nil || c.cachedDepsModified { + unwrap := func(wrappers []ModuleGroup) []*moduleGroup { + result := make([]*moduleGroup, 0, len(wrappers)) + for _, group := range wrappers { + result = append(result, group.moduleGroup) + } + return result + } + + c.cachedSortedModuleGroups = unwrap(c.nameInterface.AllModules()) + c.cachedDepsModified = false + } + + return c.cachedSortedModuleGroups +} + +func (c *Context) visitAllModules(visit func(Module)) { + var module *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitAllModules(%s) for %s", + funcName(visit), module)) + } + }() + + for _, moduleGroup := range c.sortedModuleGroups() { + for _, moduleOrAlias := range moduleGroup.modules { + if module = moduleOrAlias.module(); module != nil { + visit(module.logicModule) + } + } + } +} + +func (c *Context) visitAllModulesIf(pred func(Module) bool, + visit func(Module)) { + + var module *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitAllModulesIf(%s, %s) for %s", + funcName(pred), funcName(visit), module)) + } + }() + + for _, moduleGroup := range c.sortedModuleGroups() { + for _, moduleOrAlias := range moduleGroup.modules { + if module = moduleOrAlias.module(); module != nil { + if pred(module.logicModule) { + visit(module.logicModule) + } + } + } + } +} + +func (c *Context) visitAllModuleVariants(module *moduleInfo, + visit func(Module)) { + + var variant *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitAllModuleVariants(%s, %s) for %s", + module, funcName(visit), variant)) + } + }() + + for _, moduleOrAlias := range module.group.modules { + if variant = moduleOrAlias.module(); variant != nil { + visit(variant.logicModule) + } + } +} + +func (c *Context) requireNinjaVersion(major, minor, micro int) { + if major != 1 { + panic("ninja version with major version != 1 not supported") + } + if c.requiredNinjaMinor < minor { + c.requiredNinjaMinor = minor + c.requiredNinjaMicro = micro + } + if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro { + c.requiredNinjaMicro = micro + } +} + +func (c *Context) setOutDir(value ninjaString) { + if c.outDir == nil { + c.outDir = value + } +} + +func (c *Context) makeUniquePackageNames( + liveGlobals *liveTracker) (map[*packageContext]string, []string) { + + pkgs := make(map[string]*packageContext) + pkgNames := make(map[*packageContext]string) + longPkgNames := make(map[*packageContext]bool) + + processPackage := func(pctx *packageContext) { + if pctx == nil { + // This is a built-in rule and has no package. + return + } + if _, ok := pkgNames[pctx]; ok { + // We've already processed this package. + return + } + + otherPkg, present := pkgs[pctx.shortName] + if present { + // Short name collision. Both this package and the one that's + // already there need to use their full names. We leave the short + // name in pkgNames for now so future collisions still get caught. + longPkgNames[pctx] = true + longPkgNames[otherPkg] = true + } else { + // No collision so far. Tentatively set the package's name to be + // its short name. + pkgNames[pctx] = pctx.shortName + pkgs[pctx.shortName] = pctx + } + } + + // We try to give all packages their short name, but when we get collisions + // we need to use the full unique package name. + for v, _ := range liveGlobals.variables { + processPackage(v.packageContext()) + } + for p, _ := range liveGlobals.pools { + processPackage(p.packageContext()) + } + for r, _ := range liveGlobals.rules { + processPackage(r.packageContext()) + } + + // Add the packages that had collisions using their full unique names. This + // will overwrite any short names that were added in the previous step. + for pctx := range longPkgNames { + pkgNames[pctx] = pctx.fullName + } + + // Create deps list from calls to PackageContext.AddNinjaFileDeps + deps := []string{} + for _, pkg := range pkgs { + deps = append(deps, pkg.ninjaFileDeps...) + } + + return pkgNames, deps +} + +// memoizeFullNames stores the full name of each live global variable, rule and pool since each is +// guaranteed to be used at least twice, once in the definition and once for each usage, and many +// are used much more than once. +func (c *Context) memoizeFullNames(liveGlobals *liveTracker, pkgNames map[*packageContext]string) { + for v := range liveGlobals.variables { + v.memoizeFullName(pkgNames) + } + for r := range liveGlobals.rules { + r.memoizeFullName(pkgNames) + } + for p := range liveGlobals.pools { + p.memoizeFullName(pkgNames) + } +} + +func (c *Context) checkForVariableReferenceCycles( + variables map[Variable]ninjaString, pkgNames map[*packageContext]string) { + + visited := make(map[Variable]bool) // variables that were already checked + checking := make(map[Variable]bool) // variables actively being checked + + var check func(v Variable) []Variable + + check = func(v Variable) []Variable { + visited[v] = true + checking[v] = true + defer delete(checking, v) + + value := variables[v] + for _, dep := range value.Variables() { + if checking[dep] { + // This is a cycle. + return []Variable{dep, v} + } + + if !visited[dep] { + cycle := check(dep) + if cycle != nil { + if cycle[0] == v { + // We are the "start" of the cycle, so we're responsible + // for generating the errors. The cycle list is in + // reverse order because all the 'check' calls append + // their own module to the list. + msgs := []string{"detected variable reference cycle:"} + + // Iterate backwards through the cycle list. + curName := v.fullName(pkgNames) + curValue := value.Value(pkgNames) + for i := len(cycle) - 1; i >= 0; i-- { + next := cycle[i] + nextName := next.fullName(pkgNames) + nextValue := variables[next].Value(pkgNames) + + msgs = append(msgs, fmt.Sprintf( + " %q depends on %q", curName, nextName)) + msgs = append(msgs, fmt.Sprintf( + " [%s = %s]", curName, curValue)) + + curName = nextName + curValue = nextValue + } + + // Variable reference cycles are a programming error, + // not the fault of the Blueprint file authors. + panic(strings.Join(msgs, "\n")) + } else { + // We're not the "start" of the cycle, so we just append + // our module to the list and return it. + return append(cycle, v) + } + } + } + } + + return nil + } + + for v := range variables { + if !visited[v] { + cycle := check(v) + if cycle != nil { + panic("inconceivable!") + } + } + } +} + +// AllTargets returns a map all the build target names to the rule used to build +// them. This is the same information that is output by running 'ninja -t +// targets all'. If this is called before PrepareBuildActions successfully +// completes then ErrbuildActionsNotReady is returned. +func (c *Context) AllTargets() (map[string]string, error) { + if !c.buildActionsReady { + return nil, ErrBuildActionsNotReady + } + + targets := map[string]string{} + + // Collect all the module build targets. + for _, module := range c.moduleInfo { + for _, buildDef := range module.actionDefs.buildDefs { + ruleName := buildDef.Rule.fullName(c.pkgNames) + for _, output := range append(buildDef.Outputs, buildDef.ImplicitOutputs...) { + outputValue, err := output.Eval(c.globalVariables) + if err != nil { + return nil, err + } + targets[outputValue] = ruleName + } + } + } + + // Collect all the singleton build targets. + for _, info := range c.singletonInfo { + for _, buildDef := range info.actionDefs.buildDefs { + ruleName := buildDef.Rule.fullName(c.pkgNames) + for _, output := range append(buildDef.Outputs, buildDef.ImplicitOutputs...) { + outputValue, err := output.Eval(c.globalVariables) + if err != nil { + return nil, err + } + targets[outputValue] = ruleName + } + } + } + + return targets, nil +} + +func (c *Context) OutDir() (string, error) { + if c.outDir != nil { + return c.outDir.Eval(c.globalVariables) + } else { + return "", nil + } +} + +// ModuleTypePropertyStructs returns a mapping from module type name to a list of pointers to +// property structs returned by the factory for that module type. +func (c *Context) ModuleTypePropertyStructs() map[string][]interface{} { + ret := make(map[string][]interface{}) + for moduleType, factory := range c.moduleFactories { + _, ret[moduleType] = factory() + } + + return ret +} + +func (c *Context) ModuleTypeFactories() map[string]ModuleFactory { + ret := make(map[string]ModuleFactory) + for k, v := range c.moduleFactories { + ret[k] = v + } + return ret +} + +func (c *Context) ModuleName(logicModule Module) string { + module := c.moduleInfo[logicModule] + return module.Name() +} + +func (c *Context) ModuleDir(logicModule Module) string { + return filepath.Dir(c.BlueprintFile(logicModule)) +} + +func (c *Context) ModuleSubDir(logicModule Module) string { + module := c.moduleInfo[logicModule] + return module.variant.name +} + +func (c *Context) ModuleType(logicModule Module) string { + module := c.moduleInfo[logicModule] + return module.typeName +} + +// ModuleProvider returns the value, if any, for the provider for a module. If the value for the +// provider was not set it returns the zero value of the type of the provider, which means the +// return value can always be type-asserted to the type of the provider. The return value should +// always be considered read-only. It panics if called before the appropriate mutator or +// GenerateBuildActions pass for the provider on the module. The value returned may be a deep +// copy of the value originally passed to SetProvider. +func (c *Context) ModuleProvider(logicModule Module, provider ProviderKey) interface{} { + module := c.moduleInfo[logicModule] + value, _ := c.provider(module, provider) + return value +} + +// ModuleHasProvider returns true if the provider for the given module has been set. +func (c *Context) ModuleHasProvider(logicModule Module, provider ProviderKey) bool { + module := c.moduleInfo[logicModule] + _, ok := c.provider(module, provider) + return ok +} + +func (c *Context) BlueprintFile(logicModule Module) string { + module := c.moduleInfo[logicModule] + return module.relBlueprintsFile +} + +func (c *Context) ModuleErrorf(logicModule Module, format string, + args ...interface{}) error { + + module := c.moduleInfo[logicModule] + return &BlueprintError{ + Err: fmt.Errorf(format, args...), + Pos: module.pos, + } +} + +func (c *Context) VisitAllModules(visit func(Module)) { + c.visitAllModules(visit) +} + +func (c *Context) VisitAllModulesIf(pred func(Module) bool, + visit func(Module)) { + + c.visitAllModulesIf(pred, visit) +} + +func (c *Context) VisitDirectDeps(module Module, visit func(Module)) { + topModule := c.moduleInfo[module] + + var visiting *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s", + topModule, funcName(visit), visiting)) + } + }() + + for _, dep := range topModule.directDeps { + visiting = dep.module + visit(dep.module.logicModule) + } +} + +func (c *Context) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) { + topModule := c.moduleInfo[module] + + var visiting *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s", + topModule, funcName(pred), funcName(visit), visiting)) + } + }() + + for _, dep := range topModule.directDeps { + visiting = dep.module + if pred(dep.module.logicModule) { + visit(dep.module.logicModule) + } + } +} + +func (c *Context) VisitDepsDepthFirst(module Module, visit func(Module)) { + topModule := c.moduleInfo[module] + + var visiting *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s", + topModule, funcName(visit), visiting)) + } + }() + + c.walkDeps(topModule, false, nil, func(dep depInfo, parent *moduleInfo) { + visiting = dep.module + visit(dep.module.logicModule) + }) +} + +func (c *Context) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) { + topModule := c.moduleInfo[module] + + var visiting *moduleInfo + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s", + topModule, funcName(pred), funcName(visit), visiting)) + } + }() + + c.walkDeps(topModule, false, nil, func(dep depInfo, parent *moduleInfo) { + if pred(dep.module.logicModule) { + visiting = dep.module + visit(dep.module.logicModule) + } + }) +} + +func (c *Context) PrimaryModule(module Module) Module { + return c.moduleInfo[module].group.modules.firstModule().logicModule +} + +func (c *Context) FinalModule(module Module) Module { + return c.moduleInfo[module].group.modules.lastModule().logicModule +} + +func (c *Context) VisitAllModuleVariants(module Module, + visit func(Module)) { + + c.visitAllModuleVariants(c.moduleInfo[module], visit) +} + +// Singletons returns a list of all registered Singletons. +func (c *Context) Singletons() []Singleton { + var ret []Singleton + for _, s := range c.singletonInfo { + ret = append(ret, s.singleton) + } + return ret +} + +// SingletonName returns the name that the given singleton was registered with. +func (c *Context) SingletonName(singleton Singleton) string { + for _, s := range c.singletonInfo { + if s.singleton == singleton { + return s.name + } + } + return "" +} + +// WriteBuildFile writes the Ninja manifeset text for the generated build +// actions to w. If this is called before PrepareBuildActions successfully +// completes then ErrBuildActionsNotReady is returned. +func (c *Context) WriteBuildFile(w io.StringWriter) error { + var err error + pprof.Do(c.Context, pprof.Labels("blueprint", "WriteBuildFile"), func(ctx context.Context) { + if !c.buildActionsReady { + err = ErrBuildActionsNotReady + return + } + + nw := newNinjaWriter(w) + + err = c.writeBuildFileHeader(nw) + if err != nil { + return + } + + err = c.writeNinjaRequiredVersion(nw) + if err != nil { + return + } + + err = c.writeSubninjas(nw) + if err != nil { + return + } + + // TODO: Group the globals by package. + + err = c.writeGlobalVariables(nw) + if err != nil { + return + } + + err = c.writeGlobalPools(nw) + if err != nil { + return + } + + err = c.writeBuildDir(nw) + if err != nil { + return + } + + err = c.writeGlobalRules(nw) + if err != nil { + return + } + + err = c.writeAllModuleActions(nw) + if err != nil { + return + } + + err = c.writeAllSingletonActions(nw) + if err != nil { + return + } + }) + + if err != nil { + return err + } + + return nil +} + +type pkgAssociation struct { + PkgName string + PkgPath string +} + +type pkgAssociationSorter struct { + pkgs []pkgAssociation +} + +func (s *pkgAssociationSorter) Len() int { + return len(s.pkgs) +} + +func (s *pkgAssociationSorter) Less(i, j int) bool { + iName := s.pkgs[i].PkgName + jName := s.pkgs[j].PkgName + return iName < jName +} + +func (s *pkgAssociationSorter) Swap(i, j int) { + s.pkgs[i], s.pkgs[j] = s.pkgs[j], s.pkgs[i] +} + +func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error { + headerTemplate := template.New("fileHeader") + _, err := headerTemplate.Parse(fileHeaderTemplate) + if err != nil { + // This is a programming error. + panic(err) + } + + var pkgs []pkgAssociation + maxNameLen := 0 + for pkg, name := range c.pkgNames { + pkgs = append(pkgs, pkgAssociation{ + PkgName: name, + PkgPath: pkg.pkgPath, + }) + if len(name) > maxNameLen { + maxNameLen = len(name) + } + } + + for i := range pkgs { + pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName)) + } + + sort.Sort(&pkgAssociationSorter{pkgs}) + + params := map[string]interface{}{ + "Pkgs": pkgs, + } + + buf := bytes.NewBuffer(nil) + err = headerTemplate.Execute(buf, params) + if err != nil { + return err + } + + return nw.Comment(buf.String()) +} + +func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error { + value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor, + c.requiredNinjaMicro) + + err := nw.Assign("ninja_required_version", value) + if err != nil { + return err + } + + return nw.BlankLine() +} + +func (c *Context) writeSubninjas(nw *ninjaWriter) error { + for _, subninja := range c.subninjas { + err := nw.Subninja(subninja) + if err != nil { + return err + } + } + return nw.BlankLine() +} + +func (c *Context) writeBuildDir(nw *ninjaWriter) error { + if c.outDir != nil { + err := nw.Assign("builddir", c.outDir.Value(c.pkgNames)) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + } + return nil +} + +type globalEntity interface { + fullName(pkgNames map[*packageContext]string) string +} + +type globalEntitySorter struct { + pkgNames map[*packageContext]string + entities []globalEntity +} + +func (s *globalEntitySorter) Len() int { + return len(s.entities) +} + +func (s *globalEntitySorter) Less(i, j int) bool { + iName := s.entities[i].fullName(s.pkgNames) + jName := s.entities[j].fullName(s.pkgNames) + return iName < jName +} + +func (s *globalEntitySorter) Swap(i, j int) { + s.entities[i], s.entities[j] = s.entities[j], s.entities[i] +} + +func (c *Context) writeGlobalVariables(nw *ninjaWriter) error { + visited := make(map[Variable]bool) + + var walk func(v Variable) error + walk = func(v Variable) error { + visited[v] = true + + // First visit variables on which this variable depends. + value := c.globalVariables[v] + for _, dep := range value.Variables() { + if !visited[dep] { + err := walk(dep) + if err != nil { + return err + } + } + } + + err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames)) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + + return nil + } + + globalVariables := make([]globalEntity, 0, len(c.globalVariables)) + for variable := range c.globalVariables { + globalVariables = append(globalVariables, variable) + } + + sort.Sort(&globalEntitySorter{c.pkgNames, globalVariables}) + + for _, entity := range globalVariables { + v := entity.(Variable) + if !visited[v] { + err := walk(v) + if err != nil { + return nil + } + } + } + + return nil +} + +func (c *Context) writeGlobalPools(nw *ninjaWriter) error { + globalPools := make([]globalEntity, 0, len(c.globalPools)) + for pool := range c.globalPools { + globalPools = append(globalPools, pool) + } + + sort.Sort(&globalEntitySorter{c.pkgNames, globalPools}) + + for _, entity := range globalPools { + pool := entity.(Pool) + name := pool.fullName(c.pkgNames) + def := c.globalPools[pool] + err := def.WriteTo(nw, name) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + } + + return nil +} + +func (c *Context) writeGlobalRules(nw *ninjaWriter) error { + globalRules := make([]globalEntity, 0, len(c.globalRules)) + for rule := range c.globalRules { + globalRules = append(globalRules, rule) + } + + sort.Sort(&globalEntitySorter{c.pkgNames, globalRules}) + + for _, entity := range globalRules { + rule := entity.(Rule) + name := rule.fullName(c.pkgNames) + def := c.globalRules[rule] + err := def.WriteTo(nw, name, c.pkgNames) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + } + + return nil +} + +type depSorter []depInfo + +func (s depSorter) Len() int { + return len(s) +} + +func (s depSorter) Less(i, j int) bool { + iName := s[i].module.Name() + jName := s[j].module.Name() + if iName == jName { + iName = s[i].module.variant.name + jName = s[j].module.variant.name + } + return iName < jName +} + +func (s depSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +type moduleSorter struct { + modules []*moduleInfo + nameInterface NameInterface +} + +func (s moduleSorter) Len() int { + return len(s.modules) +} + +func (s moduleSorter) Less(i, j int) bool { + iMod := s.modules[i] + jMod := s.modules[j] + iName := s.nameInterface.UniqueName(newNamespaceContext(iMod), iMod.group.name) + jName := s.nameInterface.UniqueName(newNamespaceContext(jMod), jMod.group.name) + if iName == jName { + iVariantName := s.modules[i].variant.name + jVariantName := s.modules[j].variant.name + if iVariantName == jVariantName { + panic(fmt.Sprintf("duplicate module name: %s %s: %#v and %#v\n", + iName, iVariantName, iMod.variant.variations, jMod.variant.variations)) + } else { + return iVariantName < jVariantName + } + } else { + return iName < jName + } +} + +func (s moduleSorter) Swap(i, j int) { + s.modules[i], s.modules[j] = s.modules[j], s.modules[i] +} + +func (c *Context) writeAllModuleActions(nw *ninjaWriter) error { + headerTemplate := template.New("moduleHeader") + _, err := headerTemplate.Parse(moduleHeaderTemplate) + if err != nil { + // This is a programming error. + panic(err) + } + + modules := make([]*moduleInfo, 0, len(c.moduleInfo)) + for _, module := range c.moduleInfo { + modules = append(modules, module) + } + sort.Sort(moduleSorter{modules, c.nameInterface}) + + buf := bytes.NewBuffer(nil) + + for _, module := range modules { + if len(module.actionDefs.variables)+len(module.actionDefs.rules)+len(module.actionDefs.buildDefs) == 0 { + continue + } + + buf.Reset() + + // In order to make the bootstrap build manifest independent of the + // build dir we need to output the Blueprints file locations in the + // comments as paths relative to the source directory. + relPos := module.pos + relPos.Filename = module.relBlueprintsFile + + // Get the name and location of the factory function for the module. + factoryFunc := runtime.FuncForPC(reflect.ValueOf(module.factory).Pointer()) + factoryName := factoryFunc.Name() + + infoMap := map[string]interface{}{ + "name": module.Name(), + "typeName": module.typeName, + "goFactory": factoryName, + "pos": relPos, + "variant": module.variant.name, + } + err = headerTemplate.Execute(buf, infoMap) + if err != nil { + return err + } + + err = nw.Comment(buf.String()) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + + err = c.writeLocalBuildActions(nw, &module.actionDefs) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + } + + return nil +} + +func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error { + headerTemplate := template.New("singletonHeader") + _, err := headerTemplate.Parse(singletonHeaderTemplate) + if err != nil { + // This is a programming error. + panic(err) + } + + buf := bytes.NewBuffer(nil) + + for _, info := range c.singletonInfo { + if len(info.actionDefs.variables)+len(info.actionDefs.rules)+len(info.actionDefs.buildDefs) == 0 { + continue + } + + // Get the name of the factory function for the module. + factory := info.factory + factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()) + factoryName := factoryFunc.Name() + + buf.Reset() + infoMap := map[string]interface{}{ + "name": info.name, + "goFactory": factoryName, + } + err = headerTemplate.Execute(buf, infoMap) + if err != nil { + return err + } + + err = nw.Comment(buf.String()) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + + err = c.writeLocalBuildActions(nw, &info.actionDefs) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + } + + return nil +} + +func (c *Context) BeginEvent(name string) { + c.EventHandler.Begin(name) +} + +func (c *Context) EndEvent(name string) { + c.EventHandler.End(name) +} + +func (c *Context) writeLocalBuildActions(nw *ninjaWriter, + defs *localBuildActions) error { + + // Write the local variable assignments. + for _, v := range defs.variables { + // A localVariable doesn't need the package names or config to + // determine its name or value. + name := v.fullName(nil) + value, err := v.value(nil) + if err != nil { + panic(err) + } + err = nw.Assign(name, value.Value(c.pkgNames)) + if err != nil { + return err + } + } + + if len(defs.variables) > 0 { + err := nw.BlankLine() + if err != nil { + return err + } + } + + // Write the local rules. + for _, r := range defs.rules { + // A localRule doesn't need the package names or config to determine + // its name or definition. + name := r.fullName(nil) + def, err := r.def(nil) + if err != nil { + panic(err) + } + + err = def.WriteTo(nw, name, c.pkgNames) + if err != nil { + return err + } + + err = nw.BlankLine() + if err != nil { + return err + } + } + + // Write the build definitions. + for _, buildDef := range defs.buildDefs { + err := buildDef.WriteTo(nw, c.pkgNames) + if err != nil { + return err + } + + if len(buildDef.Args) > 0 { + err = nw.BlankLine() + if err != nil { + return err + } + } + } + + return nil +} + +func beforeInModuleList(a, b *moduleInfo, list modulesOrAliases) bool { + found := false + if a == b { + return false + } + for _, l := range list { + if l.module() == a { + found = true + } else if l.module() == b { + return found + } + } + + missing := a + if found { + missing = b + } + panic(fmt.Errorf("element %v not found in list %v", missing, list)) +} + +type panicError struct { + panic interface{} + stack []byte + in string +} + +func newPanicErrorf(panic interface{}, in string, a ...interface{}) error { + buf := make([]byte, 4096) + count := runtime.Stack(buf, false) + return panicError{ + panic: panic, + in: fmt.Sprintf(in, a...), + stack: buf[:count], + } +} + +func (p panicError) Error() string { + return fmt.Sprintf("panic in %s\n%s\n%s\n", p.in, p.panic, p.stack) +} + +func (p *panicError) addIn(in string) { + p.in += " in " + in +} + +func funcName(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} + +var fileHeaderTemplate = `****************************************************************************** +*** This file is generated and should not be edited *** +****************************************************************************** +{{if .Pkgs}} +This file contains variables, rules, and pools with name prefixes indicating +they were generated by the following Go packages: +{{range .Pkgs}} + {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}} + +` + +var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +Module: {{.name}} +Variant: {{.variant}} +Type: {{.typeName}} +Factory: {{.goFactory}} +Defined: {{.pos}} +` + +var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +Singleton: {{.name}} +Factory: {{.goFactory}} +` + +// Blueprint module type that can be used to gate blueprint files beneath this directory +type PackageIncludes struct { + properties struct { + // Package will be included if all include tags in this list are set + Match_all []string + } + name *string `blueprint:"mutated"` +} + +func (pi *PackageIncludes) Name() string { + return proptools.String(pi.name) +} + +// This module type does not have any build actions +func (pi *PackageIncludes) GenerateBuildActions(ctx ModuleContext) { +} + +func newPackageIncludesFactory() (Module, []interface{}) { + module := &PackageIncludes{} + AddLoadHook(module, func(ctx LoadHookContext) { + module.name = proptools.StringPtr(ctx.ModuleDir() + "_includes") // Generate a synthetic name + }) + return module, []interface{}{&module.properties} +} + +func RegisterPackageIncludesModuleType(ctx *Context) { + ctx.RegisterModuleType("blueprint_package_includes", newPackageIncludesFactory) +} + +func (pi *PackageIncludes) MatchAll() []string { + return pi.properties.Match_all +} + +// Returns true if all requested include tags are set in the Context object +func (pi *PackageIncludes) MatchesIncludeTags(ctx *Context) bool { + if len(pi.MatchAll()) == 0 { + ctx.ModuleErrorf(pi, "Match_all must be a non-empty list") + } + for _, includeTag := range pi.MatchAll() { + if !ctx.ContainsIncludeTag(includeTag) { + return false + } + } + return true +} diff --git a/blueprint/context_test.go b/blueprint/context_test.go new file mode 100644 index 0000000..5b045bd --- /dev/null +++ b/blueprint/context_test.go @@ -0,0 +1,1157 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "reflect" + "strings" + "sync" + "testing" + "time" + + "github.com/google/blueprint/parser" +) + +type Walker interface { + Walk() bool +} + +func walkDependencyGraph(ctx *Context, topModule *moduleInfo, allowDuplicates bool) (string, string) { + var outputDown string + var outputUp string + ctx.walkDeps(topModule, allowDuplicates, + func(dep depInfo, parent *moduleInfo) bool { + outputDown += ctx.ModuleName(dep.module.logicModule) + if tag, ok := dep.tag.(walkerDepsTag); ok { + if !tag.follow { + return false + } + } + if dep.module.logicModule.(Walker).Walk() { + return true + } + + return false + }, + func(dep depInfo, parent *moduleInfo) { + outputUp += ctx.ModuleName(dep.module.logicModule) + }) + return outputDown, outputUp +} + +type depsProvider interface { + Deps() []string + IgnoreDeps() []string +} + +type fooModule struct { + SimpleName + properties struct { + Deps []string + Ignored_deps []string + Foo string + } +} + +func newFooModule() (Module, []interface{}) { + m := &fooModule{} + return m, []interface{}{&m.properties, &m.SimpleName.Properties} +} + +func (f *fooModule) GenerateBuildActions(ModuleContext) { +} + +func (f *fooModule) Deps() []string { + return f.properties.Deps +} + +func (f *fooModule) IgnoreDeps() []string { + return f.properties.Ignored_deps +} + +func (f *fooModule) Foo() string { + return f.properties.Foo +} + +func (f *fooModule) Walk() bool { + return true +} + +type barModule struct { + SimpleName + properties struct { + Deps []string + Ignored_deps []string + Bar bool + } +} + +func newBarModule() (Module, []interface{}) { + m := &barModule{} + return m, []interface{}{&m.properties, &m.SimpleName.Properties} +} + +func (b *barModule) Deps() []string { + return b.properties.Deps +} + +func (b *barModule) IgnoreDeps() []string { + return b.properties.Ignored_deps +} + +func (b *barModule) GenerateBuildActions(ModuleContext) { +} + +func (b *barModule) Bar() bool { + return b.properties.Bar +} + +func (b *barModule) Walk() bool { + return false +} + +type walkerDepsTag struct { + BaseDependencyTag + // True if the dependency should be followed, false otherwise. + follow bool +} + +func depsMutator(mctx BottomUpMutatorContext) { + if m, ok := mctx.Module().(depsProvider); ok { + mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: false}, m.IgnoreDeps()...) + mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: true}, m.Deps()...) + } +} + +func TestContextParse(t *testing.T) { + ctx := NewContext() + ctx.RegisterModuleType("foo_module", newFooModule) + ctx.RegisterModuleType("bar_module", newBarModule) + + r := bytes.NewBufferString(` + foo_module { + name: "MyFooModule", + deps: ["MyBarModule"], + } + + bar_module { + name: "MyBarModule", + } + `) + + _, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } +} + +// |===B---D - represents a non-walkable edge +// A = represents a walkable edge +// |===C===E---G +// | | A should not be visited because it's the root node. +// |===F===| B, D and E should not be walked. +func TestWalkDeps(t *testing.T) { + ctx := NewContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + foo_module { + name: "A", + deps: ["B", "C"], + } + + bar_module { + name: "B", + deps: ["D"], + } + + foo_module { + name: "C", + deps: ["E", "F"], + } + + foo_module { + name: "D", + } + + bar_module { + name: "E", + deps: ["G"], + } + + foo_module { + name: "F", + deps: ["G"], + } + + foo_module { + name: "G", + } + `), + }) + + ctx.RegisterModuleType("foo_module", newFooModule) + ctx.RegisterModuleType("bar_module", newBarModule) + ctx.RegisterBottomUpMutator("deps", depsMutator) + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule() + outputDown, outputUp := walkDependencyGraph(ctx, topModule, false) + if outputDown != "BCEFG" { + t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEFG", outputDown) + } + if outputUp != "BEGFC" { + t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BEGFC", outputUp) + } +} + +// |===B---D - represents a non-walkable edge +// A = represents a walkable edge +// |===C===E===\ A should not be visited because it's the root node. +// | | B, D should not be walked. +// |===F===G===H G should be visited multiple times +// \===/ H should only be visited once +func TestWalkDepsDuplicates(t *testing.T) { + ctx := NewContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + foo_module { + name: "A", + deps: ["B", "C"], + } + + bar_module { + name: "B", + deps: ["D"], + } + + foo_module { + name: "C", + deps: ["E", "F"], + } + + foo_module { + name: "D", + } + + foo_module { + name: "E", + deps: ["G"], + } + + foo_module { + name: "F", + deps: ["G", "G"], + } + + foo_module { + name: "G", + deps: ["H"], + } + + foo_module { + name: "H", + } + `), + }) + + ctx.RegisterModuleType("foo_module", newFooModule) + ctx.RegisterModuleType("bar_module", newBarModule) + ctx.RegisterBottomUpMutator("deps", depsMutator) + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule() + outputDown, outputUp := walkDependencyGraph(ctx, topModule, true) + if outputDown != "BCEGHFGG" { + t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEGHFGG", outputDown) + } + if outputUp != "BHGEGGFC" { + t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BHGEGGFC", outputUp) + } +} + +// - represents a non-walkable edge +// A = represents a walkable edge +// |===B-------\ A should not be visited because it's the root node. +// | | B -> D should not be walked. +// |===C===D===E B -> C -> D -> E should be walked +func TestWalkDepsDuplicates_IgnoreFirstPath(t *testing.T) { + ctx := NewContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + foo_module { + name: "A", + deps: ["B"], + } + + foo_module { + name: "B", + deps: ["C"], + ignored_deps: ["D"], + } + + foo_module { + name: "C", + deps: ["D"], + } + + foo_module { + name: "D", + deps: ["E"], + } + + foo_module { + name: "E", + } + `), + }) + + ctx.RegisterModuleType("foo_module", newFooModule) + ctx.RegisterModuleType("bar_module", newBarModule) + ctx.RegisterBottomUpMutator("deps", depsMutator) + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule() + outputDown, outputUp := walkDependencyGraph(ctx, topModule, true) + expectedDown := "BDCDE" + if outputDown != expectedDown { + t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: %s", outputDown, expectedDown) + } + expectedUp := "DEDCB" + if outputUp != expectedUp { + t.Errorf("unexpected walkDeps behaviour: %s\nup should be: %s", outputUp, expectedUp) + } +} + +func TestCreateModule(t *testing.T) { + ctx := newContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + foo_module { + name: "A", + deps: ["B", "C"], + } + `), + }) + + ctx.RegisterTopDownMutator("create", createTestMutator) + ctx.RegisterBottomUpMutator("deps", depsMutator) + + ctx.RegisterModuleType("foo_module", newFooModule) + ctx.RegisterModuleType("bar_module", newBarModule) + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + a := ctx.moduleGroupFromName("A", nil).modules.firstModule().logicModule.(*fooModule) + b := ctx.moduleGroupFromName("B", nil).modules.firstModule().logicModule.(*barModule) + c := ctx.moduleGroupFromName("C", nil).modules.firstModule().logicModule.(*barModule) + d := ctx.moduleGroupFromName("D", nil).modules.firstModule().logicModule.(*fooModule) + + checkDeps := func(m Module, expected string) { + var deps []string + ctx.VisitDirectDeps(m, func(m Module) { + deps = append(deps, ctx.ModuleName(m)) + }) + got := strings.Join(deps, ",") + if got != expected { + t.Errorf("unexpected %q dependencies, got %q expected %q", + ctx.ModuleName(m), got, expected) + } + } + + checkDeps(a, "B,C") + checkDeps(b, "D") + checkDeps(c, "D") + checkDeps(d, "") +} + +func createTestMutator(ctx TopDownMutatorContext) { + type props struct { + Name string + Deps []string + } + + ctx.CreateModule(newBarModule, &props{ + Name: "B", + Deps: []string{"D"}, + }) + + ctx.CreateModule(newBarModule, &props{ + Name: "C", + Deps: []string{"D"}, + }) + + ctx.CreateModule(newFooModule, &props{ + Name: "D", + }) +} + +func TestWalkFileOrder(t *testing.T) { + // Run the test once to see how long it normally takes + start := time.Now() + doTestWalkFileOrder(t, time.Duration(0)) + duration := time.Since(start) + + // Run the test again, but put enough of a sleep into each visitor to detect ordering + // problems if they exist + doTestWalkFileOrder(t, duration) +} + +// test that WalkBlueprintsFiles calls asyncVisitor in the right order +func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) { + // setup mock context + ctx := newContext() + mockFiles := map[string][]byte{ + "Android.bp": []byte(` + sample_module { + name: "a", + } + `), + "dir1/Android.bp": []byte(` + sample_module { + name: "b", + } + `), + "dir1/dir2/Android.bp": []byte(` + sample_module { + name: "c", + } + `), + } + ctx.MockFileSystem(mockFiles) + + // prepare to monitor the visit order + visitOrder := []string{} + visitLock := sync.Mutex{} + correctVisitOrder := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"} + + // sleep longer when processing the earlier files + chooseSleepDuration := func(fileName string) (duration time.Duration) { + duration = time.Duration(0) + for i := len(correctVisitOrder) - 1; i >= 0; i-- { + if fileName == correctVisitOrder[i] { + return duration + } + duration = duration + sleepDuration + } + panic("unrecognized file name " + fileName) + } + + visitor := func(file *parser.File) { + time.Sleep(chooseSleepDuration(file.Name)) + visitLock.Lock() + defer visitLock.Unlock() + visitOrder = append(visitOrder, file.Name) + } + keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"} + + // visit the blueprints files + ctx.WalkBlueprintsFiles(".", keys, visitor) + + // check the order + if !reflect.DeepEqual(visitOrder, correctVisitOrder) { + t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder) + } +} + +// test that WalkBlueprintsFiles reports syntax errors +func TestWalkingWithSyntaxError(t *testing.T) { + // setup mock context + ctx := newContext() + mockFiles := map[string][]byte{ + "Android.bp": []byte(` + sample_module { + name: "a" "b", + } + `), + "dir1/Android.bp": []byte(` + sample_module { + name: "b", + `), + "dir1/dir2/Android.bp": []byte(` + sample_module { + name: "c", + } + `), + } + ctx.MockFileSystem(mockFiles) + + keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"} + + // visit the blueprints files + _, errs := ctx.WalkBlueprintsFiles(".", keys, func(file *parser.File) {}) + + expectedErrs := []error{ + errors.New(`Android.bp:3:18: expected "}", found String`), + errors.New(`dir1/Android.bp:4:3: expected "}", found EOF`), + } + if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) { + t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs) + } + +} + +func TestParseFailsForModuleWithoutName(t *testing.T) { + ctx := NewContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + foo_module { + name: "A", + } + + bar_module { + deps: ["A"], + } + `), + }) + ctx.RegisterModuleType("foo_module", newFooModule) + ctx.RegisterModuleType("bar_module", newBarModule) + + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + + expectedErrs := []error{ + errors.New(`Android.bp:6:4: property 'name' is missing from a module`), + } + if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) { + t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs) + } +} + +func Test_findVariant(t *testing.T) { + module := &moduleInfo{ + variant: variant{ + name: "normal_local", + variations: variationMap{ + "normal": "normal", + "local": "local", + }, + dependencyVariations: variationMap{ + "normal": "normal", + }, + }, + } + + type alias struct { + variant variant + target int + } + + makeDependencyGroup := func(in ...interface{}) *moduleGroup { + group := &moduleGroup{ + name: "dep", + } + for _, x := range in { + switch m := x.(type) { + case *moduleInfo: + m.group = group + group.modules = append(group.modules, m) + case alias: + // aliases may need to target modules that haven't been processed + // yet, put an empty alias in for now. + group.modules = append(group.modules, nil) + default: + t.Fatalf("unexpected type %T", x) + } + } + + for i, x := range in { + switch m := x.(type) { + case *moduleInfo: + // already added in the first pass + case alias: + group.modules[i] = &moduleAlias{ + variant: m.variant, + target: group.modules[m.target].moduleOrAliasTarget(), + } + default: + t.Fatalf("unexpected type %T", x) + } + } + + return group + } + + tests := []struct { + name string + possibleDeps *moduleGroup + variations []Variation + far bool + reverse bool + want string + }{ + { + name: "AddVariationDependencies(nil)", + // A dependency that matches the non-local variations of the module + possibleDeps: makeDependencyGroup( + &moduleInfo{ + variant: variant{ + name: "normal", + variations: variationMap{ + "normal": "normal", + }, + }, + }, + ), + variations: nil, + far: false, + reverse: false, + want: "normal", + }, + { + name: "AddVariationDependencies(nil) to alias", + // A dependency with an alias that matches the non-local variations of the module + possibleDeps: makeDependencyGroup( + alias{ + variant: variant{ + name: "normal", + variations: variationMap{ + "normal": "normal", + }, + }, + target: 1, + }, + &moduleInfo{ + variant: variant{ + name: "normal_a", + variations: variationMap{ + "normal": "normal", + "a": "a", + }, + }, + }, + ), + variations: nil, + far: false, + reverse: false, + want: "normal_a", + }, + { + name: "AddVariationDependencies(a)", + // A dependency with local variations + possibleDeps: makeDependencyGroup( + &moduleInfo{ + variant: variant{ + name: "normal_a", + variations: variationMap{ + "normal": "normal", + "a": "a", + }, + }, + }, + ), + variations: []Variation{{"a", "a"}}, + far: false, + reverse: false, + want: "normal_a", + }, + { + name: "AddFarVariationDependencies(far)", + // A dependency with far variations + possibleDeps: makeDependencyGroup( + &moduleInfo{ + variant: variant{ + name: "", + variations: nil, + }, + }, + &moduleInfo{ + variant: variant{ + name: "far", + variations: variationMap{ + "far": "far", + }, + }, + }, + ), + variations: []Variation{{"far", "far"}}, + far: true, + reverse: false, + want: "far", + }, + { + name: "AddFarVariationDependencies(far) to alias", + // A dependency with far variations and aliases + possibleDeps: makeDependencyGroup( + alias{ + variant: variant{ + name: "far", + variations: variationMap{ + "far": "far", + }, + }, + target: 2, + }, + &moduleInfo{ + variant: variant{ + name: "far_a", + variations: variationMap{ + "far": "far", + "a": "a", + }, + }, + }, + &moduleInfo{ + variant: variant{ + name: "far_b", + variations: variationMap{ + "far": "far", + "b": "b", + }, + }, + }, + ), + variations: []Variation{{"far", "far"}}, + far: true, + reverse: false, + want: "far_b", + }, + { + name: "AddFarVariationDependencies(far, b) to missing", + // A dependency with far variations and aliases + possibleDeps: makeDependencyGroup( + alias{ + variant: variant{ + name: "far", + variations: variationMap{ + "far": "far", + }, + }, + target: 1, + }, + &moduleInfo{ + variant: variant{ + name: "far_a", + variations: variationMap{ + "far": "far", + "a": "a", + }, + }, + }, + ), + variations: []Variation{{"far", "far"}, {"a", "b"}}, + far: true, + reverse: false, + want: "nil", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := findVariant(module, tt.possibleDeps, tt.variations, tt.far, tt.reverse) + if g, w := got == nil, tt.want == "nil"; g != w { + t.Fatalf("findVariant() got = %v, want %v", got, tt.want) + } + if got != nil { + if g, w := got.String(), fmt.Sprintf("module %q variant %q", "dep", tt.want); g != w { + t.Errorf("findVariant() got = %v, want %v", g, w) + } + } + }) + } +} + +func Test_parallelVisit(t *testing.T) { + addDep := func(from, to *moduleInfo) { + from.directDeps = append(from.directDeps, depInfo{to, nil}) + from.forwardDeps = append(from.forwardDeps, to) + to.reverseDeps = append(to.reverseDeps, from) + } + + create := func(name string) *moduleInfo { + m := &moduleInfo{ + group: &moduleGroup{ + name: name, + }, + } + m.group.modules = modulesOrAliases{m} + return m + } + moduleA := create("A") + moduleB := create("B") + moduleC := create("C") + moduleD := create("D") + moduleE := create("E") + moduleF := create("F") + moduleG := create("G") + + // A depends on B, B depends on C. Nothing depends on D through G, and they don't depend on + // anything. + addDep(moduleA, moduleB) + addDep(moduleB, moduleC) + + t.Run("no modules", func(t *testing.T) { + errs := parallelVisit(nil, bottomUpVisitorImpl{}, 1, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + panic("unexpected call to visitor") + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + }) + t.Run("bottom up", func(t *testing.T) { + order := "" + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 1, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + order += module.group.name + return false + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + if g, w := order, "CBA"; g != w { + t.Errorf("expected order %q, got %q", w, g) + } + }) + t.Run("pause", func(t *testing.T) { + order := "" + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}, bottomUpVisitorImpl{}, 1, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + if module == moduleC { + // Pause module C on module D + unpause := make(chan struct{}) + pause <- pauseSpec{moduleC, moduleD, unpause} + <-unpause + } + order += module.group.name + return false + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + if g, w := order, "DCBA"; g != w { + t.Errorf("expected order %q, got %q", w, g) + } + }) + t.Run("cancel", func(t *testing.T) { + order := "" + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 1, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + order += module.group.name + // Cancel in module B + return module == moduleB + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + if g, w := order, "CB"; g != w { + t.Errorf("expected order %q, got %q", w, g) + } + }) + t.Run("pause and cancel", func(t *testing.T) { + order := "" + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}, bottomUpVisitorImpl{}, 1, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + if module == moduleC { + // Pause module C on module D + unpause := make(chan struct{}) + pause <- pauseSpec{moduleC, moduleD, unpause} + <-unpause + } + order += module.group.name + // Cancel in module D + return module == moduleD + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + if g, w := order, "D"; g != w { + t.Errorf("expected order %q, got %q", w, g) + } + }) + t.Run("parallel", func(t *testing.T) { + order := "" + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 3, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + order += module.group.name + return false + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + if g, w := order, "CBA"; g != w { + t.Errorf("expected order %q, got %q", w, g) + } + }) + t.Run("pause existing", func(t *testing.T) { + order := "" + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 3, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + if module == moduleA { + // Pause module A on module B (an existing dependency) + unpause := make(chan struct{}) + pause <- pauseSpec{moduleA, moduleB, unpause} + <-unpause + } + order += module.group.name + return false + }) + if errs != nil { + t.Errorf("expected no errors, got %q", errs) + } + if g, w := order, "CBA"; g != w { + t.Errorf("expected order %q, got %q", w, g) + } + }) + t.Run("cycle", func(t *testing.T) { + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 3, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + if module == moduleC { + // Pause module C on module A (a dependency cycle) + unpause := make(chan struct{}) + pause <- pauseSpec{moduleC, moduleA, unpause} + <-unpause + } + return false + }) + want := []string{ + `encountered dependency cycle`, + `module "C" depends on module "A"`, + `module "A" depends on module "B"`, + `module "B" depends on module "C"`, + } + for i := range want { + if len(errs) <= i { + t.Errorf("missing error %s", want[i]) + } else if !strings.Contains(errs[i].Error(), want[i]) { + t.Errorf("expected error %s, got %s", want[i], errs[i]) + } + } + if len(errs) > len(want) { + for _, err := range errs[len(want):] { + t.Errorf("unexpected error %s", err.Error()) + } + } + }) + t.Run("pause cycle", func(t *testing.T) { + errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}, bottomUpVisitorImpl{}, 3, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + if module == moduleC { + // Pause module C on module D + unpause := make(chan struct{}) + pause <- pauseSpec{moduleC, moduleD, unpause} + <-unpause + } + if module == moduleD { + // Pause module D on module C (a pause cycle) + unpause := make(chan struct{}) + pause <- pauseSpec{moduleD, moduleC, unpause} + <-unpause + } + return false + }) + want := []string{ + `encountered dependency cycle`, + `module "D" depends on module "C"`, + `module "C" depends on module "D"`, + } + for i := range want { + if len(errs) <= i { + t.Errorf("missing error %s", want[i]) + } else if !strings.Contains(errs[i].Error(), want[i]) { + t.Errorf("expected error %s, got %s", want[i], errs[i]) + } + } + if len(errs) > len(want) { + for _, err := range errs[len(want):] { + t.Errorf("unexpected error %s", err.Error()) + } + } + }) + t.Run("pause cycle with deps", func(t *testing.T) { + pauseDeps := map[*moduleInfo]*moduleInfo{ + // F and G form a pause cycle + moduleF: moduleG, + moduleG: moduleF, + // D depends on E which depends on the pause cycle, making E the first alphabetical + // entry in pauseMap, which is not part of the cycle. + moduleD: moduleE, + moduleE: moduleF, + } + errs := parallelVisit([]*moduleInfo{moduleD, moduleE, moduleF, moduleG}, bottomUpVisitorImpl{}, 4, + func(module *moduleInfo, pause chan<- pauseSpec) bool { + if dep, ok := pauseDeps[module]; ok { + unpause := make(chan struct{}) + pause <- pauseSpec{module, dep, unpause} + <-unpause + } + return false + }) + want := []string{ + `encountered dependency cycle`, + `module "G" depends on module "F"`, + `module "F" depends on module "G"`, + } + for i := range want { + if len(errs) <= i { + t.Errorf("missing error %s", want[i]) + } else if !strings.Contains(errs[i].Error(), want[i]) { + t.Errorf("expected error %s, got %s", want[i], errs[i]) + } + } + if len(errs) > len(want) { + for _, err := range errs[len(want):] { + t.Errorf("unexpected error %s", err.Error()) + } + } + }) +} + +func TestPackageIncludes(t *testing.T) { + dir1_foo_bp := ` + blueprint_package_includes { + match_all: ["use_dir1"], + } + foo_module { + name: "foo", + } + ` + dir2_foo_bp := ` + blueprint_package_includes { + match_all: ["use_dir2"], + } + foo_module { + name: "foo", + } + ` + mockFs := map[string][]byte{ + "dir1/Android.bp": []byte(dir1_foo_bp), + "dir2/Android.bp": []byte(dir2_foo_bp), + } + testCases := []struct{ + desc string + includeTags []string + expectedDir string + expectedErr string + }{ + { + desc: "use_dir1 is set, use dir1 foo", + includeTags: []string{"use_dir1"}, + expectedDir: "dir1", + }, + { + desc: "use_dir2 is set, use dir2 foo", + includeTags: []string{"use_dir2"}, + expectedDir: "dir2", + }, + { + desc: "duplicate module error if both use_dir1 and use_dir2 are set", + includeTags: []string{"use_dir1", "use_dir2"}, + expectedDir: "", + expectedErr: `module "foo" already defined`, + }, + } + for _, tc := range testCases { + ctx := NewContext() + // Register mock FS + ctx.MockFileSystem(mockFs) + // Register module types + ctx.RegisterModuleType("foo_module", newFooModule) + RegisterPackageIncludesModuleType(ctx) + // Add include tags for test case + ctx.AddIncludeTags(tc.includeTags...) + // Run test + _, actualErrs := ctx.ParseFileList(".", []string{"dir1/Android.bp", "dir2/Android.bp"}, nil) + // Evaluate + if !strings.Contains(fmt.Sprintf("%s", actualErrs), fmt.Sprintf("%s", tc.expectedErr)) { + t.Errorf("Expected errors: %s, got errors: %s\n", tc.expectedErr, actualErrs) + } + if tc.expectedErr != "" { + continue // expectedDir check not necessary + } + actualBpFile := ctx.moduleGroupFromName("foo", nil).modules.firstModule().relBlueprintsFile + if tc.expectedDir != filepath.Dir(actualBpFile) { + t.Errorf("Expected foo from %s, got %s\n", tc.expectedDir, filepath.Dir(actualBpFile)) + } + } + +} diff --git a/blueprint/deptools/depfile.go b/blueprint/deptools/depfile.go new file mode 100644 index 0000000..bfcf2ce --- /dev/null +++ b/blueprint/deptools/depfile.go @@ -0,0 +1,55 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deptools + +import ( + "fmt" + "os" + "strings" +) + +var ( + pathEscaper = strings.NewReplacer( + `\`, `\\`, + ` `, `\ `, + `#`, `\#`, + `*`, `\*`, + `[`, `\[`, + `|`, `\|`) +) + +// WriteDepFile creates a new gcc-style depfile and populates it with content +// indicating that target depends on deps. +func WriteDepFile(filename, target string, deps []string) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + var escapedDeps []string + + for _, dep := range deps { + escapedDeps = append(escapedDeps, pathEscaper.Replace(dep)) + } + + _, err = fmt.Fprintf(f, "%s: \\\n %s\n", target, + strings.Join(escapedDeps, " \\\n ")) + if err != nil { + return err + } + + return nil +} diff --git a/blueprint/doc.go b/blueprint/doc.go new file mode 100644 index 0000000..4a9dfd7 --- /dev/null +++ b/blueprint/doc.go @@ -0,0 +1,68 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Blueprint is a meta-build system that reads in Blueprints files that describe +// modules that need to be built, and produces a Ninja +// (https://ninja-build.org/) manifest describing the commands that need +// to be run and their dependencies. Where most build systems use built-in +// rules or a domain-specific language to describe the logic how modules are +// converted to build rules, Blueprint delegates this to per-project build logic +// written in Go. For large, heterogenous projects this allows the inherent +// complexity of the build logic to be maintained in a high-level language, +// while still allowing simple changes to individual modules by modifying easy +// to understand Blueprints files. +// +// Blueprint uses a bootstrapping process to allow the code for Blueprint, +// the code for the build logic, and the code for the project being compiled +// to all live in the project. Dependencies between the layers are fully +// tracked - a change to the logic code will cause the logic to be recompiled, +// regenerate the project build manifest, and run modified project rules. A +// change to Blueprint itself will cause Blueprint to rebuild, and then rebuild +// the logic, etc. +// +// A Blueprints file is a list of modules in a pseudo-python data format, where +// the module type looks like a function call, and the properties of the module +// look like optional arguments. For example, a simple module might look like: +// +// cc_library { +// name: "cmd", +// srcs: [ +// "main.c", +// ], +// deps: [ +// "libc", +// ], +// } +// +// subdirs = ["subdir1", "subdir2"] +// +// The modules from the top level Blueprints file and recursively through any +// subdirectories listed by the "subdirs" variable are read by Blueprint, and +// their properties are stored into property structs by module type. Once +// all modules are read, Blueprint calls any registered Mutators, in +// registration order. Mutators can visit each module top-down or bottom-up, +// and modify them as necessary. Common modifications include setting +// properties on modules to propagate information down from dependers to +// dependees (for example, telling a module what kinds of parents depend on it), +// or splitting a module into multiple variants (for example, one per +// architecture being compiled). After all Mutators have run, each module is +// asked to generate build rules based on property values, and then singletons +// can generate any build rules from the output of all modules. +// +// The per-project build logic defines a top level command, referred to in the +// documentation as the "primary builder". This command is responsible for +// registering the module types needed for the project, as well as any +// singletons or mutators, and then calling into Blueprint with the path of the +// root Blueprint file. +package blueprint diff --git a/blueprint/glob.go b/blueprint/glob.go new file mode 100644 index 0000000..91ae723 --- /dev/null +++ b/blueprint/glob.go @@ -0,0 +1,116 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "sort" + "strings" + + "github.com/google/blueprint/pathtools" +) + +func verifyGlob(key globKey, pattern string, excludes []string, g pathtools.GlobResult) { + if pattern != g.Pattern { + panic(fmt.Errorf("Mismatched patterns %q and %q for glob key %q", pattern, g.Pattern, key)) + } + if len(excludes) != len(g.Excludes) { + panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key)) + } + + for i := range excludes { + if g.Excludes[i] != excludes[i] { + panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key)) + } + } +} + +func (c *Context) glob(pattern string, excludes []string) ([]string, error) { + // Sort excludes so that two globs with the same excludes in a different order reuse the same + // key. Make a copy first to avoid modifying the caller's version. + excludes = append([]string(nil), excludes...) + sort.Strings(excludes) + + key := globToKey(pattern, excludes) + + // Try to get existing glob from the stored results + c.globLock.Lock() + g, exists := c.globs[key] + c.globLock.Unlock() + + if exists { + // Glob has already been done, double check it is identical + verifyGlob(key, pattern, excludes, g) + // Return a copy so that modifications don't affect the cached value. + return append([]string(nil), g.Matches...), nil + } + + // Get a globbed file list + result, err := c.fs.Glob(pattern, excludes, pathtools.FollowSymlinks) + if err != nil { + return nil, err + } + + // Store the results + c.globLock.Lock() + if g, exists = c.globs[key]; !exists { + c.globs[key] = result + } + c.globLock.Unlock() + + if exists { + // Getting the list raced with another goroutine, throw away the results and use theirs + verifyGlob(key, pattern, excludes, g) + // Return a copy so that modifications don't affect the cached value. + return append([]string(nil), g.Matches...), nil + } + + // Return a copy so that modifications don't affect the cached value. + return append([]string(nil), result.Matches...), nil +} + +func (c *Context) Globs() pathtools.MultipleGlobResults { + keys := make([]globKey, 0, len(c.globs)) + for k := range c.globs { + keys = append(keys, k) + } + + sort.Slice(keys, func(i, j int) bool { + if keys[i].pattern != keys[j].pattern { + return keys[i].pattern < keys[j].pattern + } + return keys[i].excludes < keys[j].excludes + }) + + globs := make(pathtools.MultipleGlobResults, len(keys)) + for i, key := range keys { + globs[i] = c.globs[key] + } + + return globs +} + +// globKey combines a pattern and a list of excludes into a hashable struct to be used as a key in +// a map. +type globKey struct { + pattern string + excludes string +} + +// globToKey converts a pattern and an excludes list into a globKey struct that is hashable and +// usable as a key in a map. +func globToKey(pattern string, excludes []string) globKey { + return globKey{pattern, strings.Join(excludes, "|")} +} diff --git a/blueprint/glob_test.go b/blueprint/glob_test.go new file mode 100644 index 0000000..15fd395 --- /dev/null +++ b/blueprint/glob_test.go @@ -0,0 +1,55 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import "testing" + +func TestGlobCache(t *testing.T) { + ctx := NewContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": nil, + "a/a": nil, + "a/b": nil, + }) + + // Test a simple glob with no excludes + matches, err := ctx.glob("a/*", nil) + if err != nil { + t.Error("unexpected error", err) + } + if len(matches) != 2 || matches[0] != "a/a" || matches[1] != "a/b" { + t.Error(`expected ["a/a", "a/b"], got`, matches) + } + + // Test the same glob with an empty excludes array to make sure + // excludes=nil does not conflict with excludes=[]string{} in the + // cache. + matches, err = ctx.glob("a/*", []string{}) + if err != nil { + t.Error("unexpected error", err) + } + if len(matches) != 2 || matches[0] != "a/a" || matches[1] != "a/b" { + t.Error(`expected ["a/a", "a/b"], got`, matches) + } + + // Test the same glob with a different excludes array + matches, err = ctx.glob("a/*", []string{"a/b"}) + if err != nil { + t.Error("unexpected error", err) + } + if len(matches) != 1 || matches[0] != "a/a" { + t.Error(`expected ["a/a"], got`, matches) + } +} diff --git a/blueprint/go.mod b/blueprint/go.mod new file mode 100644 index 0000000..fe96d45 --- /dev/null +++ b/blueprint/go.mod @@ -0,0 +1,3 @@ +module github.com/google/blueprint + +go 1.13 diff --git a/blueprint/gotestmain/dummy.go b/blueprint/gotestmain/dummy.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/blueprint/gotestmain/dummy.go @@ -0,0 +1 @@ +package main diff --git a/blueprint/gotestmain/gotestmain.go b/blueprint/gotestmain/gotestmain.go new file mode 100644 index 0000000..ea381ca --- /dev/null +++ b/blueprint/gotestmain/gotestmain.go @@ -0,0 +1,234 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/doc" + "go/parser" + "go/token" + "io/ioutil" + "os" + "reflect" + "sort" + "strings" + "testing" + "text/template" +) + +var ( + output = flag.String("o", "", "output filename") + pkg = flag.String("pkg", "", "test package") + exitCode = 0 +) + +type data struct { + Package string + Tests []string + Examples []*doc.Example + HasMain bool + MainStartTakesFuzzers bool +} + +func findTests(srcs []string) (tests []string, examples []*doc.Example, hasMain bool) { + for _, src := range srcs { + f, err := parser.ParseFile(token.NewFileSet(), src, nil, parser.ParseComments) + if err != nil { + panic(err) + } + for _, obj := range f.Scope.Objects { + if obj.Kind != ast.Fun || !strings.HasPrefix(obj.Name, "Test") { + continue + } + if obj.Name == "TestMain" { + hasMain = true + } else { + tests = append(tests, obj.Name) + } + } + + examples = append(examples, doc.Examples(f)...) + } + sort.Strings(tests) + return +} + +// Returns true for go1.18+, where testing.MainStart takes an extra slice of fuzzers. +func mainStartTakesFuzzers() bool { + return reflect.TypeOf(testing.MainStart).NumIn() > 4 +} + +func main() { + flag.Parse() + + if flag.NArg() == 0 { + fmt.Fprintln(os.Stderr, "error: must pass at least one input") + exitCode = 1 + return + } + + buf := &bytes.Buffer{} + + tests, examples, hasMain := findTests(flag.Args()) + + d := data{ + Package: *pkg, + Tests: tests, + Examples: examples, + HasMain: hasMain, + MainStartTakesFuzzers: mainStartTakesFuzzers(), + } + + err := testMainTmpl.Execute(buf, d) + if err != nil { + panic(err) + } + + err = ioutil.WriteFile(*output, buf.Bytes(), 0666) + if err != nil { + panic(err) + } +} + +var testMainTmpl = template.Must(template.New("testMain").Parse(` +package main + +import ( + "io" +{{if not .HasMain}} + "os" +{{end}} + "reflect" + "regexp" + "testing" + "time" + + pkg "{{.Package}}" +) + +var t = []testing.InternalTest{ +{{range .Tests}} + {"{{.}}", pkg.{{.}}}, +{{end}} +} + +var e = []testing.InternalExample{ +{{range .Examples}} + {{if or .Output .EmptyOutput}} + {"{{.Name}}", pkg.Example{{.Name}}, {{.Output | printf "%q" }}, {{.Unordered}}}, + {{end}} +{{end}} +} + +var matchPat string +var matchRe *regexp.Regexp + +type matchString struct{} + +func MatchString(pat, str string) (result bool, err error) { + if matchRe == nil || matchPat != pat { + matchPat = pat + matchRe, err = regexp.Compile(matchPat) + if err != nil { + return + } + } + return matchRe.MatchString(str), nil +} + +func (matchString) MatchString(pat, str string) (bool, error) { + return MatchString(pat, str) +} + +func (matchString) StartCPUProfile(w io.Writer) error { + panic("shouldn't get here") +} + +func (matchString) StopCPUProfile() { +} + +func (matchString) WriteHeapProfile(w io.Writer) error { + panic("shouldn't get here") +} + +func (matchString) WriteProfileTo(string, io.Writer, int) error { + panic("shouldn't get here") +} + +func (matchString) ImportPath() string { + return "{{.Package}}" +} + +func (matchString) StartTestLog(io.Writer) { + panic("shouldn't get here") +} + +func (matchString) StopTestLog() error { + panic("shouldn't get here") +} + +func (matchString) SetPanicOnExit0(bool) { + panic("shouldn't get here") +} + +func (matchString) CoordinateFuzzing(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error { + panic("shouldn't get here") +} + +func (matchString) RunFuzzWorker(func(corpusEntry) error) error { + panic("shouldn't get here") +} + +func (matchString) ReadCorpus(string, []reflect.Type) ([]corpusEntry, error) { + panic("shouldn't get here") +} + +func (matchString) CheckCorpus([]interface{}, []reflect.Type) error { + panic("shouldn't get here") +} + +func (matchString) ResetCoverage() { + panic("shouldn't get here") +} + +func (matchString) SnapshotCoverage() { + panic("shouldn't get here") +} + +type corpusEntry = struct { + Parent string + Path string + Data []byte + Values []interface{} + Generation int + IsSeed bool +} + +func main() { +{{if .MainStartTakesFuzzers }} + m := testing.MainStart(matchString{}, t, nil, nil, e) +{{else}} + m := testing.MainStart(matchString{}, t, nil, e) +{{end}} +{{if .HasMain}} + pkg.TestMain(m) +{{else}} + os.Exit(m.Run()) +{{end}} +} +`)) diff --git a/blueprint/gotestmain/testmain_test.go b/blueprint/gotestmain/testmain_test.go new file mode 100644 index 0000000..e011eeb --- /dev/null +++ b/blueprint/gotestmain/testmain_test.go @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "os" + "testing" +) + +var a int + +func TestMain(m *testing.M) { + a = 1 + os.Exit(m.Run()) +} + +func TestTestMain(t *testing.T) { + if a != 1 { + t.Errorf("TestMain didn't run") + } +} diff --git a/blueprint/gotestrunner/gotestrunner.go b/blueprint/gotestrunner/gotestrunner.go new file mode 100644 index 0000000..5318747 --- /dev/null +++ b/blueprint/gotestrunner/gotestrunner.go @@ -0,0 +1,112 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "syscall" +) + +var ( + chdir = flag.String("p", "", "Change to a path before executing test") + touch = flag.String("f", "", "Write a file on success") +) + +// This will copy the stdout from the test process to our stdout +// unless it only contains "PASS\n". +func handleStdout(stdout io.Reader) { + reader := bufio.NewReader(stdout) + + // This is intentionally 6 instead of 5 to check for EOF + buf, _ := reader.Peek(6) + if bytes.Equal(buf, []byte("PASS\n")) { + return + } + + io.Copy(os.Stdout, reader) +} + +func main() { + flag.Parse() + + if flag.NArg() == 0 { + fmt.Fprintln(os.Stderr, "error: must pass at least one test executable") + os.Exit(1) + } + + test, err := filepath.Abs(flag.Arg(0)) + if err != nil { + fmt.Fprintln(os.Stderr, "error: Failed to locate test binary:", err) + } + + cmd := exec.Command(test, flag.Args()[1:]...) + if *chdir != "" { + cmd.Dir = *chdir + + // GOROOT is commonly a relative path in Android, make it + // absolute if we're changing directories. + if absRoot, err := filepath.Abs(runtime.GOROOT()); err == nil { + os.Setenv("GOROOT", absRoot) + } else { + fmt.Fprintln(os.Stderr, "error: Failed to locate GOROOT:", err) + } + } + + cmd.Stderr = os.Stderr + stdout, err := cmd.StdoutPipe() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + err = cmd.Start() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + handleStdout(stdout) + + if err = cmd.Wait(); err != nil { + if e, ok := err.(*exec.ExitError); ok { + if status, ok := e.Sys().(syscall.WaitStatus); ok && status.Exited() { + os.Exit(status.ExitStatus()) + } else if status.Signaled() { + fmt.Fprintf(os.Stderr, "test got signal %s\n", status.Signal()) + os.Exit(1) + } + } + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if *touch != "" { + err = ioutil.WriteFile(*touch, []byte{}, 0666) + if err != nil { + panic(err) + } + } + + os.Exit(0) +} diff --git a/blueprint/live_tracker.go b/blueprint/live_tracker.go new file mode 100644 index 0000000..1d48e58 --- /dev/null +++ b/blueprint/live_tracker.go @@ -0,0 +1,218 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import "sync" + +// A liveTracker tracks the values of live variables, rules, and pools. An +// entity is made "live" when it is referenced directly or indirectly by a build +// definition. When an entity is made live its value is computed based on the +// configuration. +type liveTracker struct { + sync.Mutex + config interface{} // Used to evaluate variable, rule, and pool values. + + variables map[Variable]ninjaString + pools map[Pool]*poolDef + rules map[Rule]*ruleDef +} + +func newLiveTracker(config interface{}) *liveTracker { + return &liveTracker{ + config: config, + variables: make(map[Variable]ninjaString), + pools: make(map[Pool]*poolDef), + rules: make(map[Rule]*ruleDef), + } +} + +func (l *liveTracker) AddBuildDefDeps(def *buildDef) error { + l.Lock() + defer l.Unlock() + + ruleDef, err := l.addRule(def.Rule) + if err != nil { + return err + } + def.RuleDef = ruleDef + + err = l.addNinjaStringListDeps(def.Outputs) + if err != nil { + return err + } + + err = l.addNinjaStringListDeps(def.Inputs) + if err != nil { + return err + } + + err = l.addNinjaStringListDeps(def.Implicits) + if err != nil { + return err + } + + err = l.addNinjaStringListDeps(def.OrderOnly) + if err != nil { + return err + } + + err = l.addNinjaStringListDeps(def.Validations) + if err != nil { + return err + } + + for _, value := range def.Variables { + err = l.addNinjaStringDeps(value) + if err != nil { + return err + } + } + + for _, value := range def.Args { + err = l.addNinjaStringDeps(value) + if err != nil { + return err + } + } + + return nil +} + +func (l *liveTracker) addRule(r Rule) (def *ruleDef, err error) { + def, ok := l.rules[r] + if !ok { + def, err = r.def(l.config) + if err == errRuleIsBuiltin { + // No need to do anything for built-in rules. + return nil, nil + } + if err != nil { + return nil, err + } + + if def.Pool != nil { + err = l.addPool(def.Pool) + if err != nil { + return nil, err + } + } + + err = l.addNinjaStringListDeps(def.CommandDeps) + if err != nil { + return nil, err + } + + err = l.addNinjaStringListDeps(def.CommandOrderOnly) + if err != nil { + return nil, err + } + + for _, value := range def.Variables { + err = l.addNinjaStringDeps(value) + if err != nil { + return nil, err + } + } + + l.rules[r] = def + } + + return +} + +func (l *liveTracker) addPool(p Pool) error { + _, ok := l.pools[p] + if !ok { + def, err := p.def(l.config) + if err == errPoolIsBuiltin { + // No need to do anything for built-in rules. + return nil + } + if err != nil { + return err + } + + l.pools[p] = def + } + + return nil +} + +func (l *liveTracker) addVariable(v Variable) error { + _, ok := l.variables[v] + if !ok { + value, err := v.value(l.config) + if err == errVariableIsArg { + // This variable is a placeholder for an argument that can be passed + // to a rule. It has no value and thus doesn't reference any other + // variables. + return nil + } + if err != nil { + return err + } + + l.variables[v] = value + + err = l.addNinjaStringDeps(value) + if err != nil { + return err + } + } + + return nil +} + +func (l *liveTracker) addNinjaStringListDeps(list []ninjaString) error { + for _, str := range list { + err := l.addNinjaStringDeps(str) + if err != nil { + return err + } + } + return nil +} + +func (l *liveTracker) addNinjaStringDeps(str ninjaString) error { + for _, v := range str.Variables() { + err := l.addVariable(v) + if err != nil { + return err + } + } + return nil +} + +func (l *liveTracker) RemoveVariableIfLive(v Variable) bool { + l.Lock() + defer l.Unlock() + + _, isLive := l.variables[v] + if isLive { + delete(l.variables, v) + } + return isLive +} + +func (l *liveTracker) RemoveRuleIfLive(r Rule) bool { + l.Lock() + defer l.Unlock() + + _, isLive := l.rules[r] + if isLive { + delete(l.rules, r) + } + return isLive +} diff --git a/blueprint/loadplugins/loadplugins.go b/blueprint/loadplugins/loadplugins.go new file mode 100644 index 0000000..3c7e1e3 --- /dev/null +++ b/blueprint/loadplugins/loadplugins.go @@ -0,0 +1,67 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "text/template" +) + +var ( + output = flag.String("o", "", "output filename") + pkg = flag.String("p", "main", "package name") +) + +func main() { + flag.Parse() + + if flag.NArg() == 0 { + fmt.Fprintln(os.Stderr, "error: must pass at least one input") + os.Exit(1) + } + + buf := &bytes.Buffer{} + + err := pluginTmpl.Execute(buf, struct { + Package string + Plugins []string + }{ + filepath.Base(*pkg), + flag.Args(), + }) + if err != nil { + panic(err) + } + + err = ioutil.WriteFile(*output, buf.Bytes(), 0666) + if err != nil { + panic(err) + } +} + +var pluginTmpl = template.Must(template.New("pluginloader").Parse(` +package {{.Package}} + +import ( +{{range .Plugins}} + _ "{{.}}" +{{end}} +) +`)) diff --git a/blueprint/mangle.go b/blueprint/mangle.go new file mode 100644 index 0000000..fe3fe65 --- /dev/null +++ b/blueprint/mangle.go @@ -0,0 +1,27 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +func packageNamespacePrefix(packageName string) string { + return "g." + packageName + "." +} + +func moduleNamespacePrefix(moduleName string) string { + return "m." + moduleName + "." +} + +func singletonNamespacePrefix(singletonName string) string { + return "s." + singletonName + "." +} diff --git a/blueprint/metrics/Android.bp b/blueprint/metrics/Android.bp new file mode 100644 index 0000000..3668668 --- /dev/null +++ b/blueprint/metrics/Android.bp @@ -0,0 +1,27 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["build_blueprint_license"], +} + +bootstrap_go_package { + name: "blueprint-metrics", + pkgPath: "github.com/google/blueprint/metrics", + srcs: [ + "event_handler.go", + ], +} diff --git a/blueprint/metrics/event_handler.go b/blueprint/metrics/event_handler.go new file mode 100644 index 0000000..c19d039 --- /dev/null +++ b/blueprint/metrics/event_handler.go @@ -0,0 +1,104 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "fmt" + "strings" + "time" +) + +// EventHandler tracks nested events and their start/stop times in a single +// thread. +type EventHandler struct { + completedEvents []Event + + // These fields handle event scoping. When starting a new event, a new entry + // is pushed onto these fields. When ending an event, these fields are popped. + scopeIds []string + scopeStartTimes []time.Time +} + +// _now wraps the time.Now() function. _now is declared for unit testing purpose. +var _now = func() time.Time { + return time.Now() +} + +// Event holds the performance metrics data of a single build event. +type Event struct { + // A unique human-readable identifier / "name" for the build event. Event + // names use period-delimited scoping. For example, if an event alpha starts, + // then an event bravo starts, then an event charlie starts and ends, the + // unique identifier for charlie will be 'alpha.bravo.charlie'. + Id string + + Start time.Time + end time.Time +} + +// RuntimeNanoseconds returns the number of nanoseconds between the start +// and end times of the event. +func (e Event) RuntimeNanoseconds() uint64 { + return uint64(e.end.Sub(e.Start).Nanoseconds()) +} + +// Begin logs the start of an event. This must be followed by a corresponding +// call to End (though other events may begin and end before this event ends). +// Events within the same scope must have unique names. +func (h *EventHandler) Begin(name string) { + h.scopeIds = append(h.scopeIds, name) + h.scopeStartTimes = append(h.scopeStartTimes, _now()) +} + +// End logs the end of an event. All events nested within this event must have +// themselves been marked completed. +func (h *EventHandler) End(name string) { + if len(h.scopeIds) == 0 || name != h.scopeIds[len(h.scopeIds)-1] { + panic(fmt.Errorf("Unexpected scope end '%s'. Current scope: (%s)", + name, h.scopeIds)) + } + event := Event{ + // The event Id is formed from the period-delimited scope names of all + // active events (e.g. `alpha.beta.charlie`). See Event.Id documentation + // for more detail. + Id: strings.Join(h.scopeIds, "."), + Start: h.scopeStartTimes[len(h.scopeStartTimes)-1], + end: _now(), + } + h.completedEvents = append(h.completedEvents, event) + h.scopeIds = h.scopeIds[:len(h.scopeIds)-1] + h.scopeStartTimes = h.scopeStartTimes[:len(h.scopeStartTimes)-1] +} + +// CompletedEvents returns all events which have been completed, after +// validation. +// It is an error to call this method if there are still ongoing events, or +// if two events were completed with the same scope and name. +func (h *EventHandler) CompletedEvents() []Event { + if len(h.scopeIds) > 0 { + panic(fmt.Errorf( + "Retrieving events before all events have been closed. Current scope: (%s)", + h.scopeIds)) + } + // Validate no two events have the same full id. + ids := map[string]bool{} + for _, event := range h.completedEvents { + if _, containsId := ids[event.Id]; containsId { + panic(fmt.Errorf("Duplicate event registered: %s", event.Id)) + } + ids[event.Id] = true + } + return h.completedEvents +} diff --git a/blueprint/microfactory/main/main.go b/blueprint/microfactory/main/main.go new file mode 100644 index 0000000..f639963 --- /dev/null +++ b/blueprint/microfactory/main/main.go @@ -0,0 +1,23 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/google/blueprint/microfactory" +) + +func main() { + microfactory.Main() +} diff --git a/blueprint/microfactory/microfactory.bash b/blueprint/microfactory/microfactory.bash new file mode 100644 index 0000000..ded7b33 --- /dev/null +++ b/blueprint/microfactory/microfactory.bash @@ -0,0 +1,71 @@ +# Copyright 2017 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Set of utility functions to build and run go code with microfactory +# +# Inputs: +# ${GOROOT} +# ${BUILDDIR} +# ${BLUEPRINTDIR} +# ${SRCDIR} + +# Bootstrap microfactory from source if necessary and use it to build the +# requested binary. +# +# Arguments: +# $1: name of the requested binary +# $2: package name +# ${EXTRA_ARGS}: extra arguments to pass to microfactory (-pkg-path, etc) +function build_go +{ + # Increment when microfactory changes enough that it cannot rebuild itself. + # For example, if we use a new command line argument that doesn't work on older versions. + local mf_version=3 + + local mf_src="${BLUEPRINTDIR}/microfactory" + local mf_bin="${BUILDDIR}/microfactory_$(uname)" + local mf_version_file="${BUILDDIR}/.microfactory_$(uname)_version" + local built_bin="${BUILDDIR}/$1" + local from_src=1 + + if [ -f "${mf_bin}" ] && [ -f "${mf_version_file}" ]; then + if [ "${mf_version}" -eq "$(cat "${mf_version_file}")" ]; then + from_src=0 + fi + fi + + local mf_cmd + if [ $from_src -eq 1 ]; then + # `go run` requires a single main package, so create one + local gen_src_dir="${BUILDDIR}/.microfactory_$(uname)_intermediates/src" + mkdir -p "${gen_src_dir}" + sed "s/^package microfactory/package main/" "${mf_src}/microfactory.go" >"${gen_src_dir}/microfactory.go" + + mf_cmd="${GOROOT}/bin/go run ${gen_src_dir}/microfactory.go" + else + mf_cmd="${mf_bin}" + fi + + rm -f "${BUILDDIR}/.$1.trace" + # GOROOT must be absolute because `go run` changes the local directory + GOROOT=$(cd $GOROOT; pwd) ${mf_cmd} -b "${mf_bin}" \ + -pkg-path "github.com/google/blueprint=${BLUEPRINTDIR}" \ + -trimpath "${SRCDIR}" \ + ${EXTRA_ARGS} \ + -o "${built_bin}" $2 + + if [ $? -eq 0 ] && [ $from_src -eq 1 ]; then + echo "${mf_version}" >"${mf_version_file}" + fi +} diff --git a/blueprint/microfactory/microfactory.go b/blueprint/microfactory/microfactory.go new file mode 100644 index 0000000..a0c9a14 --- /dev/null +++ b/blueprint/microfactory/microfactory.go @@ -0,0 +1,688 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Microfactory is a tool to incrementally compile a go program. It's similar +// to `go install`, but doesn't require a GOPATH. A package->path mapping can +// be specified as command line options: +// +// -pkg-path android/soong=build/soong +// -pkg-path github.com/google/blueprint=build/blueprint +// +// The paths can be relative to the current working directory, or an absolute +// path. Both packages and paths are compared with full directory names, so the +// android/soong-test package wouldn't be mapped in the above case. +// +// Microfactory will ignore *_test.go files, and limits *_darwin.go and +// *_linux.go files to MacOS and Linux respectively. It does not support build +// tags or any other suffixes. +// +// Builds are incremental by package. All input files are hashed, and if the +// hash of an input or dependency changes, the package is rebuilt. +// +// It also exposes the -trimpath option from go's compiler so that embedded +// path names (such as in log.Llongfile) are relative paths instead of absolute +// paths. +// +// If you don't have a previously built version of Microfactory, when used with +// -b , Microfactory can rebuild itself as necessary. +// Combined with a shell script like microfactory.bash that uses `go run` to +// run Microfactory for the first time, go programs can be quickly bootstrapped +// entirely from source (and a standard go distribution). +package microfactory + +import ( + "bytes" + "crypto/sha1" + "flag" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "syscall" + "time" +) + +var ( + goToolDir = filepath.Join(runtime.GOROOT(), "pkg", "tool", runtime.GOOS+"_"+runtime.GOARCH) + goVersion = findGoVersion() + isGo18 = strings.Contains(goVersion, "go1.8") +) + +func findGoVersion() string { + if version, err := ioutil.ReadFile(filepath.Join(runtime.GOROOT(), "VERSION")); err == nil { + return string(version) + } + + cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "version") + if version, err := cmd.Output(); err == nil { + return string(version) + } else { + panic(fmt.Sprintf("Unable to discover go version: %v", err)) + } +} + +type Config struct { + Race bool + Verbose bool + + TrimPath string + + TraceFunc func(name string) func() + + pkgs []string + paths map[string]string +} + +func (c *Config) Map(pkgPrefix, pathPrefix string) error { + if c.paths == nil { + c.paths = make(map[string]string) + } + if _, ok := c.paths[pkgPrefix]; ok { + return fmt.Errorf("Duplicate package prefix: %q", pkgPrefix) + } + + c.pkgs = append(c.pkgs, pkgPrefix) + c.paths[pkgPrefix] = pathPrefix + + return nil +} + +// Path takes a package name, applies the path mappings and returns the resulting path. +// +// If the package isn't mapped, we'll return false to prevent compilation attempts. +func (c *Config) Path(pkg string) (string, bool, error) { + if c == nil || c.paths == nil { + return "", false, fmt.Errorf("No package mappings") + } + + for _, pkgPrefix := range c.pkgs { + if pkg == pkgPrefix { + return c.paths[pkgPrefix], true, nil + } else if strings.HasPrefix(pkg, pkgPrefix+"/") { + return filepath.Join(c.paths[pkgPrefix], strings.TrimPrefix(pkg, pkgPrefix+"/")), true, nil + } + } + + return "", false, nil +} + +func (c *Config) trace(format string, a ...interface{}) func() { + if c.TraceFunc == nil { + return func() {} + } + s := strings.TrimSpace(fmt.Sprintf(format, a...)) + return c.TraceFunc(s) +} + +func un(f func()) { + f() +} + +type GoPackage struct { + Name string + + // Inputs + directDeps []*GoPackage // specified directly by the module + allDeps []*GoPackage // direct dependencies and transitive dependencies + files []string + + // Outputs + pkgDir string + output string + hashResult []byte + + // Status + mutex sync.Mutex + compiled bool + failed error + rebuilt bool +} + +// LinkedHashMap +type linkedDepSet struct { + packageSet map[string](*GoPackage) + packageList []*GoPackage +} + +func newDepSet() *linkedDepSet { + return &linkedDepSet{packageSet: make(map[string]*GoPackage)} +} +func (s *linkedDepSet) tryGetByName(name string) (*GoPackage, bool) { + pkg, contained := s.packageSet[name] + return pkg, contained +} +func (s *linkedDepSet) getByName(name string) *GoPackage { + pkg, _ := s.tryGetByName(name) + return pkg +} +func (s *linkedDepSet) add(name string, goPackage *GoPackage) { + s.packageSet[name] = goPackage + s.packageList = append(s.packageList, goPackage) +} +func (s *linkedDepSet) ignore(name string) { + s.packageSet[name] = nil +} + +// FindDeps searches all applicable go files in `path`, parses all of them +// for import dependencies that exist in pkgMap, then recursively does the +// same for all of those dependencies. +func (p *GoPackage) FindDeps(config *Config, path string) error { + defer un(config.trace("findDeps")) + + depSet := newDepSet() + err := p.findDeps(config, path, depSet) + if err != nil { + return err + } + p.allDeps = depSet.packageList + return nil +} + +// Roughly equivalent to go/build.Context.match +func matchBuildTag(name string) bool { + if name == "" { + return false + } + if i := strings.Index(name, ","); i >= 0 { + ok1 := matchBuildTag(name[:i]) + ok2 := matchBuildTag(name[i+1:]) + return ok1 && ok2 + } + if strings.HasPrefix(name, "!!") { + return false + } + if strings.HasPrefix(name, "!") { + return len(name) > 1 && !matchBuildTag(name[1:]) + } + + if name == runtime.GOOS || name == runtime.GOARCH || name == "gc" { + return true + } + for _, tag := range build.Default.BuildTags { + if tag == name { + return true + } + } + for _, tag := range build.Default.ReleaseTags { + if tag == name { + return true + } + } + + return false +} + +func parseBuildComment(comment string) (matches, ok bool) { + if !strings.HasPrefix(comment, "//") { + return false, false + } + for i, c := range comment { + if i < 2 || c == ' ' || c == '\t' { + continue + } else if c == '+' { + f := strings.Fields(comment[i:]) + if f[0] == "+build" { + matches = false + for _, tok := range f[1:] { + matches = matches || matchBuildTag(tok) + } + return matches, true + } + } + break + } + return false, false +} + +// findDeps is the recursive version of FindDeps. allPackages is the map of +// all locally defined packages so that the same dependency of two different +// packages is only resolved once. +func (p *GoPackage) findDeps(config *Config, path string, allPackages *linkedDepSet) error { + // If this ever becomes too slow, we can look at reading the files once instead of twice + // But that just complicates things today, and we're already really fast. + foundPkgs, err := parser.ParseDir(token.NewFileSet(), path, func(fi os.FileInfo) bool { + name := fi.Name() + if fi.IsDir() || strings.HasSuffix(name, "_test.go") || name[0] == '.' || name[0] == '_' { + return false + } + if runtime.GOOS != "darwin" && strings.HasSuffix(name, "_darwin.go") { + return false + } + if runtime.GOOS != "linux" && strings.HasSuffix(name, "_linux.go") { + return false + } + return true + }, parser.ImportsOnly|parser.ParseComments) + if err != nil { + return fmt.Errorf("Error parsing directory %q: %v", path, err) + } + + var foundPkg *ast.Package + // foundPkgs is a map[string]*ast.Package, but we only want one package + if len(foundPkgs) != 1 { + return fmt.Errorf("Expected one package in %q, got %d", path, len(foundPkgs)) + } + // Extract the first (and only) entry from the map. + for _, pkg := range foundPkgs { + foundPkg = pkg + } + + var deps []string + localDeps := make(map[string]bool) + + for filename, astFile := range foundPkg.Files { + ignore := false + for _, commentGroup := range astFile.Comments { + for _, comment := range commentGroup.List { + if matches, ok := parseBuildComment(comment.Text); ok && !matches { + ignore = true + } + } + } + if ignore { + continue + } + + p.files = append(p.files, filename) + + for _, importSpec := range astFile.Imports { + name, err := strconv.Unquote(importSpec.Path.Value) + if err != nil { + return fmt.Errorf("%s: invalid quoted string: <%s> %v", filename, importSpec.Path.Value, err) + } + + if pkg, ok := allPackages.tryGetByName(name); ok { + if pkg != nil { + if _, ok := localDeps[name]; !ok { + deps = append(deps, name) + localDeps[name] = true + } + } + continue + } + + var pkgPath string + if path, ok, err := config.Path(name); err != nil { + return err + } else if !ok { + // Probably in the stdlib, but if not, then the compiler will fail with a reasonable error message + // Mark it as such so that we don't try to decode its path again. + allPackages.ignore(name) + continue + } else { + pkgPath = path + } + + pkg := &GoPackage{ + Name: name, + } + deps = append(deps, name) + allPackages.add(name, pkg) + localDeps[name] = true + + if err := pkg.findDeps(config, pkgPath, allPackages); err != nil { + return err + } + } + } + + sort.Strings(p.files) + + if config.Verbose { + fmt.Fprintf(os.Stderr, "Package %q depends on %v\n", p.Name, deps) + } + + sort.Strings(deps) + for _, dep := range deps { + p.directDeps = append(p.directDeps, allPackages.getByName(dep)) + } + + return nil +} + +func (p *GoPackage) Compile(config *Config, outDir string) error { + p.mutex.Lock() + defer p.mutex.Unlock() + if p.compiled { + return p.failed + } + p.compiled = true + + // Build all dependencies in parallel, then fail if any of them failed. + var wg sync.WaitGroup + for _, dep := range p.directDeps { + wg.Add(1) + go func(dep *GoPackage) { + defer wg.Done() + dep.Compile(config, outDir) + }(dep) + } + wg.Wait() + for _, dep := range p.directDeps { + if dep.failed != nil { + p.failed = dep.failed + return p.failed + } + } + + endTrace := config.trace("check compile %s", p.Name) + + p.pkgDir = filepath.Join(outDir, strings.Replace(p.Name, "/", "-", -1)) + p.output = filepath.Join(p.pkgDir, p.Name) + ".a" + shaFile := p.output + ".hash" + + hash := sha1.New() + fmt.Fprintln(hash, runtime.GOOS, runtime.GOARCH, goVersion) + + cmd := exec.Command(filepath.Join(goToolDir, "compile"), + "-N", "-l", // Disable optimization and inlining so that debugging works better + "-o", p.output, + "-p", p.Name, + "-complete", "-pack", "-nolocalimports") + if !isGo18 && !config.Race { + cmd.Args = append(cmd.Args, "-c", fmt.Sprintf("%d", runtime.NumCPU())) + } + if config.Race { + cmd.Args = append(cmd.Args, "-race") + fmt.Fprintln(hash, "-race") + } + if config.TrimPath != "" { + cmd.Args = append(cmd.Args, "-trimpath", config.TrimPath) + fmt.Fprintln(hash, config.TrimPath) + } + for _, dep := range p.directDeps { + cmd.Args = append(cmd.Args, "-I", dep.pkgDir) + hash.Write(dep.hashResult) + } + for _, filename := range p.files { + cmd.Args = append(cmd.Args, filename) + fmt.Fprintln(hash, filename) + + // Hash the contents of the input files + f, err := os.Open(filename) + if err != nil { + f.Close() + err = fmt.Errorf("%s: %v", filename, err) + p.failed = err + return err + } + _, err = io.Copy(hash, f) + if err != nil { + f.Close() + err = fmt.Errorf("%s: %v", filename, err) + p.failed = err + return err + } + f.Close() + } + p.hashResult = hash.Sum(nil) + + var rebuild bool + if _, err := os.Stat(p.output); err != nil { + rebuild = true + } + if !rebuild { + if oldSha, err := ioutil.ReadFile(shaFile); err == nil { + rebuild = !bytes.Equal(oldSha, p.hashResult) + } else { + rebuild = true + } + } + + endTrace() + if !rebuild { + return nil + } + defer un(config.trace("compile %s", p.Name)) + + err := os.RemoveAll(p.pkgDir) + if err != nil { + err = fmt.Errorf("%s: %v", p.Name, err) + p.failed = err + return err + } + + err = os.MkdirAll(filepath.Dir(p.output), 0777) + if err != nil { + err = fmt.Errorf("%s: %v", p.Name, err) + p.failed = err + return err + } + + cmd.Stdin = nil + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if config.Verbose { + fmt.Fprintln(os.Stderr, cmd.Args) + } + err = cmd.Run() + if err != nil { + commandText := strings.Join(cmd.Args, " ") + err = fmt.Errorf("%q: %v", commandText, err) + p.failed = err + return err + } + + err = ioutil.WriteFile(shaFile, p.hashResult, 0666) + if err != nil { + err = fmt.Errorf("%s: %v", p.Name, err) + p.failed = err + return err + } + + p.rebuilt = true + + return nil +} + +func (p *GoPackage) Link(config *Config, out string) error { + if p.Name != "main" { + return fmt.Errorf("Can only link main package") + } + endTrace := config.trace("check link %s", p.Name) + + shaFile := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+"_hash") + + if !p.rebuilt { + if _, err := os.Stat(out); err != nil { + p.rebuilt = true + } else if oldSha, err := ioutil.ReadFile(shaFile); err != nil { + p.rebuilt = true + } else { + p.rebuilt = !bytes.Equal(oldSha, p.hashResult) + } + } + endTrace() + if !p.rebuilt { + return nil + } + defer un(config.trace("link %s", p.Name)) + + err := os.Remove(shaFile) + if err != nil && !os.IsNotExist(err) { + return err + } + err = os.Remove(out) + if err != nil && !os.IsNotExist(err) { + return err + } + + cmd := exec.Command(filepath.Join(goToolDir, "link"), "-o", out) + if config.Race { + cmd.Args = append(cmd.Args, "-race") + } + for _, dep := range p.allDeps { + cmd.Args = append(cmd.Args, "-L", dep.pkgDir) + } + cmd.Args = append(cmd.Args, p.output) + cmd.Stdin = nil + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if config.Verbose { + fmt.Fprintln(os.Stderr, cmd.Args) + } + err = cmd.Run() + if err != nil { + return fmt.Errorf("command %s failed with error %v", cmd.Args, err) + } + + return ioutil.WriteFile(shaFile, p.hashResult, 0666) +} + +func Build(config *Config, out, pkg string) (*GoPackage, error) { + p := &GoPackage{ + Name: "main", + } + + lockFileName := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+".lock") + lockFile, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return nil, fmt.Errorf("Error creating lock file (%q): %v", lockFileName, err) + } + defer lockFile.Close() + + err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX) + if err != nil { + return nil, fmt.Errorf("Error locking file (%q): %v", lockFileName, err) + } + + path, ok, err := config.Path(pkg) + if err != nil { + return nil, fmt.Errorf("Error finding package %q for main: %v", pkg, err) + } + if !ok { + return nil, fmt.Errorf("Could not find package %q", pkg) + } + + intermediates := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+"_intermediates") + if err := os.MkdirAll(intermediates, 0777); err != nil { + return nil, fmt.Errorf("Failed to create intermediates directory: %v", err) + } + + if err := p.FindDeps(config, path); err != nil { + return nil, fmt.Errorf("Failed to find deps of %v: %v", pkg, err) + } + if err := p.Compile(config, intermediates); err != nil { + return nil, fmt.Errorf("Failed to compile %v: %v", pkg, err) + } + if err := p.Link(config, out); err != nil { + return nil, fmt.Errorf("Failed to link %v: %v", pkg, err) + } + return p, nil +} + +// rebuildMicrofactory checks to see if microfactory itself needs to be rebuilt, +// and if does, it will launch a new copy and return true. Otherwise it will return +// false to continue executing. +func rebuildMicrofactory(config *Config, mybin string) bool { + if pkg, err := Build(config, mybin, "github.com/google/blueprint/microfactory/main"); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } else if !pkg.rebuilt { + return false + } + + cmd := exec.Command(mybin, os.Args[1:]...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err == nil { + return true + } else if e, ok := err.(*exec.ExitError); ok { + os.Exit(e.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()) + } + os.Exit(1) + return true +} + +// microfactory.bash will make a copy of this file renamed into the main package for use with `go run` +func main() { Main() } +func Main() { + var output, mybin string + var config Config + pkgMap := pkgPathMappingVar{&config} + + flags := flag.NewFlagSet("", flag.ExitOnError) + flags.BoolVar(&config.Race, "race", false, "enable data race detection.") + flags.BoolVar(&config.Verbose, "v", false, "Verbose") + flags.StringVar(&output, "o", "", "Output file") + flags.StringVar(&mybin, "b", "", "Microfactory binary location") + flags.StringVar(&config.TrimPath, "trimpath", "", "remove prefix from recorded source file paths") + flags.Var(&pkgMap, "pkg-path", "Mapping of package prefixes to file paths") + err := flags.Parse(os.Args[1:]) + + if err == flag.ErrHelp || flags.NArg() != 1 || output == "" { + fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "-o out/binary ") + flags.PrintDefaults() + os.Exit(1) + } + + tracePath := filepath.Join(filepath.Dir(output), "."+filepath.Base(output)+".trace") + if traceFile, err := os.OpenFile(tracePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666); err == nil { + defer traceFile.Close() + config.TraceFunc = func(name string) func() { + fmt.Fprintf(traceFile, "%d B %s\n", time.Now().UnixNano()/1000, name) + return func() { + fmt.Fprintf(traceFile, "%d E %s\n", time.Now().UnixNano()/1000, name) + } + } + } + if executable, err := os.Executable(); err == nil { + defer un(config.trace("microfactory %s", executable)) + } else { + defer un(config.trace("microfactory ")) + } + + if mybin != "" { + if rebuildMicrofactory(&config, mybin) { + return + } + } + + if _, err := Build(&config, output, flags.Arg(0)); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +// pkgPathMapping can be used with flag.Var to parse -pkg-path arguments of +// = mappings. +type pkgPathMappingVar struct{ *Config } + +func (pkgPathMappingVar) String() string { + return "=" +} + +func (p *pkgPathMappingVar) Set(value string) error { + equalPos := strings.Index(value, "=") + if equalPos == -1 { + return fmt.Errorf("Argument must be in the form of: %q", p.String()) + } + + pkgPrefix := strings.TrimSuffix(value[:equalPos], "/") + pathPrefix := strings.TrimSuffix(value[equalPos+1:], "/") + + return p.Map(pkgPrefix, pathPrefix) +} diff --git a/blueprint/microfactory/microfactory_test.go b/blueprint/microfactory/microfactory_test.go new file mode 100644 index 0000000..8356adf --- /dev/null +++ b/blueprint/microfactory/microfactory_test.go @@ -0,0 +1,423 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package microfactory + +import ( + "flag" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "runtime" + "testing" + "time" +) + +func TestSimplePackagePathMap(t *testing.T) { + t.Parallel() + + pkgMap := pkgPathMappingVar{&Config{}} + flags := flag.NewFlagSet("", flag.ContinueOnError) + flags.Var(&pkgMap, "m", "") + err := flags.Parse([]string{ + "-m", "android/soong=build/soong/", + "-m", "github.com/google/blueprint/=build/blueprint", + }) + if err != nil { + t.Fatal(err) + } + + compare := func(got, want interface{}) { + if !reflect.DeepEqual(got, want) { + t.Errorf("Unexpected values in .pkgs:\nwant: %v\n got: %v", + want, got) + } + } + + wantPkgs := []string{"android/soong", "github.com/google/blueprint"} + compare(pkgMap.pkgs, wantPkgs) + compare(pkgMap.paths[wantPkgs[0]], "build/soong") + compare(pkgMap.paths[wantPkgs[1]], "build/blueprint") + + got, ok, err := pkgMap.Path("android/soong/ui/test") + if err != nil { + t.Error("Unexpected error in pkgMap.Path(soong):", err) + } else if !ok { + t.Error("Expected a result from pkgMap.Path(soong)") + } else { + compare(got, "build/soong/ui/test") + } + + got, ok, err = pkgMap.Path("github.com/google/blueprint") + if err != nil { + t.Error("Unexpected error in pkgMap.Path(blueprint):", err) + } else if !ok { + t.Error("Expected a result from pkgMap.Path(blueprint)") + } else { + compare(got, "build/blueprint") + } +} + +func TestBadPackagePathMap(t *testing.T) { + t.Parallel() + + pkgMap := pkgPathMappingVar{&Config{}} + if _, _, err := pkgMap.Path("testing"); err == nil { + t.Error("Expected error if no maps are specified") + } + if err := pkgMap.Set(""); err == nil { + t.Error("Expected error with blank argument, but none returned") + } + if err := pkgMap.Set("a=a"); err != nil { + t.Errorf("Unexpected error: %v", err) + } + if err := pkgMap.Set("a=b"); err == nil { + t.Error("Expected error with duplicate package prefix, but none returned") + } + if _, ok, err := pkgMap.Path("testing"); err != nil { + t.Errorf("Unexpected error: %v", err) + } else if ok { + t.Error("Expected testing to be consider in the stdlib") + } +} + +// TestSingleBuild ensures that just a basic build works. +func TestSingleBuild(t *testing.T) { + t.Parallel() + + setupDir(t, func(config *Config, dir string, loadPkg loadPkgFunc) { + // The output binary + out := filepath.Join(dir, "out", "test") + + pkg := loadPkg() + + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + t.Fatal("Got error when compiling:", err) + } + + if err := pkg.Link(config, out); err != nil { + t.Fatal("Got error when linking:", err) + } + + if _, err := os.Stat(out); err != nil { + t.Error("Cannot stat output:", err) + } + }) +} + +// testBuildAgain triggers two builds, running the modify function in between +// each build. It verifies that the second build did or did not actually need +// to rebuild anything based on the shouldRebuild argument. +func testBuildAgain(t *testing.T, + shouldRecompile, shouldRelink bool, + modify func(config *Config, dir string, loadPkg loadPkgFunc), + after func(pkg *GoPackage)) { + + t.Parallel() + + setupDir(t, func(config *Config, dir string, loadPkg loadPkgFunc) { + // The output binary + out := filepath.Join(dir, "out", "test") + + pkg := loadPkg() + + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + t.Fatal("Got error when compiling:", err) + } + + if err := pkg.Link(config, out); err != nil { + t.Fatal("Got error when linking:", err) + } + + var firstTime time.Time + if stat, err := os.Stat(out); err == nil { + firstTime = stat.ModTime() + } else { + t.Fatal("Failed to stat output file:", err) + } + + // mtime on HFS+ (the filesystem on darwin) are stored with 1 + // second granularity, so the timestamp checks will fail unless + // we wait at least a second. Sleeping 1.1s to be safe. + if runtime.GOOS == "darwin" { + time.Sleep(1100 * time.Millisecond) + } + + modify(config, dir, loadPkg) + + pkg = loadPkg() + + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + t.Fatal("Got error when compiling:", err) + } + if shouldRecompile { + if !pkg.rebuilt { + t.Fatal("Package should have recompiled, but was not recompiled.") + } + } else { + if pkg.rebuilt { + t.Fatal("Package should not have needed to be recompiled, but was recompiled.") + } + } + + if err := pkg.Link(config, out); err != nil { + t.Fatal("Got error while linking:", err) + } + if shouldRelink { + if !pkg.rebuilt { + t.Error("Package should have relinked, but was not relinked.") + } + } else { + if pkg.rebuilt { + t.Error("Package should not have needed to be relinked, but was relinked.") + } + } + + if stat, err := os.Stat(out); err == nil { + if shouldRelink { + if stat.ModTime() == firstTime { + t.Error("Output timestamp should be different, but both were", firstTime) + } + } else { + if stat.ModTime() != firstTime { + t.Error("Output timestamp should be the same.") + t.Error(" first:", firstTime) + t.Error("second:", stat.ModTime()) + } + } + } else { + t.Fatal("Failed to stat output file:", err) + } + + after(pkg) + }) +} + +// TestRebuildAfterNoChanges ensures that we don't rebuild if nothing +// changes +func TestRebuildAfterNoChanges(t *testing.T) { + testBuildAgain(t, false, false, func(config *Config, dir string, loadPkg loadPkgFunc) {}, func(pkg *GoPackage) {}) +} + +// TestRebuildAfterTimestamp ensures that we don't rebuild because +// timestamps of important files have changed. We should only rebuild if the +// content hashes are different. +func TestRebuildAfterTimestampChange(t *testing.T) { + testBuildAgain(t, false, false, func(config *Config, dir string, loadPkg loadPkgFunc) { + // Ensure that we've spent some amount of time asleep + time.Sleep(100 * time.Millisecond) + + newTime := time.Now().Local() + os.Chtimes(filepath.Join(dir, "test.fact"), newTime, newTime) + os.Chtimes(filepath.Join(dir, "main/main.go"), newTime, newTime) + os.Chtimes(filepath.Join(dir, "a/a.go"), newTime, newTime) + os.Chtimes(filepath.Join(dir, "a/b.go"), newTime, newTime) + os.Chtimes(filepath.Join(dir, "b/a.go"), newTime, newTime) + }, func(pkg *GoPackage) {}) +} + +// TestRebuildAfterGoChange ensures that we rebuild after a content change +// to a package's go file. +func TestRebuildAfterGoChange(t *testing.T) { + testBuildAgain(t, true, true, func(config *Config, dir string, loadPkg loadPkgFunc) { + if err := ioutil.WriteFile(filepath.Join(dir, "a", "a.go"), []byte(go_a_a+"\n"), 0666); err != nil { + t.Fatal("Error writing a/a.go:", err) + } + }, func(pkg *GoPackage) { + if !pkg.directDeps[0].rebuilt { + t.Fatal("android/soong/a should have rebuilt") + } + if !pkg.directDeps[1].rebuilt { + t.Fatal("android/soong/b should have rebuilt") + } + }) +} + +// TestRebuildAfterMainChange ensures that we don't rebuild any dependencies +// if only the main package's go files are touched. +func TestRebuildAfterMainChange(t *testing.T) { + testBuildAgain(t, true, true, func(config *Config, dir string, loadPkg loadPkgFunc) { + if err := ioutil.WriteFile(filepath.Join(dir, "main", "main.go"), []byte(go_main_main+"\n"), 0666); err != nil { + t.Fatal("Error writing main/main.go:", err) + } + }, func(pkg *GoPackage) { + if pkg.directDeps[0].rebuilt { + t.Fatal("android/soong/a should not have rebuilt") + } + if pkg.directDeps[1].rebuilt { + t.Fatal("android/soong/b should not have rebuilt") + } + }) +} + +// TestRebuildAfterRemoveOut ensures that we rebuild if the output file is +// missing, even if everything else doesn't need rebuilding. +func TestRebuildAfterRemoveOut(t *testing.T) { + testBuildAgain(t, false, true, func(config *Config, dir string, loadPkg loadPkgFunc) { + if err := os.Remove(filepath.Join(dir, "out", "test")); err != nil { + t.Fatal("Failed to remove output:", err) + } + }, func(pkg *GoPackage) {}) +} + +// TestRebuildAfterPartialBuild ensures that even if the build was interrupted +// between the recompile and relink stages, we'll still relink when we run again. +func TestRebuildAfterPartialBuild(t *testing.T) { + testBuildAgain(t, false, true, func(config *Config, dir string, loadPkg loadPkgFunc) { + if err := ioutil.WriteFile(filepath.Join(dir, "main", "main.go"), []byte(go_main_main+"\n"), 0666); err != nil { + t.Fatal("Error writing main/main.go:", err) + } + + pkg := loadPkg() + + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + t.Fatal("Got error when compiling:", err) + } + if !pkg.rebuilt { + t.Fatal("Package should have recompiled, but was not recompiled.") + } + }, func(pkg *GoPackage) {}) +} + +// BenchmarkInitialBuild computes how long a clean build takes (for tiny test +// inputs). +func BenchmarkInitialBuild(b *testing.B) { + for i := 0; i < b.N; i++ { + setupDir(b, func(config *Config, dir string, loadPkg loadPkgFunc) { + pkg := loadPkg() + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + b.Fatal("Got error when compiling:", err) + } + + if err := pkg.Link(config, filepath.Join(dir, "out", "test")); err != nil { + b.Fatal("Got error when linking:", err) + } + }) + } +} + +// BenchmarkMinIncrementalBuild computes how long an incremental build that +// doesn't actually need to build anything takes. +func BenchmarkMinIncrementalBuild(b *testing.B) { + setupDir(b, func(config *Config, dir string, loadPkg loadPkgFunc) { + pkg := loadPkg() + + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + b.Fatal("Got error when compiling:", err) + } + + if err := pkg.Link(config, filepath.Join(dir, "out", "test")); err != nil { + b.Fatal("Got error when linking:", err) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + pkg := loadPkg() + + if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil { + b.Fatal("Got error when compiling:", err) + } + + if err := pkg.Link(config, filepath.Join(dir, "out", "test")); err != nil { + b.Fatal("Got error when linking:", err) + } + + if pkg.rebuilt { + b.Fatal("Should not have rebuilt anything") + } + } + }) +} + +/////////////////////////////////////////////////////// +// Templates used to create fake compilable packages // +/////////////////////////////////////////////////////// + +const go_main_main = ` +package main +import ( + "fmt" + "android/soong/a" + "android/soong/b" +) +func main() { + fmt.Println(a.Stdout, b.Stdout) +} +` + +const go_a_a = ` +package a +import "os" +var Stdout = os.Stdout +` + +const go_a_b = ` +package a +` + +const go_b_a = ` +package b +import "android/soong/a" +var Stdout = a.Stdout +` + +type T interface { + Fatal(args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +type loadPkgFunc func() *GoPackage + +func setupDir(t T, test func(config *Config, dir string, loadPkg loadPkgFunc)) { + dir, err := ioutil.TempDir("", "test") + if err != nil { + t.Fatalf("Error creating temporary directory: %#v", err) + } + defer os.RemoveAll(dir) + + writeFile := func(name, contents string) { + if err := ioutil.WriteFile(filepath.Join(dir, name), []byte(contents), 0666); err != nil { + t.Fatalf("Error writing %q: %#v", name, err) + } + } + mkdir := func(name string) { + if err := os.Mkdir(filepath.Join(dir, name), 0777); err != nil { + t.Fatalf("Error creating %q directory: %#v", name, err) + } + } + mkdir("main") + mkdir("a") + mkdir("b") + writeFile("main/main.go", go_main_main) + writeFile("a/a.go", go_a_a) + writeFile("a/b.go", go_a_b) + writeFile("b/a.go", go_b_a) + + config := &Config{} + config.Map("android/soong", dir) + + loadPkg := func() *GoPackage { + pkg := &GoPackage{ + Name: "main", + } + if err := pkg.FindDeps(config, filepath.Join(dir, "main")); err != nil { + t.Fatalf("Error finding deps: %v", err) + } + return pkg + } + + test(config, dir, loadPkg) +} diff --git a/blueprint/module_ctx.go b/blueprint/module_ctx.go new file mode 100644 index 0000000..53ee405 --- /dev/null +++ b/blueprint/module_ctx.go @@ -0,0 +1,1409 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "path/filepath" + "strings" + "sync" + "text/scanner" + + "github.com/google/blueprint/parser" + "github.com/google/blueprint/pathtools" + "github.com/google/blueprint/proptools" +) + +// A Module handles generating all of the Ninja build actions needed to build a +// single module based on properties defined in a Blueprints file. Module +// objects are initially created during the parse phase of a Context using one +// of the registered module types (and the associated ModuleFactory function). +// The Module's properties struct is automatically filled in with the property +// values specified in the Blueprints file (see Context.RegisterModuleType for more +// information on this). +// +// A Module can be split into multiple Modules by a Mutator. All existing +// properties set on the module will be duplicated to the new Module, and then +// modified as necessary by the Mutator. +// +// The Module implementation can access the build configuration as well as any +// modules on which it depends (as defined by the "deps" property +// specified in the Blueprints file, dynamically added by implementing the +// (deprecated) DynamicDependerModule interface, or dynamically added by a +// BottomUpMutator) using the ModuleContext passed to GenerateBuildActions. +// This ModuleContext is also used to create Ninja build actions and to report +// errors to the user. +// +// In addition to implementing the GenerateBuildActions method, a Module should +// implement methods that provide dependant modules and singletons information +// they need to generate their build actions. These methods will only be called +// after GenerateBuildActions is called because the Context calls +// GenerateBuildActions in dependency-order (and singletons are invoked after +// all the Modules). The set of methods a Module supports will determine how +// dependant Modules interact with it. +// +// For example, consider a Module that is responsible for generating a library +// that other modules can link against. The library Module might implement the +// following interface: +// +// type LibraryProducer interface { +// LibraryFileName() string +// } +// +// func IsLibraryProducer(module blueprint.Module) { +// _, ok := module.(LibraryProducer) +// return ok +// } +// +// A binary-producing Module that depends on the library Module could then do: +// +// func (m *myBinaryModule) GenerateBuildActions(ctx blueprint.ModuleContext) { +// ... +// var libraryFiles []string +// ctx.VisitDepsDepthFirstIf(IsLibraryProducer, +// func(module blueprint.Module) { +// libProducer := module.(LibraryProducer) +// libraryFiles = append(libraryFiles, libProducer.LibraryFileName()) +// }) +// ... +// } +// +// to build the list of library file names that should be included in its link +// command. +// +// GenerateBuildActions may be called from multiple threads. It is guaranteed to +// be called after it has finished being called on all dependencies and on all +// variants of that appear earlier in the ModuleContext.VisitAllModuleVariants list. +// Any accesses to global variables or to Module objects that are not dependencies +// or variants of the current Module must be synchronized by the implementation of +// GenerateBuildActions. +type Module interface { + // Name returns a string used to uniquely identify each module. The return + // value must be unique across all modules. It is only called once, during + // initial blueprint parsing. To change the name later a mutator must call + // MutatorContext.Rename + // + // In most cases, Name should return the contents of a "name:" property from + // the blueprint file. An embeddable SimpleName object can be used for this + // case. + Name() string + + // GenerateBuildActions is called by the Context that created the Module + // during its generate phase. This call should generate all Ninja build + // actions (rules, pools, and build statements) needed to build the module. + GenerateBuildActions(ModuleContext) +} + +// A DynamicDependerModule is a Module that may add dependencies that do not +// appear in its "deps" property. Any Module that implements this interface +// will have its DynamicDependencies method called by the Context that created +// it during generate phase. +// +// Deprecated, use a BottomUpMutator instead +type DynamicDependerModule interface { + Module + + // DynamicDependencies is called by the Context that created the + // DynamicDependerModule during its generate phase. This call should return + // the list of module names that the DynamicDependerModule depends on + // dynamically. Module names that already appear in the "deps" property may + // but do not need to be included in the returned list. + DynamicDependencies(DynamicDependerModuleContext) []string +} + +type EarlyModuleContext interface { + // Module returns the current module as a Module. It should rarely be necessary, as the module already has a + // reference to itself. + Module() Module + + // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when + // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. + ModuleName() string + + // ModuleDir returns the path to the directory that contains the definition of the module. + ModuleDir() string + + // ModuleType returns the name of the module type that was used to create the module, as specified in + // Context.RegisterModuleType(). + ModuleType() string + + // BlueprintsFile returns the name of the blueprint file that contains the definition of this + // module. + BlueprintsFile() string + + // Config returns the config object that was passed to Context.PrepareBuildActions. + Config() interface{} + + // ContainsProperty returns true if the specified property name was set in the module definition. + ContainsProperty(name string) bool + + // Errorf reports an error at the specified position of the module definition file. + Errorf(pos scanner.Position, fmt string, args ...interface{}) + + // ModuleErrorf reports an error at the line number of the module type in the module definition. + ModuleErrorf(fmt string, args ...interface{}) + + // PropertyErrorf reports an error at the line number of a property in the module definition. + PropertyErrorf(property, fmt string, args ...interface{}) + + // Failed returns true if any errors have been reported. In most cases the module can continue with generating + // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error + // has prevented the module from creating necessary data it can return early when Failed returns true. + Failed() bool + + // GlobWithDeps returns a list of files and directories that match the + // specified pattern but do not match any of the patterns in excludes. + // Any directories will have a '/' suffix. It also adds efficient + // dependencies to rerun the primary builder whenever a file matching + // the pattern as added or removed, without rerunning if a file that + // does not match the pattern is added to a searched directory. + GlobWithDeps(pattern string, excludes []string) ([]string, error) + + // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows + // the module to be used in build system tests that run against a mock filesystem. + Fs() pathtools.FileSystem + + // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The + // primary builder will be rerun whenever the specified files are modified. + AddNinjaFileDeps(deps ...string) + + moduleInfo() *moduleInfo + error(err error) + + // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the + // default SimpleNameInterface if Context.SetNameInterface was not called. + Namespace() Namespace + + // ModuleFactories returns a map of all of the global ModuleFactories by name. + ModuleFactories() map[string]ModuleFactory +} + +type BaseModuleContext interface { + EarlyModuleContext + + // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if + // none exists. It panics if the dependency does not have the specified tag. + GetDirectDepWithTag(name string, tag DependencyTag) Module + + // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified + // name, or nil if none exists. If there are multiple dependencies on the same module it returns + // the first DependencyTag. + GetDirectDep(name string) (Module, DependencyTag) + + // VisitDirectDeps calls visit for each direct dependency. If there are multiple direct dependencies on the same + // module visit will be called multiple times on that module and OtherModuleDependencyTag will return a different + // tag for each. + // + // The Module passed to the visit function should not be retained outside of the visit function, it may be + // invalidated by future mutators. + VisitDirectDeps(visit func(Module)) + + // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are + // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and + // OtherModuleDependencyTag will return a different tag for each. + // + // The Module passed to the visit function should not be retained outside of the visit function, it may be + // invalidated by future mutators. + VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) + + // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first + // order. visit will only be called once for any given module, even if there are multiple paths through the + // dependency tree to the module or multiple direct dependencies with different tags. OtherModuleDependencyTag will + // return the tag for the first path found to the module. + // + // The Module passed to the visit function should not be retained outside of the visit function, it may be + // invalidated by future mutators. + VisitDepsDepthFirst(visit func(Module)) + + // VisitDepsDepthFirstIf calls pred for each transitive dependency, and if pred returns true calls visit, traversing + // the dependency tree in depth first order. visit will only be called once for any given module, even if there are + // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags. + // OtherModuleDependencyTag will return the tag for the first path found to the module. The return value of pred + // does not affect which branches of the tree are traversed. + // + // The Module passed to the visit function should not be retained outside of the visit function, it may be + // invalidated by future mutators. + VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) + + // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may + // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the + // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited + // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. + // + // The Modules passed to the visit function should not be retained outside of the visit function, they may be + // invalidated by future mutators. + WalkDeps(visit func(Module, Module) bool) + + // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the + // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are + // only done once for all variants of a module. + PrimaryModule() Module + + // FinalModule returns the last variant of the current module. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all + // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform + // singleton actions that are only done once for all variants of a module. + FinalModule() Module + + // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always + // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read + // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any + // data modified by the current mutator. + VisitAllModuleVariants(visit func(Module)) + + // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleName(m Module) string + + // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleDir(m Module) string + + // OtherModuleSubDir returns the unique subdirectory name of another Module. See ModuleContext.ModuleSubDir for + // more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleSubDir(m Module) string + + // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleType(m Module) string + + // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleErrorf(m Module, fmt string, args ...interface{}) + + // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency + // on the module. When called inside a Visit* method with current module being visited, and there are multiple + // dependencies on the module being visited, it returns the dependency tag used for the current dependency. + OtherModuleDependencyTag(m Module) DependencyTag + + // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface + // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. + OtherModuleExists(name string) bool + + // ModuleFromName returns (module, true) if a module exists by the given name and same context namespace, + // or (nil, false) if it does not exist. It panics if there is either more than one + // module of the given name, or if the given name refers to an alias instead of a module. + // There are no guarantees about which variant of the module will be returned. + // Prefer retrieving the module using GetDirectDep or a visit function, when possible, as + // this will guarantee the appropriate module-variant dependency is returned. + ModuleFromName(name string) (Module, bool) + + // OtherModuleDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations. It must also match all the non-local variations of the current + // module. In other words, it checks for the module that AddVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleDependencyVariantExists(variations []Variation, name string) bool + + // OtherModuleFarDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations, but not the non-local variations of the current module. In + // other words, it checks for the module that AddFarVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleFarDependencyVariantExists(variations []Variation, name string) bool + + // OtherModuleReverseDependencyVariantExists returns true if a module with the + // specified name exists with the same variations as the current module. In + // other words, it checks for the module that AddReverseDependency would add a + // dependency on with the same argument. + OtherModuleReverseDependencyVariantExists(name string) bool + + // OtherModuleProvider returns the value for a provider for the given module. If the value is + // not set it returns the zero value of the type of the provider, so the return value can always + // be type asserted to the type of the provider. The value returned may be a deep copy of the + // value originally passed to SetProvider. + OtherModuleProvider(m Module, provider ProviderKey) interface{} + + // OtherModuleHasProvider returns true if the provider for the given module has been set. + OtherModuleHasProvider(m Module, provider ProviderKey) bool + + // Provider returns the value for a provider for the current module. If the value is + // not set it returns the zero value of the type of the provider, so the return value can always + // be type asserted to the type of the provider. It panics if called before the appropriate + // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep + // copy of the value originally passed to SetProvider. + Provider(provider ProviderKey) interface{} + + // HasProvider returns true if the provider for the current module has been set. + HasProvider(provider ProviderKey) bool + + // SetProvider sets the value for a provider for the current module. It panics if not called + // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value + // is not of the appropriate type, or if the value has already been set. The value should not + // be modified after being passed to SetProvider. + SetProvider(provider ProviderKey, value interface{}) +} + +type DynamicDependerModuleContext BottomUpMutatorContext + +type ModuleContext interface { + BaseModuleContext + + // ModuleSubDir returns a unique name for the current variant of a module that can be used as part of the path + // to ensure that each variant of a module gets its own intermediates directory to write to. + ModuleSubDir() string + + // Variable creates a new ninja variable scoped to the module. It can be referenced by calls to Rule and Build + // in the same module. + Variable(pctx PackageContext, name, value string) + + // Rule creates a new ninja rule scoped to the module. It can be referenced by calls to Build in the same module. + Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule + + // Build creates a new ninja build statement. + Build(pctx PackageContext, params BuildParams) + + // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, + // but do not exist. It can be used with Context.SetAllowMissingDependencies to allow the primary builder to + // handle missing dependencies on its own instead of having Blueprint treat them as an error. + GetMissingDependencies() []string +} + +var _ BaseModuleContext = (*baseModuleContext)(nil) + +type baseModuleContext struct { + context *Context + config interface{} + module *moduleInfo + errs []error + visitingParent *moduleInfo + visitingDep depInfo + ninjaFileDeps []string +} + +func (d *baseModuleContext) moduleInfo() *moduleInfo { + return d.module +} + +func (d *baseModuleContext) Module() Module { + return d.module.logicModule +} + +func (d *baseModuleContext) ModuleName() string { + return d.module.Name() +} + +func (d *baseModuleContext) ModuleType() string { + return d.module.typeName +} + +func (d *baseModuleContext) ContainsProperty(name string) bool { + _, ok := d.module.propertyPos[name] + return ok +} + +func (d *baseModuleContext) ModuleDir() string { + return filepath.Dir(d.module.relBlueprintsFile) +} + +func (d *baseModuleContext) BlueprintsFile() string { + return d.module.relBlueprintsFile +} + +func (d *baseModuleContext) Config() interface{} { + return d.config +} + +func (d *baseModuleContext) error(err error) { + if err != nil { + d.errs = append(d.errs, err) + } +} + +func (d *baseModuleContext) Errorf(pos scanner.Position, + format string, args ...interface{}) { + + d.error(&BlueprintError{ + Err: fmt.Errorf(format, args...), + Pos: pos, + }) +} + +func (d *baseModuleContext) ModuleErrorf(format string, + args ...interface{}) { + + d.error(&ModuleError{ + BlueprintError: BlueprintError{ + Err: fmt.Errorf(format, args...), + Pos: d.module.pos, + }, + module: d.module, + }) +} + +func (d *baseModuleContext) PropertyErrorf(property, format string, + args ...interface{}) { + + pos := d.module.propertyPos[property] + + if !pos.IsValid() { + pos = d.module.pos + } + + d.error(&PropertyError{ + ModuleError: ModuleError{ + BlueprintError: BlueprintError{ + Err: fmt.Errorf(format, args...), + Pos: pos, + }, + module: d.module, + }, + property: property, + }) +} + +func (d *baseModuleContext) Failed() bool { + return len(d.errs) > 0 +} + +func (d *baseModuleContext) GlobWithDeps(pattern string, + excludes []string) ([]string, error) { + return d.context.glob(pattern, excludes) +} + +func (d *baseModuleContext) Fs() pathtools.FileSystem { + return d.context.fs +} + +func (d *baseModuleContext) Namespace() Namespace { + return d.context.nameInterface.GetNamespace(newNamespaceContext(d.module)) +} + +var _ ModuleContext = (*moduleContext)(nil) + +type moduleContext struct { + baseModuleContext + scope *localScope + actionDefs localBuildActions + handledMissingDeps bool +} + +func (m *baseModuleContext) OtherModuleName(logicModule Module) string { + module := m.context.moduleInfo[logicModule] + return module.Name() +} + +func (m *baseModuleContext) OtherModuleDir(logicModule Module) string { + module := m.context.moduleInfo[logicModule] + return filepath.Dir(module.relBlueprintsFile) +} + +func (m *baseModuleContext) OtherModuleSubDir(logicModule Module) string { + module := m.context.moduleInfo[logicModule] + return module.variant.name +} + +func (m *baseModuleContext) OtherModuleType(logicModule Module) string { + module := m.context.moduleInfo[logicModule] + return module.typeName +} + +func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string, + args ...interface{}) { + + module := m.context.moduleInfo[logicModule] + m.errs = append(m.errs, &ModuleError{ + BlueprintError: BlueprintError{ + Err: fmt.Errorf(format, args...), + Pos: module.pos, + }, + module: module, + }) +} + +func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag { + // fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps + if logicModule == m.visitingDep.module.logicModule { + return m.visitingDep.tag + } + + for _, dep := range m.visitingParent.directDeps { + if dep.module.logicModule == logicModule { + return dep.tag + } + } + + return nil +} + +func (m *baseModuleContext) ModuleFromName(name string) (Module, bool) { + moduleGroup, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace()) + if exists { + if len(moduleGroup.modules) != 1 { + panic(fmt.Errorf("Expected exactly one module named %q, but got %d", name, len(moduleGroup.modules))) + } + moduleInfo := moduleGroup.modules[0].module() + if moduleInfo != nil { + return moduleInfo.logicModule, true + } else { + panic(fmt.Errorf(`Expected actual module named %q, but group did not contain a module. + There may instead be an alias by that name.`, name)) + } + } + return nil, exists +} + +func (m *baseModuleContext) OtherModuleExists(name string) bool { + _, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace()) + return exists +} + +func (m *baseModuleContext) OtherModuleDependencyVariantExists(variations []Variation, name string) bool { + possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) + if possibleDeps == nil { + return false + } + found, _ := findVariant(m.module, possibleDeps, variations, false, false) + return found != nil +} + +func (m *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []Variation, name string) bool { + possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) + if possibleDeps == nil { + return false + } + found, _ := findVariant(m.module, possibleDeps, variations, true, false) + return found != nil +} + +func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { + possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) + if possibleDeps == nil { + return false + } + found, _ := findVariant(m.module, possibleDeps, nil, false, true) + return found != nil +} + +func (m *baseModuleContext) OtherModuleProvider(logicModule Module, provider ProviderKey) interface{} { + module := m.context.moduleInfo[logicModule] + value, _ := m.context.provider(module, provider) + return value +} + +func (m *baseModuleContext) OtherModuleHasProvider(logicModule Module, provider ProviderKey) bool { + module := m.context.moduleInfo[logicModule] + _, ok := m.context.provider(module, provider) + return ok +} + +func (m *baseModuleContext) Provider(provider ProviderKey) interface{} { + value, _ := m.context.provider(m.module, provider) + return value +} + +func (m *baseModuleContext) HasProvider(provider ProviderKey) bool { + _, ok := m.context.provider(m.module, provider) + return ok +} + +func (m *baseModuleContext) SetProvider(provider ProviderKey, value interface{}) { + m.context.setProvider(m.module, provider, value) +} + +func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) { + for _, dep := range m.module.directDeps { + if dep.module.Name() == name { + return dep.module.logicModule, dep.tag + } + } + + return nil, nil +} + +func (m *baseModuleContext) GetDirectDepWithTag(name string, tag DependencyTag) Module { + var deps []depInfo + for _, dep := range m.module.directDeps { + if dep.module.Name() == name { + if dep.tag == tag { + return dep.module.logicModule + } + deps = append(deps, dep) + } + } + + if len(deps) != 0 { + panic(fmt.Errorf("Unable to find dependency %q with requested tag %#v. Found: %#v", deps[0].module, tag, deps)) + } + + return nil +} + +func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) { + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s", + m.module, funcName(visit), m.visitingDep.module)) + } + }() + + m.visitingParent = m.module + + for _, dep := range m.module.directDeps { + m.visitingDep = dep + visit(dep.module.logicModule) + } + + m.visitingParent = nil + m.visitingDep = depInfo{} +} + +func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s", + m.module, funcName(pred), funcName(visit), m.visitingDep.module)) + } + }() + + m.visitingParent = m.module + + for _, dep := range m.module.directDeps { + m.visitingDep = dep + if pred(dep.module.logicModule) { + visit(dep.module.logicModule) + } + } + + m.visitingParent = nil + m.visitingDep = depInfo{} +} + +func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s", + m.module, funcName(visit), m.visitingDep.module)) + } + }() + + m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { + m.visitingParent = parent + m.visitingDep = dep + visit(dep.module.logicModule) + }) + + m.visitingParent = nil + m.visitingDep = depInfo{} +} + +func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, + visit func(Module)) { + + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s", + m.module, funcName(pred), funcName(visit), m.visitingDep.module)) + } + }() + + m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { + if pred(dep.module.logicModule) { + m.visitingParent = parent + m.visitingDep = dep + visit(dep.module.logicModule) + } + }) + + m.visitingParent = nil + m.visitingDep = depInfo{} +} + +func (m *baseModuleContext) WalkDeps(visit func(child, parent Module) bool) { + m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool { + m.visitingParent = parent + m.visitingDep = dep + return visit(dep.module.logicModule, parent.logicModule) + }, nil) + + m.visitingParent = nil + m.visitingDep = depInfo{} +} + +func (m *baseModuleContext) PrimaryModule() Module { + return m.module.group.modules.firstModule().logicModule +} + +func (m *baseModuleContext) FinalModule() Module { + return m.module.group.modules.lastModule().logicModule +} + +func (m *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { + m.context.visitAllModuleVariants(m.module, visit) +} + +func (m *baseModuleContext) AddNinjaFileDeps(deps ...string) { + m.ninjaFileDeps = append(m.ninjaFileDeps, deps...) +} + +func (m *baseModuleContext) ModuleFactories() map[string]ModuleFactory { + ret := make(map[string]ModuleFactory) + for k, v := range m.context.moduleFactories { + ret[k] = v + } + return ret +} + +func (m *moduleContext) ModuleSubDir() string { + return m.module.variant.name +} + +func (m *moduleContext) Variable(pctx PackageContext, name, value string) { + m.scope.ReparentTo(pctx) + + v, err := m.scope.AddLocalVariable(name, value) + if err != nil { + panic(err) + } + + m.actionDefs.variables = append(m.actionDefs.variables, v) +} + +func (m *moduleContext) Rule(pctx PackageContext, name string, + params RuleParams, argNames ...string) Rule { + + m.scope.ReparentTo(pctx) + + r, err := m.scope.AddLocalRule(name, ¶ms, argNames...) + if err != nil { + panic(err) + } + + m.actionDefs.rules = append(m.actionDefs.rules, r) + + return r +} + +func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { + m.scope.ReparentTo(pctx) + + def, err := parseBuildParams(m.scope, ¶ms) + if err != nil { + panic(err) + } + + m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def) +} + +func (m *moduleContext) GetMissingDependencies() []string { + m.handledMissingDeps = true + return m.module.missingDeps +} + +// +// MutatorContext +// + +type mutatorContext struct { + baseModuleContext + name string + reverseDeps []reverseDep + rename []rename + replace []replace + newVariations modulesOrAliases // new variants of existing modules + newModules []*moduleInfo // brand new modules + defaultVariation *string + pauseCh chan<- pauseSpec +} + +type BaseMutatorContext interface { + BaseModuleContext + + // Rename all variants of a module. The new name is not visible to calls to ModuleName, + // AddDependency or OtherModuleName until after this mutator pass is complete. + Rename(name string) + + // MutatorName returns the name that this mutator was registered with. + MutatorName() string +} + +type TopDownMutatorContext interface { + BaseMutatorContext + + // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies + // the specified property structs to it as if the properties were set in a blueprint file. + CreateModule(ModuleFactory, ...interface{}) Module +} + +type BottomUpMutatorContext interface { + BaseMutatorContext + + // AddDependency adds a dependency to the given module. It returns a slice of modules for each + // dependency (some entries may be nil). Does not affect the ordering of the current mutator + // pass, but will be ordered correctly for all future mutator passes. + // + // If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the + // new dependencies have had the current mutator called on them. If the mutator is not + // parallel this method does not affect the ordering of the current mutator pass, but will + // be ordered correctly for all future mutator passes. + AddDependency(module Module, tag DependencyTag, name ...string) []Module + + // AddReverseDependency adds a dependency from the destination to the given module. + // Does not affect the ordering of the current mutator pass, but will be ordered + // correctly for all future mutator passes. All reverse dependencies for a destination module are + // collected until the end of the mutator pass, sorted by name, and then appended to the destination + // module's dependency list. + AddReverseDependency(module Module, tag DependencyTag, name string) + + // CreateVariations splits a module into multiple variants, one for each name in the variationNames + // parameter. It returns a list of new modules in the same order as the variationNames + // list. + // + // If any of the dependencies of the module being operated on were already split + // by calling CreateVariations with the same name, the dependency will automatically + // be updated to point the matching variant. + // + // If a module is split, and then a module depending on the first module is not split + // when the Mutator is later called on it, the dependency of the depending module will + // automatically be updated to point to the first variant. + CreateVariations(variationNames ...string) []Module + + // CreateLocalVariations splits a module into multiple variants, one for each name in the variationNames + // parameter. It returns a list of new modules in the same order as the variantNames + // list. + // + // Local variations do not affect automatic dependency resolution - dependencies added + // to the split module via deps or DynamicDependerModule must exactly match a variant + // that contains all the non-local variations. + CreateLocalVariations(variationNames ...string) []Module + + // SetDependencyVariation sets all dangling dependencies on the current module to point to the variation + // with given name. This function ignores the default variation set by SetDefaultDependencyVariation. + SetDependencyVariation(string) + + // SetDefaultDependencyVariation sets the default variation when a dangling reference is detected + // during the subsequent calls on Create*Variations* functions. To reset, set it to nil. + SetDefaultDependencyVariation(*string) + + // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations + // argument to select which variant of the dependency to use. It returns a slice of modules for + // each dependency (some entries may be nil). A variant of the dependency must exist that matches + // the all of the non-local variations of the current module, plus the variations argument. + // + // If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the + // new dependencies have had the current mutator called on them. If the mutator is not + // parallel this method does not affect the ordering of the current mutator pass, but will + // be ordered correctly for all future mutator passes. + AddVariationDependencies([]Variation, DependencyTag, ...string) []Module + + // AddFarVariationDependencies adds deps as dependencies of the current module, but uses the + // variations argument to select which variant of the dependency to use. It returns a slice of + // modules for each dependency (some entries may be nil). A variant of the dependency must + // exist that matches the variations argument, but may also have other variations. + // For any unspecified variation the first variant will be used. + // + // Unlike AddVariationDependencies, the variations of the current module are ignored - the + // dependency only needs to match the supplied variations. + // + // If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the + // new dependencies have had the current mutator called on them. If the mutator is not + // parallel this method does not affect the ordering of the current mutator pass, but will + // be ordered correctly for all future mutator passes. + AddFarVariationDependencies([]Variation, DependencyTag, ...string) []Module + + // AddInterVariantDependency adds a dependency between two variants of the same module. Variants are always + // ordered in the same order as they were listed in CreateVariations, and AddInterVariantDependency does not change + // that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps, + // WalkDeps, etc. + AddInterVariantDependency(tag DependencyTag, from, to Module) + + // ReplaceDependencies replaces all dependencies on the identical variant of the module with the + // specified name with the current variant of this module. Replacements don't take effect until + // after the mutator pass is finished. + ReplaceDependencies(string) + + // ReplaceDependenciesIf replaces all dependencies on the identical variant of the module with the + // specified name with the current variant of this module as long as the supplied predicate returns + // true. + // + // Replacements don't take effect until after the mutator pass is finished. + ReplaceDependenciesIf(string, ReplaceDependencyPredicate) + + // AliasVariation takes a variationName that was passed to CreateVariations for this module, + // and creates an alias from the current variant (before the mutator has run) to the new + // variant. The alias will be valid until the next time a mutator calls CreateVariations or + // CreateLocalVariations on this module without also calling AliasVariation. The alias can + // be used to add dependencies on the newly created variant using the variant map from + // before CreateVariations was run. + AliasVariation(variationName string) + + // CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this + // module, and creates an alias from a new fromVariationName variant the toVariationName + // variant. The alias will be valid until the next time a mutator calls CreateVariations or + // CreateLocalVariations on this module without also calling AliasVariation. The alias can + // be used to add dependencies on the toVariationName variant using the fromVariationName + // variant. + CreateAliasVariation(fromVariationName, toVariationName string) + + // SetVariationProvider sets the value for a provider for the given newly created variant of + // the current module, i.e. one of the Modules returned by CreateVariations.. It panics if + // not called during the appropriate mutator or GenerateBuildActions pass for the provider, + // if the value is not of the appropriate type, or if the module is not a newly created + // variant of the current module. The value should not be modified after being passed to + // SetVariationProvider. + SetVariationProvider(module Module, provider ProviderKey, value interface{}) +} + +// A Mutator function is called for each Module, and can use +// MutatorContext.CreateVariations to split a Module into multiple Modules, +// modifying properties on the new modules to differentiate them. It is called +// after parsing all Blueprint files, but before generating any build rules, +// and is always called on dependencies before being called on the depending module. +// +// The Mutator function should only modify members of properties structs, and not +// members of the module struct itself, to ensure the modified values are copied +// if a second Mutator chooses to split the module a second time. +type TopDownMutator func(mctx TopDownMutatorContext) +type BottomUpMutator func(mctx BottomUpMutatorContext) + +// DependencyTag is an interface to an arbitrary object that embeds BaseDependencyTag. It can be +// used to transfer information on a dependency between the mutator that called AddDependency +// and the GenerateBuildActions method. Variants created by CreateVariations have a copy of the +// interface (pointing to the same concrete object) from their original module. +type DependencyTag interface { + dependencyTag(DependencyTag) +} + +type BaseDependencyTag struct { +} + +func (BaseDependencyTag) dependencyTag(DependencyTag) { +} + +var _ DependencyTag = BaseDependencyTag{} + +func (mctx *mutatorContext) MutatorName() string { + return mctx.name +} + +func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module { + return mctx.createVariations(variationNames, false) +} + +func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module { + return mctx.createVariations(variationNames, true) +} + +func (mctx *mutatorContext) SetVariationProvider(module Module, provider ProviderKey, value interface{}) { + for _, variant := range mctx.newVariations { + if m := variant.module(); m != nil && m.logicModule == module { + mctx.context.setProvider(m, provider, value) + return + } + } + panic(fmt.Errorf("module %q is not a newly created variant of %q", module, mctx.module)) +} + +func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module { + var ret []Module + modules, errs := mctx.context.createVariations(mctx.module, mctx.name, mctx.defaultVariation, variationNames, local) + if len(errs) > 0 { + mctx.errs = append(mctx.errs, errs...) + } + + for _, module := range modules { + ret = append(ret, module.module().logicModule) + } + + if mctx.newVariations != nil { + panic("module already has variations from this mutator") + } + mctx.newVariations = modules + + if len(ret) != len(variationNames) { + panic("oops!") + } + + return ret +} + +func (mctx *mutatorContext) AliasVariation(variationName string) { + for _, moduleOrAlias := range mctx.module.splitModules { + if alias := moduleOrAlias.alias(); alias != nil { + if alias.variant.variations.equal(mctx.module.variant.variations) { + panic(fmt.Errorf("AliasVariation already called")) + } + } + } + + for _, variant := range mctx.newVariations { + if variant.moduleOrAliasVariant().variations[mctx.name] == variationName { + alias := &moduleAlias{ + variant: mctx.module.variant, + target: variant.moduleOrAliasTarget(), + } + // Prepend the alias so that AddFarVariationDependencies subset match matches + // the alias before matching the first variation. + mctx.module.splitModules = append(modulesOrAliases{alias}, mctx.module.splitModules...) + return + } + } + + var foundVariations []string + for _, variant := range mctx.newVariations { + foundVariations = append(foundVariations, variant.moduleOrAliasVariant().variations[mctx.name]) + } + panic(fmt.Errorf("no %q variation in module variations %q", variationName, foundVariations)) +} + +func (mctx *mutatorContext) CreateAliasVariation(aliasVariationName, targetVariationName string) { + newVariant := newVariant(mctx.module, mctx.name, aliasVariationName, false) + + for _, moduleOrAlias := range mctx.module.splitModules { + if moduleOrAlias.moduleOrAliasVariant().variations.equal(newVariant.variations) { + if alias := moduleOrAlias.alias(); alias != nil { + panic(fmt.Errorf("can't alias %q to %q, already aliased to %q", aliasVariationName, targetVariationName, alias.target.variant.name)) + } else { + panic(fmt.Errorf("can't alias %q to %q, there is already a variant with that name", aliasVariationName, targetVariationName)) + } + } + } + + for _, variant := range mctx.newVariations { + if variant.moduleOrAliasVariant().variations[mctx.name] == targetVariationName { + // Append the alias here so that it comes after any aliases created by AliasVariation. + mctx.module.splitModules = append(mctx.module.splitModules, &moduleAlias{ + variant: newVariant, + target: variant.moduleOrAliasTarget(), + }) + return + } + } + + var foundVariations []string + for _, variant := range mctx.newVariations { + foundVariations = append(foundVariations, variant.moduleOrAliasVariant().variations[mctx.name]) + } + panic(fmt.Errorf("no %q variation in module variations %q", targetVariationName, foundVariations)) +} + +func (mctx *mutatorContext) SetDependencyVariation(variationName string) { + mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName, nil) +} + +func (mctx *mutatorContext) SetDefaultDependencyVariation(variationName *string) { + mctx.defaultVariation = variationName +} + +func (mctx *mutatorContext) Module() Module { + return mctx.module.logicModule +} + +func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) []Module { + depInfos := make([]Module, 0, len(deps)) + for _, dep := range deps { + modInfo := mctx.context.moduleInfo[module] + depInfo, errs := mctx.context.addDependency(modInfo, tag, dep) + if len(errs) > 0 { + mctx.errs = append(mctx.errs, errs...) + } + if !mctx.pause(depInfo) { + // Pausing not supported by this mutator, new dependencies can't be returned. + depInfo = nil + } + depInfos = append(depInfos, maybeLogicModule(depInfo)) + } + return depInfos +} + +func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, destName string) { + if _, ok := tag.(BaseDependencyTag); ok { + panic("BaseDependencyTag is not allowed to be used directly!") + } + + destModule, errs := mctx.context.findReverseDependency(mctx.context.moduleInfo[module], destName) + if len(errs) > 0 { + mctx.errs = append(mctx.errs, errs...) + return + } + + mctx.reverseDeps = append(mctx.reverseDeps, reverseDep{ + destModule, + depInfo{mctx.context.moduleInfo[module], tag}, + }) +} + +func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag DependencyTag, + deps ...string) []Module { + + depInfos := make([]Module, 0, len(deps)) + for _, dep := range deps { + depInfo, errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, false) + if len(errs) > 0 { + mctx.errs = append(mctx.errs, errs...) + } + if !mctx.pause(depInfo) { + // Pausing not supported by this mutator, new dependencies can't be returned. + depInfo = nil + } + depInfos = append(depInfos, maybeLogicModule(depInfo)) + } + return depInfos +} + +func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, tag DependencyTag, + deps ...string) []Module { + + depInfos := make([]Module, 0, len(deps)) + for _, dep := range deps { + depInfo, errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, true) + if len(errs) > 0 { + mctx.errs = append(mctx.errs, errs...) + } + if !mctx.pause(depInfo) { + // Pausing not supported by this mutator, new dependencies can't be returned. + depInfo = nil + } + depInfos = append(depInfos, maybeLogicModule(depInfo)) + } + return depInfos +} + +func (mctx *mutatorContext) AddInterVariantDependency(tag DependencyTag, from, to Module) { + mctx.context.addInterVariantDependency(mctx.module, tag, from, to) +} + +func (mctx *mutatorContext) ReplaceDependencies(name string) { + mctx.ReplaceDependenciesIf(name, nil) +} + +type ReplaceDependencyPredicate func(from Module, tag DependencyTag, to Module) bool + +func (mctx *mutatorContext) ReplaceDependenciesIf(name string, predicate ReplaceDependencyPredicate) { + target := mctx.context.moduleMatchingVariant(mctx.module, name) + + if target == nil { + panic(fmt.Errorf("ReplaceDependencies could not find identical variant {%s} for module %s\n"+ + "available variants:\n %s", + mctx.context.prettyPrintVariant(mctx.module.variant.variations), + name, + mctx.context.prettyPrintGroupVariants(mctx.context.moduleGroupFromName(name, mctx.module.namespace())))) + } + + mctx.replace = append(mctx.replace, replace{target, mctx.module, predicate}) +} + +func (mctx *mutatorContext) Rename(name string) { + mctx.rename = append(mctx.rename, rename{mctx.module.group, name}) +} + +func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { + module := newModule(factory) + + module.relBlueprintsFile = mctx.module.relBlueprintsFile + module.pos = mctx.module.pos + module.propertyPos = mctx.module.propertyPos + module.createdBy = mctx.module + + for _, p := range props { + err := proptools.AppendMatchingProperties(module.properties, p, nil) + if err != nil { + panic(err) + } + } + + mctx.newModules = append(mctx.newModules, module) + + return module.logicModule +} + +// pause waits until the given dependency has been visited by the mutator's parallelVisit call. +// It returns true if the pause was supported, false if the pause was not supported and did not +// occur, which will happen when the mutator is not parallelizable. If the dependency is nil +// it returns true if pausing is supported or false if it is not. +func (mctx *mutatorContext) pause(dep *moduleInfo) bool { + if mctx.pauseCh != nil { + if dep != nil { + unpause := make(unpause) + mctx.pauseCh <- pauseSpec{ + paused: mctx.module, + until: dep, + unpause: unpause, + } + <-unpause + } + return true + } + return false +} + +// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property +// called "name". Modules that embed it must also add SimpleName.Properties to their property +// structure list. +type SimpleName struct { + Properties struct { + Name string + } +} + +func (s *SimpleName) Name() string { + return s.Properties.Name +} + +// Load Hooks + +type LoadHookContext interface { + EarlyModuleContext + + // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies + // the specified property structs to it as if the properties were set in a blueprint file. + CreateModule(ModuleFactory, string, ...interface{}) Module + + // RegisterScopedModuleType creates a new module type that is scoped to the current Blueprints + // file. + RegisterScopedModuleType(name string, factory ModuleFactory) +} + +func (l *loadHookContext) CreateModule(factory ModuleFactory, typeName string, props ...interface{}) Module { + module := newModule(factory) + + module.relBlueprintsFile = l.module.relBlueprintsFile + module.pos = l.module.pos + module.propertyPos = l.module.propertyPos + module.createdBy = l.module + module.typeName = typeName + + for _, p := range props { + err := proptools.AppendMatchingProperties(module.properties, p, nil) + if err != nil { + panic(err) + } + } + + l.newModules = append(l.newModules, module) + + return module.logicModule +} + +func (l *loadHookContext) RegisterScopedModuleType(name string, factory ModuleFactory) { + if _, exists := l.context.moduleFactories[name]; exists { + panic(fmt.Errorf("A global module type named %q already exists", name)) + } + + if _, exists := (*l.scopedModuleFactories)[name]; exists { + panic(fmt.Errorf("A module type named %q already exists in this scope", name)) + } + + if *l.scopedModuleFactories == nil { + *l.scopedModuleFactories = make(map[string]ModuleFactory) + } + + (*l.scopedModuleFactories)[name] = factory +} + +type loadHookContext struct { + baseModuleContext + newModules []*moduleInfo + scopedModuleFactories *map[string]ModuleFactory +} + +type LoadHook func(ctx LoadHookContext) + +// Load hooks need to be added by module factories, which don't have any parameter to get to the +// Context, and only produce a Module interface with no base implementation, so the load hooks +// must be stored in a global map. The key is a pointer allocated by the module factory, so there +// is no chance of collisions even if tests are running in parallel with multiple contexts. The +// contents should be short-lived, they are added during a module factory and removed immediately +// after the module factory returns. +var pendingHooks sync.Map + +func AddLoadHook(module Module, hook LoadHook) { + // Only one goroutine can be processing a given module, so no additional locking is required + // for the slice stored in the sync.Map. + v, exists := pendingHooks.Load(module) + if !exists { + v, _ = pendingHooks.LoadOrStore(module, new([]LoadHook)) + } + hooks := v.(*[]LoadHook) + *hooks = append(*hooks, hook) +} + +func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo, + scopedModuleFactories *map[string]ModuleFactory) (newModules []*moduleInfo, deps []string, errs []error) { + + if v, exists := pendingHooks.Load(module.logicModule); exists { + hooks := v.(*[]LoadHook) + + for _, hook := range *hooks { + mctx := &loadHookContext{ + baseModuleContext: baseModuleContext{ + context: ctx, + config: config, + module: module, + }, + scopedModuleFactories: scopedModuleFactories, + } + hook(mctx) + newModules = append(newModules, mctx.newModules...) + deps = append(deps, mctx.ninjaFileDeps...) + errs = append(errs, mctx.errs...) + } + pendingHooks.Delete(module.logicModule) + + return newModules, deps, errs + } + + return nil, nil, nil +} + +// Check the syntax of a generated blueprint file. +// +// This is intended to perform a quick syntactic check for generated blueprint +// code, where syntactically correct means: +// * No variable definitions. +// * Valid module types. +// * Valid property names. +// * Valid values for the property type. +// +// It does not perform any semantic checking of properties, existence of referenced +// files, or dependencies. +// +// At a low level it: +// * Parses the contents. +// * Invokes relevant factory to create Module instances. +// * Unpacks the properties into the Module. +// * Does not invoke load hooks or any mutators. +// +// The filename is only used for reporting errors. +func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error { + scope := parser.NewScope(nil) + file, errs := parser.Parse(filename, strings.NewReader(contents), scope) + if len(errs) != 0 { + return errs + } + + for _, def := range file.Defs { + switch def := def.(type) { + case *parser.Module: + _, moduleErrs := processModuleDef(def, filename, moduleFactories, nil, false) + errs = append(errs, moduleErrs...) + + default: + panic(fmt.Errorf("unknown definition type: %T", def)) + } + } + + return errs +} + +func maybeLogicModule(module *moduleInfo) Module { + if module != nil { + return module.logicModule + } else { + return nil + } +} diff --git a/blueprint/module_ctx_test.go b/blueprint/module_ctx_test.go new file mode 100644 index 0000000..af23be7 --- /dev/null +++ b/blueprint/module_ctx_test.go @@ -0,0 +1,633 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "reflect" + "strings" + "testing" +) + +type moduleCtxTestModule struct { + SimpleName +} + +func newModuleCtxTestModule() (Module, []interface{}) { + m := &moduleCtxTestModule{} + return m, []interface{}{&m.SimpleName.Properties} +} + +func (f *moduleCtxTestModule) GenerateBuildActions(ModuleContext) { +} + +func noAliasMutator(name string) func(ctx BottomUpMutatorContext) { + return func(ctx BottomUpMutatorContext) { + if ctx.ModuleName() == name { + ctx.CreateVariations("a", "b") + } + } +} + +func aliasMutator(name string) func(ctx BottomUpMutatorContext) { + return func(ctx BottomUpMutatorContext) { + if ctx.ModuleName() == name { + ctx.CreateVariations("a", "b") + ctx.AliasVariation("b") + } + } +} + +func createAliasMutator(name string) func(ctx BottomUpMutatorContext) { + return func(ctx BottomUpMutatorContext) { + if ctx.ModuleName() == name { + ctx.CreateVariations("a", "b") + ctx.CreateAliasVariation("c", "a") + ctx.CreateAliasVariation("d", "b") + ctx.CreateAliasVariation("e", "a") + } + } +} + +func addVariantDepsMutator(variants []Variation, tag DependencyTag, from, to string) func(ctx BottomUpMutatorContext) { + return func(ctx BottomUpMutatorContext) { + if ctx.ModuleName() == from { + ctx.AddVariationDependencies(variants, tag, to) + } + } +} + +func addVariantDepsResultMutator(variants []Variation, tag DependencyTag, from, to string, results map[string][]Module) func(ctx BottomUpMutatorContext) { + return func(ctx BottomUpMutatorContext) { + if ctx.ModuleName() == from { + ret := ctx.AddVariationDependencies(variants, tag, to) + results[ctx.ModuleName()] = ret + } + } +} + +func TestAliasVariation(t *testing.T) { + runWithFailures := func(ctx *Context, expectedErr string) { + t.Helper() + bp := ` + test { + name: "foo", + } + + test { + name: "bar", + } + ` + + mockFS := map[string][]byte{ + "Android.bp": []byte(bp), + } + + ctx.MockFileSystem(mockFS) + + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}, nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + if expectedErr == "" { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + } else { + for _, err := range errs { + if strings.Contains(err.Error(), expectedErr) { + continue + } else { + t.Errorf("unexpected dep error: %s", err) + } + } + } + } else if expectedErr != "" { + t.Errorf("missing dep error: %s", expectedErr) + } + } + + run := func(ctx *Context) { + t.Helper() + runWithFailures(ctx, "") + } + + t.Run("simple", func(t *testing.T) { + // Creates a module "bar" with variants "a" and "b" and alias "" -> "b". + // Tests a dependency from "foo" to "bar" variant "b" through alias "". + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", addVariantDepsMutator(nil, nil, "foo", "bar")) + + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + barB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b") + + if g, w := foo.forwardDeps, []*moduleInfo{barB}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + }) + + t.Run("chained", func(t *testing.T) { + // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b", + // "a" -> "a_b", and "b" -> "b_b". + // Tests a dependency from "foo" to "bar" variant "b_b" through alias "". + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar")) + + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + barBB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b_b") + + if g, w := foo.forwardDeps, []*moduleInfo{barBB}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + }) + + t.Run("chained2", func(t *testing.T) { + // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b", + // "a" -> "a_b", and "b" -> "b_b". + // Tests a dependency from "foo" to "bar" variant "a_b" through alias "a". + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "a"}}, nil, "foo", "bar")) + + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + barAB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("a_b") + + if g, w := foo.forwardDeps, []*moduleInfo{barAB}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + }) + + t.Run("removed dangling alias", func(t *testing.T) { + // Creates a module "bar" with variants "a" and "b" and aliases "" -> "b", then splits the variants into + // "a_a", "a_b", "b_a" and "b_b" without creating new aliases. + // Tests a dependency from "foo" to removed "bar" alias "" fails. + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", noAliasMutator("bar")) + ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar")) + + runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n \n"+ + "available variants:"+ + "\n 1:a,2:a\n 1:a,2:b\n 1:b,2:a\n 1:b,2:b") + }) +} + +func TestCreateAliasVariations(t *testing.T) { + runWithFailures := func(ctx *Context, expectedErr string) { + t.Helper() + bp := ` + test { + name: "foo", + } + + test { + name: "bar", + } + ` + + mockFS := map[string][]byte{ + "Android.bp": []byte(bp), + } + + ctx.MockFileSystem(mockFS) + + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}, nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + if expectedErr == "" { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + } else { + for _, err := range errs { + if strings.Contains(err.Error(), expectedErr) { + continue + } else { + t.Errorf("unexpected dep error: %s", err) + } + } + } + } else if expectedErr != "" { + t.Errorf("missing dep error: %s", expectedErr) + } + } + + run := func(ctx *Context) { + t.Helper() + runWithFailures(ctx, "") + } + + t.Run("simple", func(t *testing.T) { + // Creates a module "bar" with variants "a" and "b" and alias "c" -> "a", "d" -> "b", and "e" -> "a". + // Tests a dependency from "foo" to "bar" variant "b" through alias "d". + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar")) + + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + barB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b") + + if g, w := foo.forwardDeps, []*moduleInfo{barB}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + }) + + t.Run("chained", func(t *testing.T) { + // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "c" -> "a_b", + // "d" -> "b_b", and "d" -> "a_b". + // Tests a dependency from "foo" to "bar" variant "b_b" through alias "d". + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", aliasMutator("bar")) + ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar")) + + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + barBB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b_b") + + if g, w := foo.forwardDeps, []*moduleInfo{barBB}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + }) + + t.Run("removed dangling alias", func(t *testing.T) { + // Creates a module "bar" with variants "a" and "b" and alias "c" -> "a", "d" -> "b", and "e" -> "a", + // then splits the variants into "a_a", "a_b", "b_a" and "b_b" without creating new aliases. + // Tests a dependency from "foo" to removed "bar" alias "d" fails. + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) + ctx.RegisterBottomUpMutator("2", noAliasMutator("bar")) + ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar")) + + runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n 1:d\n"+ + "available variants:"+ + "\n 1:a,2:a\n 1:a,2:b\n 1:b,2:a\n 1:b,2:b") + }) +} + +func expectedErrors(t *testing.T, errs []error, expectedMessages ...string) { + t.Helper() + if len(errs) != len(expectedMessages) { + t.Errorf("expected %d error, found: %q", len(expectedMessages), errs) + } else { + for i, expected := range expectedMessages { + err := errs[i] + if err.Error() != expected { + t.Errorf("expected error %q found %q", expected, err) + } + } + } +} + +func TestAddVariationDependencies(t *testing.T) { + runWithFailures := func(ctx *Context, expectedErr string) { + t.Helper() + bp := ` + test { + name: "foo", + } + + test { + name: "bar", + } + ` + + mockFS := map[string][]byte{ + "Android.bp": []byte(bp), + } + + ctx.MockFileSystem(mockFS) + + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}, nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + if expectedErr == "" { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + } else { + for _, err := range errs { + if strings.Contains(err.Error(), expectedErr) { + continue + } else { + t.Errorf("unexpected dep error: %s", err) + } + } + } + } else if expectedErr != "" { + t.Errorf("missing dep error: %s", expectedErr) + } + } + + run := func(ctx *Context) { + t.Helper() + runWithFailures(ctx, "") + } + + t.Run("parallel", func(t *testing.T) { + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + results := make(map[string][]Module) + depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "bar", results) + ctx.RegisterBottomUpMutator("deps", depsMutator).Parallel() + + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + bar := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("") + + if g, w := foo.forwardDeps, []*moduleInfo{bar}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + + if g, w := results["foo"], []Module{bar.logicModule}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) + } + }) + + t.Run("non-parallel", func(t *testing.T) { + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + results := make(map[string][]Module) + depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "bar", results) + ctx.RegisterBottomUpMutator("deps", depsMutator) + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + bar := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("") + + if g, w := foo.forwardDeps, []*moduleInfo{bar}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + + if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) + } + }) + + t.Run("missing", func(t *testing.T) { + ctx := NewContext() + ctx.RegisterModuleType("test", newModuleCtxTestModule) + results := make(map[string][]Module) + depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "baz", results) + ctx.RegisterBottomUpMutator("deps", depsMutator).Parallel() + runWithFailures(ctx, `"foo" depends on undefined module "baz"`) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + + if g, w := foo.forwardDeps, []*moduleInfo(nil); !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + + if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) + } + }) + + t.Run("allow missing", func(t *testing.T) { + ctx := NewContext() + ctx.SetAllowMissingDependencies(true) + ctx.RegisterModuleType("test", newModuleCtxTestModule) + results := make(map[string][]Module) + depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "baz", results) + ctx.RegisterBottomUpMutator("deps", depsMutator).Parallel() + run(ctx) + + foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") + + if g, w := foo.forwardDeps, []*moduleInfo(nil); !reflect.DeepEqual(g, w) { + t.Fatalf("expected foo deps to be %q, got %q", w, g) + } + + if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { + t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) + } + }) + +} + +func TestCheckBlueprintSyntax(t *testing.T) { + factories := map[string]ModuleFactory{ + "test": newModuleCtxTestModule, + } + + t.Run("valid", func(t *testing.T) { + errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` +test { + name: "test", +} +`) + expectedErrors(t, errs) + }) + + t.Run("syntax error", func(t *testing.T) { + errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` +test { + name: "test", + +`) + + expectedErrors(t, errs, `path/Blueprint:5:1: expected "}", found EOF`) + }) + + t.Run("unknown module type", func(t *testing.T) { + errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` +test2 { + name: "test", +} +`) + + expectedErrors(t, errs, `path/Blueprint:2:1: unrecognized module type "test2"`) + }) + + t.Run("unknown property name", func(t *testing.T) { + errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` +test { + nam: "test", +} +`) + + expectedErrors(t, errs, `path/Blueprint:3:5: unrecognized property "nam"`) + }) + + t.Run("invalid property type", func(t *testing.T) { + errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` +test { + name: false, +} +`) + + expectedErrors(t, errs, `path/Blueprint:3:8: can't assign bool value to string property "name"`) + }) + + t.Run("multiple failures", func(t *testing.T) { + errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` +test { + name: false, +} + +test2 { + name: false, +} +`) + + expectedErrors(t, errs, + `path/Blueprint:3:8: can't assign bool value to string property "name"`, + `path/Blueprint:6:1: unrecognized module type "test2"`, + ) + }) +} + +type addNinjaDepsTestModule struct { + SimpleName +} + +func addNinjaDepsTestModuleFactory() (Module, []interface{}) { + module := &addNinjaDepsTestModule{} + AddLoadHook(module, func(ctx LoadHookContext) { + ctx.AddNinjaFileDeps("LoadHookContext") + }) + return module, []interface{}{&module.SimpleName.Properties} +} + +func (m *addNinjaDepsTestModule) GenerateBuildActions(ctx ModuleContext) { + ctx.AddNinjaFileDeps("GenerateBuildActions") +} + +func addNinjaDepsTestBottomUpMutator(ctx BottomUpMutatorContext) { + ctx.AddNinjaFileDeps("BottomUpMutator") +} + +func addNinjaDepsTestTopDownMutator(ctx TopDownMutatorContext) { + ctx.AddNinjaFileDeps("TopDownMutator") +} + +type addNinjaDepsTestPreSingleton struct{} + +func addNinjaDepsTestPreSingletonFactory() Singleton { + return &addNinjaDepsTestPreSingleton{} +} + +func (s *addNinjaDepsTestPreSingleton) GenerateBuildActions(ctx SingletonContext) { + ctx.AddNinjaFileDeps("PreSingleton") +} + +type addNinjaDepsTestSingleton struct{} + +func addNinjaDepsTestSingletonFactory() Singleton { + return &addNinjaDepsTestSingleton{} +} + +func (s *addNinjaDepsTestSingleton) GenerateBuildActions(ctx SingletonContext) { + ctx.AddNinjaFileDeps("Singleton") +} + +func TestAddNinjaFileDeps(t *testing.T) { + ctx := NewContext() + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + test { + name: "test", + } + `), + }) + + ctx.RegisterModuleType("test", addNinjaDepsTestModuleFactory) + ctx.RegisterBottomUpMutator("testBottomUpMutator", addNinjaDepsTestBottomUpMutator) + ctx.RegisterTopDownMutator("testTopDownMutator", addNinjaDepsTestTopDownMutator) + ctx.RegisterPreSingletonType("testPreSingleton", addNinjaDepsTestPreSingletonFactory) + ctx.RegisterSingletonType("testSingleton", addNinjaDepsTestSingletonFactory) + parseDeps, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + resolveDeps, errs := ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + prepareDeps, errs := ctx.PrepareBuildActions(nil) + if len(errs) > 0 { + t.Errorf("unexpected prepare errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + if g, w := parseDeps, []string{"Android.bp", "LoadHookContext"}; !reflect.DeepEqual(g, w) { + t.Errorf("ParseBlueprintsFiles: wanted deps %q, got %q", w, g) + } + + if g, w := resolveDeps, []string{"PreSingleton", "BottomUpMutator", "TopDownMutator"}; !reflect.DeepEqual(g, w) { + t.Errorf("ResolveDependencies: wanted deps %q, got %q", w, g) + } + + if g, w := prepareDeps, []string{"GenerateBuildActions", "Singleton"}; !reflect.DeepEqual(g, w) { + t.Errorf("PrepareBuildActions: wanted deps %q, got %q", w, g) + } + +} diff --git a/blueprint/name_interface.go b/blueprint/name_interface.go new file mode 100644 index 0000000..5e7e16e --- /dev/null +++ b/blueprint/name_interface.go @@ -0,0 +1,180 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "sort" +) + +// This file exposes the logic of locating a module via a query string, to enable +// other projects to override it if desired. +// The default name resolution implementation, SimpleNameInterface, +// just treats the query string as a module name, and does a simple map lookup. + +// A ModuleGroup just points to a moduleGroup to allow external packages to refer +// to a moduleGroup but not use it +type ModuleGroup struct { + *moduleGroup +} + +func (h *ModuleGroup) String() string { + return h.moduleGroup.name +} + +// The Namespace interface is just a marker interface for usage by the NameInterface, +// to allow a NameInterface to specify that a certain parameter should be a Namespace. +// In practice, a specific NameInterface will expect to only give and receive structs of +// the same concrete type, but because Go doesn't support generics, we use a marker interface +// for a little bit of clarity, and expect implementers to do typecasting instead. +type Namespace interface { + namespace(Namespace) +} +type NamespaceMarker struct { +} + +func (m *NamespaceMarker) namespace(Namespace) { +} + +// A NameInterface tells how to locate modules by name. +// There should only be one name interface per Context, but potentially many namespaces +type NameInterface interface { + // Gets called when a new module is created + NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) + + // Finds the module with the given name + ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) + + // Returns an error indicating that the given module could not be found. + // The error contains some diagnostic information about where the dependency can be found. + MissingDependencyError(depender string, dependerNamespace Namespace, depName string) (err error) + + // Rename + Rename(oldName string, newName string, namespace Namespace) []error + + // Returns all modules in a deterministic order. + AllModules() []ModuleGroup + + // gets the namespace for a given path + GetNamespace(ctx NamespaceContext) (namespace Namespace) + + // returns a deterministic, unique, arbitrary string for the given name in the given namespace + UniqueName(ctx NamespaceContext, name string) (unique string) +} + +// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface +// to choose the namespace for any given module +type NamespaceContext interface { + ModulePath() string +} + +type namespaceContextImpl struct { + modulePath string +} + +func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) { + return &namespaceContextImpl{moduleInfo.pos.Filename} +} + +func (ctx *namespaceContextImpl) ModulePath() string { + return ctx.modulePath +} + +// a SimpleNameInterface just stores all modules in a map based on name +type SimpleNameInterface struct { + modules map[string]ModuleGroup +} + +func NewSimpleNameInterface() *SimpleNameInterface { + return &SimpleNameInterface{ + modules: make(map[string]ModuleGroup), + } +} + +func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) { + name := group.name + if group, present := s.modules[name]; present { + return nil, []error{ + // seven characters at the start of the second line to align with the string "error: " + fmt.Errorf("module %q already defined\n"+ + " %s <-- previous definition here", name, group.modules.firstModule().pos), + } + } + + s.modules[name] = group + + return nil, []error{} +} + +func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) { + group, found = s.modules[moduleName] + return group, found +} + +func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) { + existingGroup, exists := s.modules[newName] + if exists { + return []error{ + // seven characters at the start of the second line to align with the string "error: " + fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+ + " %s <-- existing module defined here", + oldName, newName, existingGroup.modules.firstModule().pos), + } + } + + group, exists := s.modules[oldName] + if !exists { + return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)} + } + s.modules[newName] = group + delete(s.modules, group.name) + group.name = newName + return nil +} + +func (s *SimpleNameInterface) AllModules() []ModuleGroup { + groups := make([]ModuleGroup, 0, len(s.modules)) + for _, group := range s.modules { + groups = append(groups, group) + } + + duplicateName := "" + less := func(i, j int) bool { + if groups[i].name == groups[j].name { + duplicateName = groups[i].name + } + return groups[i].name < groups[j].name + } + sort.Slice(groups, less) + if duplicateName != "" { + // It is permitted to have two moduleGroup's with the same name, but not within the same + // Namespace. The SimpleNameInterface should catch this in NewModule, however, so this + // should never happen. + panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName)) + } + return groups +} + +func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string) (err error) { + return fmt.Errorf("%q depends on undefined module %q", depender, dependency) +} + +func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace { + return nil +} + +func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) { + return name +} diff --git a/blueprint/ninja_defs.go b/blueprint/ninja_defs.go new file mode 100644 index 0000000..69233c2 --- /dev/null +++ b/blueprint/ninja_defs.go @@ -0,0 +1,462 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "errors" + "fmt" + "sort" + "strconv" + "strings" +) + +// A Deps value indicates the dependency file format that Ninja should expect to +// be output by a compiler. +type Deps int + +const ( + DepsNone Deps = iota + DepsGCC + DepsMSVC +) + +func (d Deps) String() string { + switch d { + case DepsNone: + return "none" + case DepsGCC: + return "gcc" + case DepsMSVC: + return "msvc" + default: + panic(fmt.Sprintf("unknown deps value: %d", d)) + } +} + +// A PoolParams object contains the set of parameters that make up a Ninja pool +// definition. +type PoolParams struct { + Comment string // The comment that will appear above the definition. + Depth int // The Ninja pool depth. +} + +// A RuleParams object contains the set of parameters that make up a Ninja rule +// definition. +type RuleParams struct { + // These fields correspond to a Ninja variable of the same name. + Command string // The command that Ninja will run for the rule. + Depfile string // The dependency file name. + Deps Deps // The format of the dependency file. + Description string // The description that Ninja will print for the rule. + Generator bool // Whether the rule generates the Ninja manifest file. + Pool Pool // The Ninja pool to which the rule belongs. + Restat bool // Whether Ninja should re-stat the rule's outputs. + Rspfile string // The response file. + RspfileContent string // The response file content. + SymlinkOutputs []string // The list of Outputs or ImplicitOutputs that are symlinks. + + // These fields are used internally in Blueprint + CommandDeps []string // Command-specific implicit dependencies to prepend to builds + CommandOrderOnly []string // Command-specific order-only dependencies to prepend to builds + Comment string // The comment that will appear above the definition. +} + +// A BuildParams object contains the set of parameters that make up a Ninja +// build statement. Each field except for Args corresponds with a part of the +// Ninja build statement. The Args field contains variable names and values +// that are set within the build statement's scope in the Ninja file. +type BuildParams struct { + Comment string // The comment that will appear above the definition. + Depfile string // The dependency file name. + Deps Deps // The format of the dependency file. + Description string // The description that Ninja will print for the build. + Rule Rule // The rule to invoke. + Outputs []string // The list of explicit output targets. + ImplicitOutputs []string // The list of implicit output targets. + SymlinkOutputs []string // The list of Outputs or ImplicitOutputs that are symlinks. + Inputs []string // The list of explicit input dependencies. + Implicits []string // The list of implicit input dependencies. + OrderOnly []string // The list of order-only dependencies. + Validations []string // The list of validations to run when this rule runs. + Args map[string]string // The variable/value pairs to set. + Optional bool // Skip outputting a default statement +} + +// A poolDef describes a pool definition. It does not include the name of the +// pool. +type poolDef struct { + Comment string + Depth int +} + +func parsePoolParams(scope scope, params *PoolParams) (*poolDef, + error) { + + def := &poolDef{ + Comment: params.Comment, + Depth: params.Depth, + } + + return def, nil +} + +func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error { + if p.Comment != "" { + err := nw.Comment(p.Comment) + if err != nil { + return err + } + } + + err := nw.Pool(name) + if err != nil { + return err + } + + return nw.ScopedAssign("depth", strconv.Itoa(p.Depth)) +} + +// A ruleDef describes a rule definition. It does not include the name of the +// rule. +type ruleDef struct { + CommandDeps []ninjaString + CommandOrderOnly []ninjaString + Comment string + Pool Pool + Variables map[string]ninjaString +} + +func parseRuleParams(scope scope, params *RuleParams) (*ruleDef, + error) { + + r := &ruleDef{ + Comment: params.Comment, + Pool: params.Pool, + Variables: make(map[string]ninjaString), + } + + if params.Command == "" { + return nil, fmt.Errorf("encountered rule params with no command " + + "specified") + } + + if r.Pool != nil && !scope.IsPoolVisible(r.Pool) { + return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool) + } + + value, err := parseNinjaString(scope, params.Command) + if err != nil { + return nil, fmt.Errorf("error parsing Command param: %s", err) + } + r.Variables["command"] = value + + if params.Depfile != "" { + value, err = parseNinjaString(scope, params.Depfile) + if err != nil { + return nil, fmt.Errorf("error parsing Depfile param: %s", err) + } + r.Variables["depfile"] = value + } + + if params.Deps != DepsNone { + r.Variables["deps"] = simpleNinjaString(params.Deps.String()) + } + + if params.Description != "" { + value, err = parseNinjaString(scope, params.Description) + if err != nil { + return nil, fmt.Errorf("error parsing Description param: %s", err) + } + r.Variables["description"] = value + } + + if params.Generator { + r.Variables["generator"] = simpleNinjaString("true") + } + + if params.Restat { + r.Variables["restat"] = simpleNinjaString("true") + } + + if params.Rspfile != "" { + value, err = parseNinjaString(scope, params.Rspfile) + if err != nil { + return nil, fmt.Errorf("error parsing Rspfile param: %s", err) + } + r.Variables["rspfile"] = value + } + + if params.RspfileContent != "" { + value, err = parseNinjaString(scope, params.RspfileContent) + if err != nil { + return nil, fmt.Errorf("error parsing RspfileContent param: %s", + err) + } + r.Variables["rspfile_content"] = value + } + + if len(params.SymlinkOutputs) > 0 { + value, err = parseNinjaString(scope, strings.Join(params.SymlinkOutputs, " ")) + if err != nil { + return nil, fmt.Errorf("error parsing SymlinkOutputs param: %s", + err) + } + r.Variables["symlink_outputs"] = value + } + + r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps) + if err != nil { + return nil, fmt.Errorf("error parsing CommandDeps param: %s", err) + } + + r.CommandOrderOnly, err = parseNinjaStrings(scope, params.CommandOrderOnly) + if err != nil { + return nil, fmt.Errorf("error parsing CommandDeps param: %s", err) + } + + return r, nil +} + +func (r *ruleDef) WriteTo(nw *ninjaWriter, name string, + pkgNames map[*packageContext]string) error { + + if r.Comment != "" { + err := nw.Comment(r.Comment) + if err != nil { + return err + } + } + + err := nw.Rule(name) + if err != nil { + return err + } + + if r.Pool != nil { + err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames)) + if err != nil { + return err + } + } + + err = writeVariables(nw, r.Variables, pkgNames) + if err != nil { + return err + } + + return nil +} + +// A buildDef describes a build target definition. +type buildDef struct { + Comment string + Rule Rule + RuleDef *ruleDef + Outputs []ninjaString + ImplicitOutputs []ninjaString + Inputs []ninjaString + Implicits []ninjaString + OrderOnly []ninjaString + Validations []ninjaString + Args map[Variable]ninjaString + Variables map[string]ninjaString + Optional bool +} + +func parseBuildParams(scope scope, params *BuildParams) (*buildDef, + error) { + + comment := params.Comment + rule := params.Rule + + b := &buildDef{ + Comment: comment, + Rule: rule, + } + + setVariable := func(name string, value ninjaString) { + if b.Variables == nil { + b.Variables = make(map[string]ninjaString) + } + b.Variables[name] = value + } + + if !scope.IsRuleVisible(rule) { + return nil, fmt.Errorf("Rule %s is not visible in this scope", rule) + } + + if len(params.Outputs) == 0 { + return nil, errors.New("Outputs param has no elements") + } + + var err error + b.Outputs, err = parseNinjaStrings(scope, params.Outputs) + if err != nil { + return nil, fmt.Errorf("error parsing Outputs param: %s", err) + } + + b.ImplicitOutputs, err = parseNinjaStrings(scope, params.ImplicitOutputs) + if err != nil { + return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err) + } + + b.Inputs, err = parseNinjaStrings(scope, params.Inputs) + if err != nil { + return nil, fmt.Errorf("error parsing Inputs param: %s", err) + } + + b.Implicits, err = parseNinjaStrings(scope, params.Implicits) + if err != nil { + return nil, fmt.Errorf("error parsing Implicits param: %s", err) + } + + b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly) + if err != nil { + return nil, fmt.Errorf("error parsing OrderOnly param: %s", err) + } + + b.Validations, err = parseNinjaStrings(scope, params.Validations) + if err != nil { + return nil, fmt.Errorf("error parsing Validations param: %s", err) + } + + b.Optional = params.Optional + + if params.Depfile != "" { + value, err := parseNinjaString(scope, params.Depfile) + if err != nil { + return nil, fmt.Errorf("error parsing Depfile param: %s", err) + } + setVariable("depfile", value) + } + + if params.Deps != DepsNone { + setVariable("deps", simpleNinjaString(params.Deps.String())) + } + + if params.Description != "" { + value, err := parseNinjaString(scope, params.Description) + if err != nil { + return nil, fmt.Errorf("error parsing Description param: %s", err) + } + setVariable("description", value) + } + + if len(params.SymlinkOutputs) > 0 { + setVariable( + "symlink_outputs", + simpleNinjaString(strings.Join(params.SymlinkOutputs, " "))) + } + + argNameScope := rule.scope() + + if len(params.Args) > 0 { + b.Args = make(map[Variable]ninjaString) + for name, value := range params.Args { + if !rule.isArg(name) { + return nil, fmt.Errorf("unknown argument %q", name) + } + + argVar, err := argNameScope.LookupVariable(name) + if err != nil { + // This shouldn't happen. + return nil, fmt.Errorf("argument lookup error: %s", err) + } + + ninjaValue, err := parseNinjaString(scope, value) + if err != nil { + return nil, fmt.Errorf("error parsing variable %q: %s", name, + err) + } + + b.Args[argVar] = ninjaValue + } + } + + return b, nil +} + +func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error { + var ( + comment = b.Comment + rule = b.Rule.fullName(pkgNames) + outputs = b.Outputs + implicitOuts = b.ImplicitOutputs + explicitDeps = b.Inputs + implicitDeps = b.Implicits + orderOnlyDeps = b.OrderOnly + validations = b.Validations + ) + + if b.RuleDef != nil { + implicitDeps = append(b.RuleDef.CommandDeps, implicitDeps...) + orderOnlyDeps = append(b.RuleDef.CommandOrderOnly, orderOnlyDeps...) + } + + err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps, validations, pkgNames) + if err != nil { + return err + } + + err = writeVariables(nw, b.Variables, pkgNames) + if err != nil { + return err + } + + type nameValuePair struct { + name, value string + } + + args := make([]nameValuePair, 0, len(b.Args)) + + for argVar, value := range b.Args { + fullName := argVar.fullName(pkgNames) + args = append(args, nameValuePair{fullName, value.Value(pkgNames)}) + } + sort.Slice(args, func(i, j int) bool { return args[i].name < args[j].name }) + + for _, pair := range args { + err = nw.ScopedAssign(pair.name, pair.value) + if err != nil { + return err + } + } + + if !b.Optional { + err = nw.Default(pkgNames, outputs...) + if err != nil { + return err + } + } + + return nw.BlankLine() +} + +func writeVariables(nw *ninjaWriter, variables map[string]ninjaString, + pkgNames map[*packageContext]string) error { + var keys []string + for k := range variables { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, name := range keys { + err := nw.ScopedAssign(name, variables[name].Value(pkgNames)) + if err != nil { + return err + } + } + return nil +} diff --git a/blueprint/ninja_strings.go b/blueprint/ninja_strings.go new file mode 100644 index 0000000..51a167d --- /dev/null +++ b/blueprint/ninja_strings.go @@ -0,0 +1,420 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "bytes" + "fmt" + "io" + "strings" +) + +const eof = -1 + +var ( + defaultEscaper = strings.NewReplacer( + "\n", "$\n") + inputEscaper = strings.NewReplacer( + "\n", "$\n", + " ", "$ ") + outputEscaper = strings.NewReplacer( + "\n", "$\n", + " ", "$ ", + ":", "$:") +) + +type ninjaString interface { + Value(pkgNames map[*packageContext]string) string + ValueWithEscaper(w io.StringWriter, pkgNames map[*packageContext]string, escaper *strings.Replacer) + Eval(variables map[Variable]ninjaString) (string, error) + Variables() []Variable +} + +type varNinjaString struct { + strings []string + variables []Variable +} + +type literalNinjaString string + +type scope interface { + LookupVariable(name string) (Variable, error) + IsRuleVisible(rule Rule) bool + IsPoolVisible(pool Pool) bool +} + +func simpleNinjaString(str string) ninjaString { + return literalNinjaString(str) +} + +type parseState struct { + scope scope + str string + pendingStr string + stringStart int + varStart int + result *varNinjaString +} + +func (ps *parseState) pushVariable(v Variable) { + if len(ps.result.variables) == len(ps.result.strings) { + // Last push was a variable, we need a blank string separator + ps.result.strings = append(ps.result.strings, "") + } + if ps.pendingStr != "" { + panic("oops, pushed variable with pending string") + } + ps.result.variables = append(ps.result.variables, v) +} + +func (ps *parseState) pushString(s string) { + if len(ps.result.strings) != len(ps.result.variables) { + panic("oops, pushed string after string") + } + ps.result.strings = append(ps.result.strings, ps.pendingStr+s) + ps.pendingStr = "" +} + +type stateFunc func(*parseState, int, rune) (stateFunc, error) + +// parseNinjaString parses an unescaped ninja string (i.e. all $ +// occurrences are expected to be variables or $$) and returns a list of the +// variable names that the string references. +func parseNinjaString(scope scope, str string) (ninjaString, error) { + // naively pre-allocate slices by counting $ signs + n := strings.Count(str, "$") + if n == 0 { + if strings.HasPrefix(str, " ") { + str = "$" + str + } + return literalNinjaString(str), nil + } + result := &varNinjaString{ + strings: make([]string, 0, n+1), + variables: make([]Variable, 0, n), + } + + parseState := &parseState{ + scope: scope, + str: str, + result: result, + } + + state := parseFirstRuneState + var err error + for i := 0; i < len(str); i++ { + r := rune(str[i]) + state, err = state(parseState, i, r) + if err != nil { + return nil, err + } + } + + _, err = state(parseState, len(parseState.str), eof) + if err != nil { + return nil, err + } + + return result, nil +} + +func parseFirstRuneState(state *parseState, i int, r rune) (stateFunc, error) { + if r == ' ' { + state.pendingStr += "$" + } + return parseStringState(state, i, r) +} + +func parseStringState(state *parseState, i int, r rune) (stateFunc, error) { + switch { + case r == '$': + state.varStart = i + 1 + return parseDollarStartState, nil + + case r == eof: + state.pushString(state.str[state.stringStart:i]) + return nil, nil + + default: + return parseStringState, nil + } +} + +func parseDollarStartState(state *parseState, i int, r rune) (stateFunc, error) { + switch { + case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', + r >= '0' && r <= '9', r == '_', r == '-': + // The beginning of a of the variable name. Output the string and + // keep going. + state.pushString(state.str[state.stringStart : i-1]) + return parseDollarState, nil + + case r == '$': + // Just a "$$". Go back to parseStringState without changing + // state.stringStart. + return parseStringState, nil + + case r == '{': + // This is a bracketted variable name (e.g. "${blah.blah}"). Output + // the string and keep going. + state.pushString(state.str[state.stringStart : i-1]) + state.varStart = i + 1 + return parseBracketsState, nil + + case r == eof: + return nil, fmt.Errorf("unexpected end of string after '$'") + + default: + // This was some arbitrary character following a dollar sign, + // which is not allowed. + return nil, fmt.Errorf("invalid character after '$' at byte "+ + "offset %d", i) + } +} + +func parseDollarState(state *parseState, i int, r rune) (stateFunc, error) { + switch { + case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', + r >= '0' && r <= '9', r == '_', r == '-': + // A part of the variable name. Keep going. + return parseDollarState, nil + + case r == '$': + // A dollar after the variable name (e.g. "$blah$"). Output the + // variable we have and start a new one. + v, err := state.scope.LookupVariable(state.str[state.varStart:i]) + if err != nil { + return nil, err + } + + state.pushVariable(v) + state.varStart = i + 1 + state.stringStart = i + + return parseDollarStartState, nil + + case r == eof: + // This is the end of the variable name. + v, err := state.scope.LookupVariable(state.str[state.varStart:i]) + if err != nil { + return nil, err + } + + state.pushVariable(v) + + // We always end with a string, even if it's an empty one. + state.pushString("") + + return nil, nil + + default: + // We've just gone past the end of the variable name, so record what + // we have. + v, err := state.scope.LookupVariable(state.str[state.varStart:i]) + if err != nil { + return nil, err + } + + state.pushVariable(v) + state.stringStart = i + return parseStringState, nil + } +} + +func parseBracketsState(state *parseState, i int, r rune) (stateFunc, error) { + switch { + case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', + r >= '0' && r <= '9', r == '_', r == '-', r == '.': + // A part of the variable name. Keep going. + return parseBracketsState, nil + + case r == '}': + if state.varStart == i { + // The brackets were immediately closed. That's no good. + return nil, fmt.Errorf("empty variable name at byte offset %d", + i) + } + + // This is the end of the variable name. + v, err := state.scope.LookupVariable(state.str[state.varStart:i]) + if err != nil { + return nil, err + } + + state.pushVariable(v) + state.stringStart = i + 1 + return parseStringState, nil + + case r == eof: + return nil, fmt.Errorf("unexpected end of string in variable name") + + default: + // This character isn't allowed in a variable name. + return nil, fmt.Errorf("invalid character in variable name at "+ + "byte offset %d", i) + } +} + +func parseNinjaStrings(scope scope, strs []string) ([]ninjaString, + error) { + + if len(strs) == 0 { + return nil, nil + } + result := make([]ninjaString, len(strs)) + for i, str := range strs { + ninjaStr, err := parseNinjaString(scope, str) + if err != nil { + return nil, fmt.Errorf("error parsing element %d: %s", i, err) + } + result[i] = ninjaStr + } + return result, nil +} + +func (n varNinjaString) Value(pkgNames map[*packageContext]string) string { + if len(n.strings) == 1 { + return defaultEscaper.Replace(n.strings[0]) + } + str := &strings.Builder{} + n.ValueWithEscaper(str, pkgNames, defaultEscaper) + return str.String() +} + +func (n varNinjaString) ValueWithEscaper(w io.StringWriter, pkgNames map[*packageContext]string, + escaper *strings.Replacer) { + + w.WriteString(escaper.Replace(n.strings[0])) + for i, v := range n.variables { + w.WriteString("${") + w.WriteString(v.fullName(pkgNames)) + w.WriteString("}") + w.WriteString(escaper.Replace(n.strings[i+1])) + } +} + +func (n varNinjaString) Eval(variables map[Variable]ninjaString) (string, error) { + str := n.strings[0] + for i, v := range n.variables { + variable, ok := variables[v] + if !ok { + return "", fmt.Errorf("no such global variable: %s", v) + } + value, err := variable.Eval(variables) + if err != nil { + return "", err + } + str += value + n.strings[i+1] + } + return str, nil +} + +func (n varNinjaString) Variables() []Variable { + return n.variables +} + +func (l literalNinjaString) Value(pkgNames map[*packageContext]string) string { + return defaultEscaper.Replace(string(l)) +} + +func (l literalNinjaString) ValueWithEscaper(w io.StringWriter, pkgNames map[*packageContext]string, + escaper *strings.Replacer) { + w.WriteString(escaper.Replace(string(l))) +} + +func (l literalNinjaString) Eval(variables map[Variable]ninjaString) (string, error) { + return string(l), nil +} + +func (l literalNinjaString) Variables() []Variable { + return nil +} + +func validateNinjaName(name string) error { + for i, r := range name { + valid := (r >= 'a' && r <= 'z') || + (r >= 'A' && r <= 'Z') || + (r >= '0' && r <= '9') || + (r == '_') || + (r == '-') || + (r == '.') + if !valid { + + return fmt.Errorf("%q contains an invalid Ninja name character "+ + "%q at byte offset %d", name, r, i) + } + } + return nil +} + +func toNinjaName(name string) string { + ret := bytes.Buffer{} + ret.Grow(len(name)) + for _, r := range name { + valid := (r >= 'a' && r <= 'z') || + (r >= 'A' && r <= 'Z') || + (r >= '0' && r <= '9') || + (r == '_') || + (r == '-') || + (r == '.') + if valid { + ret.WriteRune(r) + } else { + // TODO(jeffrygaston): do escaping so that toNinjaName won't ever output duplicate + // names for two different input names + ret.WriteRune('_') + } + } + + return ret.String() +} + +var builtinRuleArgs = []string{"out", "in"} + +func validateArgName(argName string) error { + err := validateNinjaName(argName) + if err != nil { + return err + } + + // We only allow globals within the rule's package to be used as rule + // arguments. A global in another package can always be mirrored into + // the rule's package by defining a new variable, so this doesn't limit + // what's possible. This limitation prevents situations where a Build + // invocation in another package must use the rule-defining package's + // import name for a 3rd package in order to set the rule's arguments. + if strings.ContainsRune(argName, '.') { + return fmt.Errorf("%q contains a '.' character", argName) + } + + for _, builtin := range builtinRuleArgs { + if argName == builtin { + return fmt.Errorf("%q conflicts with Ninja built-in", argName) + } + } + + return nil +} + +func validateArgNames(argNames []string) error { + for _, argName := range argNames { + err := validateArgName(argName) + if err != nil { + return err + } + } + + return nil +} diff --git a/blueprint/ninja_strings_test.go b/blueprint/ninja_strings_test.go new file mode 100644 index 0000000..c1e05f7 --- /dev/null +++ b/blueprint/ninja_strings_test.go @@ -0,0 +1,221 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "reflect" + "strconv" + "strings" + "testing" +) + +var ninjaParseTestCases = []struct { + input string + vars []string + strs []string + literal bool + err string +}{ + { + input: "abc def $ghi jkl", + vars: []string{"ghi"}, + strs: []string{"abc def ", " jkl"}, + }, + { + input: "abc def $ghi$jkl", + vars: []string{"ghi", "jkl"}, + strs: []string{"abc def ", "", ""}, + }, + { + input: "foo $012_-345xyz_! bar", + vars: []string{"012_-345xyz_"}, + strs: []string{"foo ", "! bar"}, + }, + { + input: "foo ${012_-345xyz_} bar", + vars: []string{"012_-345xyz_"}, + strs: []string{"foo ", " bar"}, + }, + { + input: "foo ${012_-345xyz_} bar", + vars: []string{"012_-345xyz_"}, + strs: []string{"foo ", " bar"}, + }, + { + input: "foo $$ bar", + vars: nil, + strs: []string{"foo $$ bar"}, + // this is technically a literal, but not recognized as such due to the $$ + }, + { + input: "$foo${bar}", + vars: []string{"foo", "bar"}, + strs: []string{"", "", ""}, + }, + { + input: "$foo$$", + vars: []string{"foo"}, + strs: []string{"", "$$"}, + }, + { + input: "foo bar", + vars: nil, + strs: []string{"foo bar"}, + literal: true, + }, + { + input: " foo ", + vars: nil, + strs: []string{"$ foo "}, + literal: true, + }, + { + input: " $foo ", + vars: []string{"foo"}, + strs: []string{"$ ", " "}, + }, { + input: "foo $ bar", + err: "invalid character after '$' at byte offset 5", + }, + { + input: "foo $", + err: "unexpected end of string after '$'", + }, + { + input: "foo ${} bar", + err: "empty variable name at byte offset 6", + }, + { + input: "foo ${abc!} bar", + err: "invalid character in variable name at byte offset 9", + }, + { + input: "foo ${abc", + err: "unexpected end of string in variable name", + }, +} + +func TestParseNinjaString(t *testing.T) { + for _, testCase := range ninjaParseTestCases { + scope := newLocalScope(nil, "namespace") + expectedVars := []Variable{} + for _, varName := range testCase.vars { + v, err := scope.LookupVariable(varName) + if err != nil { + v, err = scope.AddLocalVariable(varName, "") + if err != nil { + t.Fatalf("error creating scope: %s", err) + } + } + expectedVars = append(expectedVars, v) + } + + var expected ninjaString + if len(testCase.strs) > 0 { + if testCase.literal { + expected = literalNinjaString(testCase.strs[0]) + } else { + expected = &varNinjaString{ + strings: testCase.strs, + variables: expectedVars, + } + } + } + + output, err := parseNinjaString(scope, testCase.input) + if err == nil { + if !reflect.DeepEqual(output, expected) { + t.Errorf("incorrect ninja string:") + t.Errorf(" input: %q", testCase.input) + t.Errorf(" expected: %#v", expected) + t.Errorf(" got: %#v", output) + } + } + var errStr string + if err != nil { + errStr = err.Error() + } + if err != nil && err.Error() != testCase.err { + t.Errorf("unexpected error:") + t.Errorf(" input: %q", testCase.input) + t.Errorf(" expected: %q", testCase.err) + t.Errorf(" got: %q", errStr) + } + } +} + +func TestParseNinjaStringWithImportedVar(t *testing.T) { + ImpVar := &staticVariable{name_: "ImpVar"} + impScope := newScope(nil) + impScope.AddVariable(ImpVar) + scope := newScope(nil) + scope.AddImport("impPkg", impScope) + + input := "abc def ${impPkg.ImpVar} ghi" + output, err := parseNinjaString(scope, input) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expect := []Variable{ImpVar} + if !reflect.DeepEqual(output.(*varNinjaString).variables, expect) { + t.Errorf("incorrect output:") + t.Errorf(" input: %q", input) + t.Errorf(" expected: %#v", expect) + t.Errorf(" got: %#v", output) + } +} + +func BenchmarkNinjaString_Value(b *testing.B) { + b.Run("constant", func(b *testing.B) { + for _, l := range []int{1, 10, 100, 1000} { + ns := simpleNinjaString(strings.Repeat("a", l)) + b.Run(strconv.Itoa(l), func(b *testing.B) { + for n := 0; n < b.N; n++ { + ns.Value(nil) + } + }) + } + }) + b.Run("variable", func(b *testing.B) { + for _, l := range []int{1, 10, 100, 1000} { + scope := newLocalScope(nil, "") + scope.AddLocalVariable("a", strings.Repeat("b", l/3)) + ns, _ := parseNinjaString(scope, strings.Repeat("a", l/3)+"${a}"+strings.Repeat("a", l/3)) + b.Run(strconv.Itoa(l), func(b *testing.B) { + for n := 0; n < b.N; n++ { + ns.Value(nil) + } + }) + } + }) + b.Run("variables", func(b *testing.B) { + for _, l := range []int{1, 2, 3, 4, 5, 10, 100, 1000} { + scope := newLocalScope(nil, "") + str := strings.Repeat("a", 10) + for i := 0; i < l; i++ { + scope.AddLocalVariable("a"+strconv.Itoa(i), strings.Repeat("b", 10)) + str += "${a" + strconv.Itoa(i) + "}" + } + ns, _ := parseNinjaString(scope, str) + b.Run(strconv.Itoa(l), func(b *testing.B) { + for n := 0; n < b.N; n++ { + ns.Value(nil) + } + }) + } + }) + +} diff --git a/blueprint/ninja_writer.go b/blueprint/ninja_writer.go new file mode 100644 index 0000000..f9951b4 --- /dev/null +++ b/blueprint/ninja_writer.go @@ -0,0 +1,414 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "io" + "strings" + "unicode" +) + +const ( + indentWidth = 4 + maxIndentDepth = 2 + lineWidth = 80 +) + +var indentString = strings.Repeat(" ", indentWidth*maxIndentDepth) + +type StringWriterWriter interface { + io.StringWriter + io.Writer +} + +type ninjaWriter struct { + writer io.StringWriter + + justDidBlankLine bool // true if the last operation was a BlankLine +} + +func newNinjaWriter(writer io.StringWriter) *ninjaWriter { + return &ninjaWriter{ + writer: writer, + } +} + +func (n *ninjaWriter) Comment(comment string) error { + n.justDidBlankLine = false + + const lineHeaderLen = len("# ") + const maxLineLen = lineWidth - lineHeaderLen + + var lineStart, lastSplitPoint int + for i, r := range comment { + if unicode.IsSpace(r) { + // We know we can safely split the line here. + lastSplitPoint = i + 1 + } + + var line string + var writeLine bool + switch { + case r == '\n': + // Output the line without trimming the left so as to allow comments + // to contain their own indentation. + line = strings.TrimRightFunc(comment[lineStart:i], unicode.IsSpace) + writeLine = true + + case (i-lineStart > maxLineLen) && (lastSplitPoint > lineStart): + // The line has grown too long and is splittable. Split it at the + // last split point. + line = strings.TrimSpace(comment[lineStart:lastSplitPoint]) + writeLine = true + } + + if writeLine { + line = strings.TrimSpace("# "+line) + "\n" + _, err := n.writer.WriteString(line) + if err != nil { + return err + } + lineStart = lastSplitPoint + } + } + + if lineStart != len(comment) { + line := strings.TrimSpace(comment[lineStart:]) + _, err := n.writer.WriteString("# ") + if err != nil { + return err + } + _, err = n.writer.WriteString(line) + if err != nil { + return err + } + _, err = n.writer.WriteString("\n") + if err != nil { + return err + } + } + + return nil +} + +func (n *ninjaWriter) Pool(name string) error { + n.justDidBlankLine = false + return n.writeStatement("pool", name) +} + +func (n *ninjaWriter) Rule(name string) error { + n.justDidBlankLine = false + return n.writeStatement("rule", name) +} + +func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts, + explicitDeps, implicitDeps, orderOnlyDeps, validations []ninjaString, + pkgNames map[*packageContext]string) error { + + n.justDidBlankLine = false + + const lineWrapLen = len(" $") + const maxLineLen = lineWidth - lineWrapLen + + wrapper := &ninjaWriterWithWrap{ + ninjaWriter: n, + maxLineLen: maxLineLen, + } + + if comment != "" { + err := wrapper.Comment(comment) + if err != nil { + return err + } + } + + wrapper.WriteString("build") + + for _, output := range outputs { + wrapper.Space() + output.ValueWithEscaper(wrapper, pkgNames, outputEscaper) + } + + if len(implicitOuts) > 0 { + wrapper.WriteStringWithSpace("|") + + for _, out := range implicitOuts { + wrapper.Space() + out.ValueWithEscaper(wrapper, pkgNames, outputEscaper) + } + } + + wrapper.WriteString(":") + + wrapper.WriteStringWithSpace(rule) + + for _, dep := range explicitDeps { + wrapper.Space() + dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper) + } + + if len(implicitDeps) > 0 { + wrapper.WriteStringWithSpace("|") + + for _, dep := range implicitDeps { + wrapper.Space() + dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper) + } + } + + if len(orderOnlyDeps) > 0 { + wrapper.WriteStringWithSpace("||") + + for _, dep := range orderOnlyDeps { + wrapper.Space() + dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper) + } + } + + if len(validations) > 0 { + wrapper.WriteStringWithSpace("|@") + + for _, dep := range validations { + wrapper.Space() + dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper) + } + } + + return wrapper.Flush() +} + +func (n *ninjaWriter) Assign(name, value string) error { + n.justDidBlankLine = false + _, err := n.writer.WriteString(name) + if err != nil { + return err + } + _, err = n.writer.WriteString(" = ") + if err != nil { + return err + } + _, err = n.writer.WriteString(value) + if err != nil { + return err + } + _, err = n.writer.WriteString("\n") + if err != nil { + return err + } + return nil +} + +func (n *ninjaWriter) ScopedAssign(name, value string) error { + n.justDidBlankLine = false + _, err := n.writer.WriteString(indentString[:indentWidth]) + if err != nil { + return err + } + _, err = n.writer.WriteString(name) + if err != nil { + return err + } + _, err = n.writer.WriteString(" = ") + if err != nil { + return err + } + _, err = n.writer.WriteString(value) + if err != nil { + return err + } + _, err = n.writer.WriteString("\n") + if err != nil { + return err + } + return nil +} + +func (n *ninjaWriter) Default(pkgNames map[*packageContext]string, targets ...ninjaString) error { + n.justDidBlankLine = false + + const lineWrapLen = len(" $") + const maxLineLen = lineWidth - lineWrapLen + + wrapper := &ninjaWriterWithWrap{ + ninjaWriter: n, + maxLineLen: maxLineLen, + } + + wrapper.WriteString("default") + + for _, target := range targets { + wrapper.Space() + target.ValueWithEscaper(wrapper, pkgNames, outputEscaper) + } + + return wrapper.Flush() +} + +func (n *ninjaWriter) Subninja(file string) error { + n.justDidBlankLine = false + return n.writeStatement("subninja", file) +} + +func (n *ninjaWriter) BlankLine() (err error) { + // We don't output multiple blank lines in a row. + if !n.justDidBlankLine { + n.justDidBlankLine = true + _, err = n.writer.WriteString("\n") + } + return err +} + +func (n *ninjaWriter) writeStatement(directive, name string) error { + _, err := n.writer.WriteString(directive + " ") + if err != nil { + return err + } + _, err = n.writer.WriteString(name) + if err != nil { + return err + } + _, err = n.writer.WriteString("\n") + if err != nil { + return err + } + return nil +} + +// ninjaWriterWithWrap is an io.StringWriter that writes through to a ninjaWriter, but supports +// user-readable line wrapping on boundaries when ninjaWriterWithWrap.Space is called. +// It collects incoming calls to WriteString until either the line length is exceeded, in which case +// it inserts a wrap before the pending strings and then writes them, or the next call to Space, in +// which case it writes out the pending strings. +// +// WriteString never returns an error, all errors are held until Flush is called. Once an error has +// occurred all writes become noops. +type ninjaWriterWithWrap struct { + *ninjaWriter + // pending lists the strings that have been written since the last call to Space. + pending []string + + // pendingLen accumulates the lengths of the strings in pending. + pendingLen int + + // lineLen accumulates the number of bytes on the current line. + lineLen int + + // maxLineLen is the length of the line before wrapping. + maxLineLen int + + // space is true if the strings in pending should be preceded by a space. + space bool + + // err holds any error that has occurred to return in Flush. + err error +} + +// WriteString writes the string to buffer, wrapping on a previous Space call if necessary. +// It never returns an error, all errors are held until Flush is called. +func (n *ninjaWriterWithWrap) WriteString(s string) (written int, noError error) { + // Always return the full length of the string and a nil error. + // ninjaWriterWithWrap doesn't return errors to the caller, it saves them until Flush() + written = len(s) + + if n.err != nil { + return + } + + const spaceLen = 1 + if !n.space { + // No space is pending, so a line wrap can't be inserted before this, so just write + // the string. + n.lineLen += len(s) + _, n.err = n.writer.WriteString(s) + } else if n.lineLen+len(s)+spaceLen > n.maxLineLen { + // A space is pending, and the pending strings plus the current string would exceed the + // maximum line length. Wrap and indent before the pending space and strings, then write + // the pending and current strings. + _, n.err = n.writer.WriteString(" $\n") + if n.err != nil { + return + } + _, n.err = n.writer.WriteString(indentString[:indentWidth*2]) + if n.err != nil { + return + } + n.lineLen = indentWidth*2 + n.pendingLen + s = strings.TrimLeftFunc(s, unicode.IsSpace) + n.pending = append(n.pending, s) + n.writePending() + + n.space = false + } else { + // A space is pending but the current string would not reach the maximum line length, + // add it to the pending list. + n.pending = append(n.pending, s) + n.pendingLen += len(s) + n.lineLen += len(s) + } + + return +} + +// Space inserts a space that is also a possible wrapping point into the string. +func (n *ninjaWriterWithWrap) Space() { + if n.err != nil { + return + } + if n.space { + // A space was already pending, and the space plus any strings written after the space did + // not reach the maxmimum line length, so write out the old space and pending strings. + _, n.err = n.writer.WriteString(" ") + n.lineLen++ + n.writePending() + } + n.space = true +} + +// writePending writes out all the strings stored in pending and resets it. +func (n *ninjaWriterWithWrap) writePending() { + if n.err != nil { + return + } + for _, pending := range n.pending { + _, n.err = n.writer.WriteString(pending) + if n.err != nil { + return + } + } + // Reset the length of pending back to 0 without reducing its capacity to avoid reallocating + // the backing array. + n.pending = n.pending[:0] + n.pendingLen = 0 +} + +// WriteStringWithSpace is a helper that calls Space and WriteString. +func (n *ninjaWriterWithWrap) WriteStringWithSpace(s string) { + n.Space() + _, _ = n.WriteString(s) +} + +// Flush writes out any pending space or strings and then a newline. It also returns any errors +// that have previously occurred. +func (n *ninjaWriterWithWrap) Flush() error { + if n.space { + _, n.err = n.writer.WriteString(" ") + } + n.writePending() + if n.err != nil { + return n.err + } + _, err := n.writer.WriteString("\n") + return err +} diff --git a/blueprint/ninja_writer_test.go b/blueprint/ninja_writer_test.go new file mode 100644 index 0000000..82eeee5 --- /dev/null +++ b/blueprint/ninja_writer_test.go @@ -0,0 +1,145 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "bytes" + "strings" + "testing" +) + +func ck(err error) { + if err != nil { + panic(err) + } +} + +var ninjaWriterTestCases = []struct { + input func(w *ninjaWriter) + output string +}{ + { + input: func(w *ninjaWriter) { + ck(w.Comment("foo")) + }, + output: "# foo\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Pool("foo")) + }, + output: "pool foo\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Rule("foo")) + }, + output: "rule foo\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Build("foo comment", "foo", testNinjaStrings("o1", "o2"), + testNinjaStrings("io1", "io2"), testNinjaStrings("e1", "e2"), + testNinjaStrings("i1", "i2"), testNinjaStrings("oo1", "oo2"), + testNinjaStrings("v1", "v2"), nil)) + }, + output: "# foo comment\nbuild o1 o2 | io1 io2: foo e1 e2 | i1 i2 || oo1 oo2 |@ v1 v2\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Build("foo comment", "foo", + testNinjaStrings(strings.Repeat("o", lineWidth)), + nil, + testNinjaStrings(strings.Repeat("i", lineWidth)), + nil, nil, nil, nil)) + }, + output: "# foo comment\nbuild $\n " + strings.Repeat("o", lineWidth) + ": foo $\n " + strings.Repeat("i", lineWidth) + "\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Default(nil, testNinjaStrings("foo")...)) + }, + output: "default foo\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Assign("foo", "bar")) + }, + output: "foo = bar\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.ScopedAssign("foo", "bar")) + }, + output: " foo = bar\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Subninja("build.ninja")) + }, + output: "subninja build.ninja\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.BlankLine()) + }, + output: "\n", + }, + { + input: func(w *ninjaWriter) { + ck(w.Pool("p")) + ck(w.ScopedAssign("depth", "3")) + ck(w.BlankLine()) + ck(w.Comment("here comes a rule")) + ck(w.Rule("r")) + ck(w.ScopedAssign("command", "echo out: $out in: $in _arg: $_arg")) + ck(w.ScopedAssign("pool", "p")) + ck(w.BlankLine()) + ck(w.Build("r comment", "r", testNinjaStrings("foo.o"), + nil, testNinjaStrings("foo.in"), nil, nil, nil, nil)) + ck(w.ScopedAssign("_arg", "arg value")) + }, + output: `pool p + depth = 3 + +# here comes a rule +rule r + command = echo out: $out in: $in _arg: $_arg + pool = p + +# r comment +build foo.o: r foo.in + _arg = arg value +`, + }, +} + +func TestNinjaWriter(t *testing.T) { + for i, testCase := range ninjaWriterTestCases { + buf := bytes.NewBuffer(nil) + w := newNinjaWriter(buf) + testCase.input(w) + if buf.String() != testCase.output { + t.Errorf("incorrect output for test case %d", i) + t.Errorf(" expected: %q", testCase.output) + t.Errorf(" got: %q", buf.String()) + } + } +} + +func testNinjaStrings(s ...string) []ninjaString { + ret, _ := parseNinjaStrings(nil, s) + return ret +} diff --git a/blueprint/package_ctx.go b/blueprint/package_ctx.go new file mode 100644 index 0000000..1eafdb9 --- /dev/null +++ b/blueprint/package_ctx.go @@ -0,0 +1,957 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "runtime" + "strings" + "sync" +) + +// A PackageContext provides a way to create package-scoped Ninja pools, +// rules, and variables. A Go package should create a single unexported +// package-scoped PackageContext variable that it uses to create all package- +// scoped Ninja object definitions. This PackageContext object should then be +// passed to all calls to define module- or singleton-specific Ninja +// definitions. For example: +// +// package blah +// +// import ( +// "blueprint" +// ) +// +// var ( +// pctx = NewPackageContext("path/to/blah") +// +// myPrivateVar = pctx.StaticVariable("myPrivateVar", "abcdef") +// MyExportedVar = pctx.StaticVariable("MyExportedVar", "$myPrivateVar 123456!") +// +// SomeRule = pctx.StaticRule(...) +// ) +// +// // ... +// +// func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) { +// ctx.Build(pctx, blueprint.BuildParams{ +// Rule: SomeRule, +// Outputs: []string{"$myPrivateVar"}, +// }) +// } +type PackageContext interface { + Import(pkgPath string) + ImportAs(as, pkgPath string) + + StaticVariable(name, value string) Variable + VariableFunc(name string, f func(config interface{}) (string, error)) Variable + VariableConfigMethod(name string, method interface{}) Variable + + StaticPool(name string, params PoolParams) Pool + PoolFunc(name string, f func(interface{}) (PoolParams, error)) Pool + + StaticRule(name string, params RuleParams, argNames ...string) Rule + RuleFunc(name string, f func(interface{}) (RuleParams, error), argNames ...string) Rule + + AddNinjaFileDeps(deps ...string) + + getScope() *basicScope +} + +type packageContext struct { + fullName string + shortName string + pkgPath string + scope *basicScope + ninjaFileDeps []string +} + +var _ PackageContext = (*packageContext)(nil) + +func (p *packageContext) getScope() *basicScope { + return p.scope +} + +var packageContexts = map[string]*packageContext{} + +// NewPackageContext creates a PackageContext object for a given package. The +// pkgPath argument should always be set to the full path used to import the +// package. This function may only be called from a Go package's init() +// function or as part of a package-scoped variable initialization. +func NewPackageContext(pkgPath string) PackageContext { + checkCalledFromInit() + + if _, present := packageContexts[pkgPath]; present { + panic(fmt.Errorf("package %q already has a package context", pkgPath)) + } + + pkgName := pkgPathToName(pkgPath) + err := validateNinjaName(pkgName) + if err != nil { + panic(err) + } + + i := strings.LastIndex(pkgPath, "/") + shortName := pkgPath[i+1:] + + p := &packageContext{ + fullName: pkgName, + shortName: shortName, + pkgPath: pkgPath, + scope: newScope(nil), + } + + packageContexts[pkgPath] = p + + return p +} + +var Phony Rule = NewBuiltinRule("phony") + +var Console Pool = NewBuiltinPool("console") + +var errRuleIsBuiltin = errors.New("the rule is a built-in") +var errPoolIsBuiltin = errors.New("the pool is a built-in") +var errVariableIsArg = errors.New("argument variables have no value") + +// checkCalledFromInit panics if a Go package's init function is not on the +// call stack. +func checkCalledFromInit() { + for skip := 3; ; skip++ { + _, funcName, ok := callerName(skip) + if !ok { + panic("not called from an init func") + } + + if funcName == "init" || strings.HasPrefix(funcName, "init·") || + funcName == "init.ializers" || strings.HasPrefix(funcName, "init.") { + return + } + } +} + +// A regex to find a package path within a function name. It finds the shortest string that is +// followed by '.' and doesn't have any '/'s left. +var pkgPathRe = regexp.MustCompile(`^(.*?)\.([^/]+)$`) + +// callerName returns the package path and function name of the calling +// function. The skip argument has the same meaning as the skip argument of +// runtime.Callers. +func callerName(skip int) (pkgPath, funcName string, ok bool) { + var pc [1]uintptr + n := runtime.Callers(skip+1, pc[:]) + if n != 1 { + return "", "", false + } + frames := runtime.CallersFrames(pc[:]) + frame, _ := frames.Next() + f := frame.Function + s := pkgPathRe.FindStringSubmatch(f) + if len(s) < 3 { + panic(fmt.Errorf("failed to extract package path and function name from %q", f)) + } + + return s[1], s[2], true +} + +// pkgPathToName makes a Ninja-friendly name out of a Go package name by +// replaceing all the '/' characters with '.'. We assume the results are +// unique, though this is not 100% guaranteed for Go package names that +// already contain '.' characters. Disallowing package names with '.' isn't +// reasonable since many package names contain the name of the hosting site +// (e.g. "code.google.com"). In practice this probably isn't really a +// problem. +func pkgPathToName(pkgPath string) string { + return strings.Replace(pkgPath, "/", ".", -1) +} + +// Import enables access to the exported Ninja pools, rules, and variables +// that are defined at the package scope of another Go package. Go's +// visibility rules apply to these references - capitalized names indicate +// that something is exported. It may only be called from a Go package's +// init() function. The Go package path passed to Import must have already +// been imported into the Go package using a Go import statement. The +// imported variables may then be accessed from Ninja strings as +// "${pkg.Variable}", while the imported rules can simply be accessed as +// exported Go variables from the package. For example: +// +// import ( +// "blueprint" +// "foo/bar" +// ) +// +// var pctx = NewPackagePath("blah") +// +// func init() { +// pctx.Import("foo/bar") +// } +// +// ... +// +// func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) { +// ctx.Build(pctx, blueprint.BuildParams{ +// Rule: bar.SomeRule, +// Outputs: []string{"${bar.SomeVariable}"}, +// }) +// } +// +// Note that the local name used to refer to the package in Ninja variable names +// is derived from pkgPath by extracting the last path component. This differs +// from Go's import declaration, which derives the local name from the package +// clause in the imported package. By convention these names are made to match, +// but this is not required. +func (p *packageContext) Import(pkgPath string) { + checkCalledFromInit() + importPkg, ok := packageContexts[pkgPath] + if !ok { + panic(fmt.Errorf("package %q has no context", pkgPath)) + } + + err := p.scope.AddImport(importPkg.shortName, importPkg.scope) + if err != nil { + panic(err) + } +} + +// ImportAs provides the same functionality as Import, but it allows the local +// name that will be used to refer to the package to be specified explicitly. +// It may only be called from a Go package's init() function. +func (p *packageContext) ImportAs(as, pkgPath string) { + checkCalledFromInit() + importPkg, ok := packageContexts[pkgPath] + if !ok { + panic(fmt.Errorf("package %q has no context", pkgPath)) + } + + err := validateNinjaName(as) + if err != nil { + panic(err) + } + + err = p.scope.AddImport(as, importPkg.scope) + if err != nil { + panic(err) + } +} + +type staticVariable struct { + pctx *packageContext + name_ string + value_ string + fullName_ string +} + +// StaticVariable returns a Variable whose value does not depend on any +// configuration information. It may only be called during a Go package's +// initialization - either from the init() function or as part of a package- +// scoped variable's initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja variable that will be output. The name argument should +// exactly match the Go variable name, and the value string may reference other +// Ninja variables that are visible within the calling Go package. +func (p *packageContext) StaticVariable(name, value string) Variable { + checkCalledFromInit() + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + v := &staticVariable{ + pctx: p, + name_: name, + value_: value, + } + err = p.scope.AddVariable(v) + if err != nil { + panic(err) + } + + return v +} + +func (v *staticVariable) packageContext() *packageContext { + return v.pctx +} + +func (v *staticVariable) name() string { + return v.name_ +} + +func (v *staticVariable) fullName(pkgNames map[*packageContext]string) string { + if v.fullName_ != "" { + return v.fullName_ + } + return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_ +} + +func (v *staticVariable) memoizeFullName(pkgNames map[*packageContext]string) { + v.fullName_ = v.fullName(pkgNames) +} + +func (v *staticVariable) value(interface{}) (ninjaString, error) { + ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_) + if err != nil { + err = fmt.Errorf("error parsing variable %s value: %s", v, err) + panic(err) + } + return ninjaStr, nil +} + +func (v *staticVariable) String() string { + return v.pctx.pkgPath + "." + v.name_ +} + +type variableFunc struct { + pctx *packageContext + name_ string + value_ func(interface{}) (string, error) + fullName_ string +} + +// VariableFunc returns a Variable whose value is determined by a function that +// takes a config object as input and returns either the variable value or an +// error. It may only be called during a Go package's initialization - either +// from the init() function or as part of a package-scoped variable's +// initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja variable that will be output. The name argument should +// exactly match the Go variable name, and the value string returned by f may +// reference other Ninja variables that are visible within the calling Go +// package. +func (p *packageContext) VariableFunc(name string, + f func(config interface{}) (string, error)) Variable { + + checkCalledFromInit() + + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + v := &variableFunc{ + pctx: p, + name_: name, + value_: f, + } + err = p.scope.AddVariable(v) + if err != nil { + panic(err) + } + + return v +} + +// VariableConfigMethod returns a Variable whose value is determined by calling +// a method on the config object. The method must take no arguments and return +// a single string that will be the variable's value. It may only be called +// during a Go package's initialization - either from the init() function or as +// part of a package-scoped variable's initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja variable that will be output. The name argument should +// exactly match the Go variable name, and the value string returned by method +// may reference other Ninja variables that are visible within the calling Go +// package. +func (p *packageContext) VariableConfigMethod(name string, + method interface{}) Variable { + + checkCalledFromInit() + + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + methodValue := reflect.ValueOf(method) + validateVariableMethod(name, methodValue) + + fun := func(config interface{}) (string, error) { + result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)}) + resultStr := result[0].Interface().(string) + return resultStr, nil + } + + v := &variableFunc{ + pctx: p, + name_: name, + value_: fun, + } + err = p.scope.AddVariable(v) + if err != nil { + panic(err) + } + + return v +} + +func (v *variableFunc) packageContext() *packageContext { + return v.pctx +} + +func (v *variableFunc) name() string { + return v.name_ +} + +func (v *variableFunc) fullName(pkgNames map[*packageContext]string) string { + if v.fullName_ != "" { + return v.fullName_ + } + return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_ +} + +func (v *variableFunc) memoizeFullName(pkgNames map[*packageContext]string) { + v.fullName_ = v.fullName(pkgNames) +} + +func (v *variableFunc) value(config interface{}) (ninjaString, error) { + value, err := v.value_(config) + if err != nil { + return nil, err + } + + ninjaStr, err := parseNinjaString(v.pctx.scope, value) + if err != nil { + err = fmt.Errorf("error parsing variable %s value: %s", v, err) + panic(err) + } + + return ninjaStr, nil +} + +func (v *variableFunc) String() string { + return v.pctx.pkgPath + "." + v.name_ +} + +func validateVariableMethod(name string, methodValue reflect.Value) { + methodType := methodValue.Type() + if methodType.Kind() != reflect.Func { + panic(fmt.Errorf("method given for variable %s is not a function", + name)) + } + if n := methodType.NumIn(); n != 1 { + panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)", + name, n)) + } + if n := methodType.NumOut(); n != 1 { + panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)", + name, n)) + } + if kind := methodType.Out(0).Kind(); kind != reflect.String { + panic(fmt.Errorf("method for variable %s does not return a string", + name)) + } +} + +// An argVariable is a Variable that exists only when it is set by a build +// statement to pass a value to the rule being invoked. It has no value, so it +// can never be used to create a Ninja assignment statement. It is inserted +// into the rule's scope, which is used for name lookups within the rule and +// when assigning argument values as part of a build statement. +type argVariable struct { + name_ string +} + +func (v *argVariable) packageContext() *packageContext { + panic("this should not be called") +} + +func (v *argVariable) name() string { + return v.name_ +} + +func (v *argVariable) fullName(pkgNames map[*packageContext]string) string { + return v.name_ +} + +func (v *argVariable) memoizeFullName(pkgNames map[*packageContext]string) { + // Nothing to do, full name is known at initialization. +} + +func (v *argVariable) value(config interface{}) (ninjaString, error) { + return nil, errVariableIsArg +} + +func (v *argVariable) String() string { + return ":" + v.name_ +} + +type staticPool struct { + pctx *packageContext + name_ string + params PoolParams + fullName_ string +} + +// StaticPool returns a Pool whose value does not depend on any configuration +// information. It may only be called during a Go package's initialization - +// either from the init() function or as part of a package-scoped Go variable's +// initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja pool that will be output. The name argument should +// exactly match the Go variable name, and the params fields may reference other +// Ninja variables that are visible within the calling Go package. +func (p *packageContext) StaticPool(name string, params PoolParams) Pool { + checkCalledFromInit() + + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + pool := &staticPool{ + pctx: p, + name_: name, + params: params, + } + err = p.scope.AddPool(pool) + if err != nil { + panic(err) + } + + return pool +} + +func (p *staticPool) packageContext() *packageContext { + return p.pctx +} + +func (p *staticPool) name() string { + return p.name_ +} + +func (p *staticPool) fullName(pkgNames map[*packageContext]string) string { + if p.fullName_ != "" { + return p.fullName_ + } + return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_ +} + +func (p *staticPool) memoizeFullName(pkgNames map[*packageContext]string) { + p.fullName_ = p.fullName(pkgNames) +} + +func (p *staticPool) def(config interface{}) (*poolDef, error) { + def, err := parsePoolParams(p.pctx.scope, &p.params) + if err != nil { + panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err)) + } + return def, nil +} + +func (p *staticPool) String() string { + return p.pctx.pkgPath + "." + p.name_ +} + +type poolFunc struct { + pctx *packageContext + name_ string + paramsFunc func(interface{}) (PoolParams, error) + fullName_ string +} + +// PoolFunc returns a Pool whose value is determined by a function that takes a +// config object as input and returns either the pool parameters or an error. It +// may only be called during a Go package's initialization - either from the +// init() function or as part of a package-scoped variable's initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja pool that will be output. The name argument should +// exactly match the Go variable name, and the string fields of the PoolParams +// returned by f may reference other Ninja variables that are visible within the +// calling Go package. +func (p *packageContext) PoolFunc(name string, f func(interface{}) (PoolParams, + error)) Pool { + + checkCalledFromInit() + + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + pool := &poolFunc{ + pctx: p, + name_: name, + paramsFunc: f, + } + err = p.scope.AddPool(pool) + if err != nil { + panic(err) + } + + return pool +} + +func (p *poolFunc) packageContext() *packageContext { + return p.pctx +} + +func (p *poolFunc) name() string { + return p.name_ +} + +func (p *poolFunc) fullName(pkgNames map[*packageContext]string) string { + if p.fullName_ != "" { + return p.fullName_ + } + return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_ +} + +func (p *poolFunc) memoizeFullName(pkgNames map[*packageContext]string) { + p.fullName_ = p.fullName(pkgNames) +} + +func (p *poolFunc) def(config interface{}) (*poolDef, error) { + params, err := p.paramsFunc(config) + if err != nil { + return nil, err + } + def, err := parsePoolParams(p.pctx.scope, ¶ms) + if err != nil { + panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err)) + } + return def, nil +} + +func (p *poolFunc) String() string { + return p.pctx.pkgPath + "." + p.name_ +} + +type builtinPool struct { + name_ string +} + +func (p *builtinPool) packageContext() *packageContext { + return nil +} + +func (p *builtinPool) name() string { + return p.name_ +} + +func (p *builtinPool) fullName(pkgNames map[*packageContext]string) string { + return p.name_ +} + +func (p *builtinPool) memoizeFullName(pkgNames map[*packageContext]string) { + // Nothing to do, full name is known at initialization. +} + +func (p *builtinPool) def(config interface{}) (*poolDef, error) { + return nil, errPoolIsBuiltin +} + +// NewBuiltinPool returns a Pool object that refers to a pool name created outside of Blueprint +func NewBuiltinPool(name string) Pool { + return &builtinPool{ + name_: name, + } +} + +func (p *builtinPool) String() string { + return ":" + p.name_ +} + +type staticRule struct { + pctx *packageContext + name_ string + params RuleParams + argNames map[string]bool + scope_ *basicScope + fullName_ string + sync.Mutex // protects scope_ during lazy creation +} + +// StaticRule returns a Rule whose value does not depend on any configuration +// information. It may only be called during a Go package's initialization - +// either from the init() function or as part of a package-scoped Go variable's +// initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja rule that will be output. The name argument should +// exactly match the Go variable name, and the params fields may reference other +// Ninja variables that are visible within the calling Go package. +// +// The argNames arguments list Ninja variables that may be overridden by Ninja +// build statements that invoke the rule. These arguments may be referenced in +// any of the string fields of params. Arguments can shadow package-scoped +// variables defined within the caller's Go package, but they may not shadow +// those defined in another package. Shadowing a package-scoped variable +// results in the package-scoped variable's value being used for build +// statements that do not override the argument. For argument names that do not +// shadow package-scoped variables the default value is an empty string. +func (p *packageContext) StaticRule(name string, params RuleParams, + argNames ...string) Rule { + + checkCalledFromInit() + + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + err = validateArgNames(argNames) + if err != nil { + panic(fmt.Errorf("invalid argument name: %s", err)) + } + + argNamesSet := make(map[string]bool) + for _, argName := range argNames { + argNamesSet[argName] = true + } + + ruleScope := (*basicScope)(nil) // This will get created lazily + + r := &staticRule{ + pctx: p, + name_: name, + params: params, + argNames: argNamesSet, + scope_: ruleScope, + } + err = p.scope.AddRule(r) + if err != nil { + panic(err) + } + + return r +} + +func (r *staticRule) packageContext() *packageContext { + return r.pctx +} + +func (r *staticRule) name() string { + return r.name_ +} + +func (r *staticRule) fullName(pkgNames map[*packageContext]string) string { + if r.fullName_ != "" { + return r.fullName_ + } + return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_ +} + +func (r *staticRule) memoizeFullName(pkgNames map[*packageContext]string) { + r.fullName_ = r.fullName(pkgNames) +} + +func (r *staticRule) def(interface{}) (*ruleDef, error) { + def, err := parseRuleParams(r.scope(), &r.params) + if err != nil { + panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err)) + } + return def, nil +} + +func (r *staticRule) scope() *basicScope { + // We lazily create the scope so that all the package-scoped variables get + // declared before the args are created. Otherwise we could incorrectly + // shadow a package-scoped variable with an arg variable. + r.Lock() + defer r.Unlock() + + if r.scope_ == nil { + r.scope_ = makeRuleScope(r.pctx.scope, r.argNames) + } + return r.scope_ +} + +func (r *staticRule) isArg(argName string) bool { + return r.argNames[argName] +} + +func (r *staticRule) String() string { + return r.pctx.pkgPath + "." + r.name_ +} + +type ruleFunc struct { + pctx *packageContext + name_ string + paramsFunc func(interface{}) (RuleParams, error) + argNames map[string]bool + scope_ *basicScope + fullName_ string + sync.Mutex // protects scope_ during lazy creation +} + +// RuleFunc returns a Rule whose value is determined by a function that takes a +// config object as input and returns either the rule parameters or an error. It +// may only be called during a Go package's initialization - either from the +// init() function or as part of a package-scoped variable's initialization. +// +// This function is usually used to initialize a package-scoped Go variable that +// represents a Ninja rule that will be output. The name argument should +// exactly match the Go variable name, and the string fields of the RuleParams +// returned by f may reference other Ninja variables that are visible within the +// calling Go package. +// +// The argNames arguments list Ninja variables that may be overridden by Ninja +// build statements that invoke the rule. These arguments may be referenced in +// any of the string fields of the RuleParams returned by f. Arguments can +// shadow package-scoped variables defined within the caller's Go package, but +// they may not shadow those defined in another package. Shadowing a package- +// scoped variable results in the package-scoped variable's value being used for +// build statements that do not override the argument. For argument names that +// do not shadow package-scoped variables the default value is an empty string. +func (p *packageContext) RuleFunc(name string, f func(interface{}) (RuleParams, + error), argNames ...string) Rule { + + checkCalledFromInit() + + err := validateNinjaName(name) + if err != nil { + panic(err) + } + + err = validateArgNames(argNames) + if err != nil { + panic(fmt.Errorf("invalid argument name: %s", err)) + } + + argNamesSet := make(map[string]bool) + for _, argName := range argNames { + argNamesSet[argName] = true + } + + ruleScope := (*basicScope)(nil) // This will get created lazily + + rule := &ruleFunc{ + pctx: p, + name_: name, + paramsFunc: f, + argNames: argNamesSet, + scope_: ruleScope, + } + err = p.scope.AddRule(rule) + if err != nil { + panic(err) + } + + return rule +} + +func (r *ruleFunc) packageContext() *packageContext { + return r.pctx +} + +func (r *ruleFunc) name() string { + return r.name_ +} + +func (r *ruleFunc) fullName(pkgNames map[*packageContext]string) string { + if r.fullName_ != "" { + return r.fullName_ + } + return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_ +} + +func (r *ruleFunc) memoizeFullName(pkgNames map[*packageContext]string) { + r.fullName_ = r.fullName(pkgNames) +} + +func (r *ruleFunc) def(config interface{}) (*ruleDef, error) { + params, err := r.paramsFunc(config) + if err != nil { + return nil, err + } + def, err := parseRuleParams(r.scope(), ¶ms) + if err != nil { + panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err)) + } + return def, nil +} + +func (r *ruleFunc) scope() *basicScope { + // We lazily create the scope so that all the global variables get declared + // before the args are created. Otherwise we could incorrectly shadow a + // global variable with an arg variable. + r.Lock() + defer r.Unlock() + + if r.scope_ == nil { + r.scope_ = makeRuleScope(r.pctx.scope, r.argNames) + } + return r.scope_ +} + +func (r *ruleFunc) isArg(argName string) bool { + return r.argNames[argName] +} + +func (r *ruleFunc) String() string { + return r.pctx.pkgPath + "." + r.name_ +} + +type builtinRule struct { + name_ string + scope_ *basicScope + sync.Mutex // protects scope_ during lazy creation +} + +func (r *builtinRule) packageContext() *packageContext { + return nil +} + +func (r *builtinRule) name() string { + return r.name_ +} + +func (r *builtinRule) fullName(pkgNames map[*packageContext]string) string { + return r.name_ +} + +func (r *builtinRule) memoizeFullName(pkgNames map[*packageContext]string) { + // Nothing to do, full name is known at initialization. +} + +func (r *builtinRule) def(config interface{}) (*ruleDef, error) { + return nil, errRuleIsBuiltin +} + +func (r *builtinRule) scope() *basicScope { + r.Lock() + defer r.Unlock() + + if r.scope_ == nil { + r.scope_ = makeRuleScope(nil, nil) + } + return r.scope_ +} + +func (r *builtinRule) isArg(argName string) bool { + return false +} + +func (r *builtinRule) String() string { + return ":" + r.name_ +} + +// NewBuiltinRule returns a Rule object that refers to a rule that was created outside of Blueprint +func NewBuiltinRule(name string) Rule { + return &builtinRule{ + name_: name, + } +} + +func (p *packageContext) AddNinjaFileDeps(deps ...string) { + p.ninjaFileDeps = append(p.ninjaFileDeps, deps...) +} diff --git a/blueprint/parser/ast.go b/blueprint/parser/ast.go new file mode 100644 index 0000000..cb311ee --- /dev/null +++ b/blueprint/parser/ast.go @@ -0,0 +1,549 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "strings" + "text/scanner" +) + +type Node interface { + // Pos returns the position of the first token in the Node + Pos() scanner.Position + // End returns the position of the character after the last token in the Node + End() scanner.Position +} + +// Definition is an Assignment or a Module at the top level of a Blueprints file +type Definition interface { + Node + String() string + definitionTag() +} + +// An Assignment is a variable assignment at the top level of a Blueprints file, scoped to the +// file and subdirs. +type Assignment struct { + Name string + NamePos scanner.Position + Value Expression + OrigValue Expression + EqualsPos scanner.Position + Assigner string + Referenced bool +} + +func (a *Assignment) String() string { + return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.OrigValue, a.Referenced) +} + +func (a *Assignment) Pos() scanner.Position { return a.NamePos } +func (a *Assignment) End() scanner.Position { return a.Value.End() } + +func (a *Assignment) definitionTag() {} + +// A Module is a module definition at the top level of a Blueprints file +type Module struct { + Type string + TypePos scanner.Position + Map +} + +func (m *Module) Copy() *Module { + ret := *m + ret.Properties = make([]*Property, len(m.Properties)) + for i := range m.Properties { + ret.Properties[i] = m.Properties[i].Copy() + } + return &ret +} + +func (m *Module) String() string { + propertyStrings := make([]string, len(m.Properties)) + for i, property := range m.Properties { + propertyStrings[i] = property.String() + } + return fmt.Sprintf("%s@%s-%s{%s}", m.Type, + m.LBracePos, m.RBracePos, + strings.Join(propertyStrings, ", ")) +} + +func (m *Module) definitionTag() {} + +func (m *Module) Pos() scanner.Position { return m.TypePos } +func (m *Module) End() scanner.Position { return m.Map.End() } + +// A Property is a name: value pair within a Map, which may be a top level Module. +type Property struct { + Name string + NamePos scanner.Position + ColonPos scanner.Position + Value Expression +} + +func (p *Property) Copy() *Property { + ret := *p + ret.Value = p.Value.Copy() + return &ret +} + +func (p *Property) String() string { + return fmt.Sprintf("%s@%s: %s", p.Name, p.ColonPos, p.Value) +} + +func (p *Property) Pos() scanner.Position { return p.NamePos } +func (p *Property) End() scanner.Position { return p.Value.End() } + +// A MapItem is a key: value pair within a Map, corresponding to map type, rather than a struct. +type MapItem struct { + ColonPos scanner.Position + Key *String + Value Expression +} + +func (m *MapItem) Copy() *MapItem { + ret := MapItem{ + ColonPos: m.ColonPos, + Key: m.Key.Copy().(*String), + Value: m.Value.Copy(), + } + return &ret +} + +func (m *MapItem) String() string { + return fmt.Sprintf("%s@%s: %s", m.Key, m.ColonPos, m.Value) +} + +func (m *MapItem) Pos() scanner.Position { return m.Key.Pos() } +func (m *MapItem) End() scanner.Position { return m.Value.End() } + +// An Expression is a Value in a Property or Assignment. It can be a literal (String or Bool), a +// Map, a List, an Operator that combines two expressions of the same type, or a Variable that +// references and Assignment. +type Expression interface { + Node + // Copy returns a copy of the Expression that will not affect the original if mutated + Copy() Expression + String() string + // Type returns the underlying Type enum of the Expression if it were to be evalutated + Type() Type + // Eval returns an expression that is fully evaluated to a simple type (List, Map, String, or + // Bool). It will return the same object for every call to Eval(). + Eval() Expression +} + +// ExpressionsAreSame tells whether the two values are the same Expression. +// This includes the symbolic representation of each Expression but not their positions in the original source tree. +// This does not apply any simplification to the expressions before comparing them +// (for example, "!!a" wouldn't be deemed equal to "a") +func ExpressionsAreSame(a Expression, b Expression) (equal bool, err error) { + return hackyExpressionsAreSame(a, b) +} + +// TODO(jeffrygaston) once positions are removed from Expression structs, +// remove this function and have callers use reflect.DeepEqual(a, b) +func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) { + if a.Type() != b.Type() { + return false, nil + } + left, err := hackyFingerprint(a) + if err != nil { + return false, nil + } + right, err := hackyFingerprint(b) + if err != nil { + return false, nil + } + areEqual := string(left) == string(right) + return areEqual, nil +} + +func hackyFingerprint(expression Expression) (fingerprint []byte, err error) { + assignment := &Assignment{"a", noPos, expression, expression, noPos, "=", false} + module := &File{} + module.Defs = append(module.Defs, assignment) + p := newPrinter(module) + return p.Print() +} + +type Type int + +const ( + BoolType Type = iota + 1 + StringType + Int64Type + ListType + MapType + NotEvaluatedType +) + +func (t Type) String() string { + switch t { + case BoolType: + return "bool" + case StringType: + return "string" + case Int64Type: + return "int64" + case ListType: + return "list" + case MapType: + return "map" + case NotEvaluatedType: + return "notevaluated" + default: + panic(fmt.Errorf("Unknown type %d", t)) + } +} + +type Operator struct { + Args [2]Expression + Operator rune + OperatorPos scanner.Position + Value Expression +} + +func (x *Operator) Copy() Expression { + ret := *x + ret.Args[0] = x.Args[0].Copy() + ret.Args[1] = x.Args[1].Copy() + return &ret +} + +func (x *Operator) Eval() Expression { + return x.Value.Eval() +} + +func (x *Operator) Type() Type { + return x.Args[0].Type() +} + +func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() } +func (x *Operator) End() scanner.Position { return x.Args[1].End() } + +func (x *Operator) String() string { + return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(), + x.Value, x.OperatorPos) +} + +type Variable struct { + Name string + NamePos scanner.Position + Value Expression +} + +func (x *Variable) Pos() scanner.Position { return x.NamePos } +func (x *Variable) End() scanner.Position { return endPos(x.NamePos, len(x.Name)) } + +func (x *Variable) Copy() Expression { + ret := *x + return &ret +} + +func (x *Variable) Eval() Expression { + return x.Value.Eval() +} + +func (x *Variable) String() string { + return x.Name + " = " + x.Value.String() +} + +func (x *Variable) Type() Type { return x.Value.Type() } + +type Map struct { + LBracePos scanner.Position + RBracePos scanner.Position + Properties []*Property + MapItems []*MapItem +} + +func (x *Map) Pos() scanner.Position { return x.LBracePos } +func (x *Map) End() scanner.Position { return endPos(x.RBracePos, 1) } + +func (x *Map) Copy() Expression { + ret := *x + ret.Properties = make([]*Property, len(x.Properties)) + for i := range x.Properties { + ret.Properties[i] = x.Properties[i].Copy() + } + ret.MapItems = make([]*MapItem, len(x.MapItems)) + for i := range x.MapItems { + ret.MapItems[i] = x.MapItems[i].Copy() + } + return &ret +} + +func (x *Map) Eval() Expression { + if len(x.Properties) > 0 && len(x.MapItems) > 0 { + panic("Cannot support both Properties and MapItems") + } + return x +} + +func (x *Map) String() string { + var s string + if len(x.MapItems) > 0 { + mapStrings := make([]string, len(x.MapItems)) + for i, mapItem := range x.MapItems { + mapStrings[i] = mapItem.String() + } + s = strings.Join(mapStrings, ", ") + } else { + propertyStrings := make([]string, len(x.Properties)) + for i, property := range x.Properties { + propertyStrings[i] = property.String() + } + s = strings.Join(propertyStrings, ", ") + } + return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos, s) +} + +func (x *Map) Type() Type { return MapType } + +// GetProperty looks for a property with the given name. +// It resembles the bracket operator of a built-in Golang map. +func (x *Map) GetProperty(name string) (Property *Property, found bool) { + prop, found, _ := x.getPropertyImpl(name) + return prop, found // we don't currently expose the index to callers +} + +func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) { + for i, prop := range x.Properties { + if prop.Name == name { + return prop, true, i + } + } + return nil, false, -1 +} + +// RemoveProperty removes the property with the given name, if it exists. +func (x *Map) RemoveProperty(propertyName string) (removed bool) { + _, found, index := x.getPropertyImpl(propertyName) + if found { + x.Properties = append(x.Properties[:index], x.Properties[index+1:]...) + } + return found +} + +type List struct { + LBracePos scanner.Position + RBracePos scanner.Position + Values []Expression +} + +func (x *List) Pos() scanner.Position { return x.LBracePos } +func (x *List) End() scanner.Position { return endPos(x.RBracePos, 1) } + +func (x *List) Copy() Expression { + ret := *x + ret.Values = make([]Expression, len(x.Values)) + for i := range ret.Values { + ret.Values[i] = x.Values[i].Copy() + } + return &ret +} + +func (x *List) Eval() Expression { + return x +} + +func (x *List) String() string { + valueStrings := make([]string, len(x.Values)) + for i, value := range x.Values { + valueStrings[i] = value.String() + } + return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos, + strings.Join(valueStrings, ", ")) +} + +func (x *List) Type() Type { return ListType } + +type String struct { + LiteralPos scanner.Position + Value string +} + +func (x *String) Pos() scanner.Position { return x.LiteralPos } +func (x *String) End() scanner.Position { return endPos(x.LiteralPos, len(x.Value)+2) } + +func (x *String) Copy() Expression { + ret := *x + return &ret +} + +func (x *String) Eval() Expression { + return x +} + +func (x *String) String() string { + return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos) +} + +func (x *String) Type() Type { + return StringType +} + +type Int64 struct { + LiteralPos scanner.Position + Value int64 + Token string +} + +func (x *Int64) Pos() scanner.Position { return x.LiteralPos } +func (x *Int64) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) } + +func (x *Int64) Copy() Expression { + ret := *x + return &ret +} + +func (x *Int64) Eval() Expression { + return x +} + +func (x *Int64) String() string { + return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos) +} + +func (x *Int64) Type() Type { + return Int64Type +} + +type Bool struct { + LiteralPos scanner.Position + Value bool + Token string +} + +func (x *Bool) Pos() scanner.Position { return x.LiteralPos } +func (x *Bool) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) } + +func (x *Bool) Copy() Expression { + ret := *x + return &ret +} + +func (x *Bool) Eval() Expression { + return x +} + +func (x *Bool) String() string { + return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos) +} + +func (x *Bool) Type() Type { + return BoolType +} + +type CommentGroup struct { + Comments []*Comment +} + +func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() } +func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() } + +type Comment struct { + Comment []string + Slash scanner.Position +} + +func (c Comment) Pos() scanner.Position { + return c.Slash +} + +func (c Comment) End() scanner.Position { + pos := c.Slash + for _, comment := range c.Comment { + pos.Offset += len(comment) + 1 + pos.Column = len(comment) + 1 + } + pos.Line += len(c.Comment) - 1 + return pos +} + +func (c Comment) String() string { + l := 0 + for _, comment := range c.Comment { + l += len(comment) + 1 + } + buf := make([]byte, 0, l) + for _, comment := range c.Comment { + buf = append(buf, comment...) + buf = append(buf, '\n') + } + + return string(buf) + "@" + c.Slash.String() +} + +// Return the text of the comment with // or /* and */ stripped +func (c Comment) Text() string { + l := 0 + for _, comment := range c.Comment { + l += len(comment) + 1 + } + buf := make([]byte, 0, l) + + blockComment := false + if strings.HasPrefix(c.Comment[0], "/*") { + blockComment = true + } + + for i, comment := range c.Comment { + if blockComment { + if i == 0 { + comment = strings.TrimPrefix(comment, "/*") + } + if i == len(c.Comment)-1 { + comment = strings.TrimSuffix(comment, "*/") + } + } else { + comment = strings.TrimPrefix(comment, "//") + } + buf = append(buf, comment...) + buf = append(buf, '\n') + } + + return string(buf) +} + +type NotEvaluated struct { + Position scanner.Position +} + +func (n NotEvaluated) Copy() Expression { + return NotEvaluated{Position: n.Position} +} + +func (n NotEvaluated) String() string { + return "Not Evaluated" +} + +func (n NotEvaluated) Type() Type { + return NotEvaluatedType +} + +func (n NotEvaluated) Eval() Expression { + return NotEvaluated{Position: n.Position} +} + +func (n NotEvaluated) Pos() scanner.Position { return n.Position } +func (n NotEvaluated) End() scanner.Position { return n.Position } + +func endPos(pos scanner.Position, n int) scanner.Position { + pos.Offset += n + pos.Column += n + return pos +} diff --git a/blueprint/parser/modify.go b/blueprint/parser/modify.go new file mode 100644 index 0000000..3051f66 --- /dev/null +++ b/blueprint/parser/modify.go @@ -0,0 +1,120 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "io" + "math" + "sort" +) + +func AddStringToList(list *List, s string) (modified bool) { + for _, v := range list.Values { + if v.Type() != StringType { + panic(fmt.Errorf("expected string in list, got %s", v.Type())) + } + + if sv, ok := v.(*String); ok && sv.Value == s { + // string already exists + return false + } + } + + list.Values = append(list.Values, &String{ + LiteralPos: list.RBracePos, + Value: s, + }) + + return true +} + +func RemoveStringFromList(list *List, s string) (modified bool) { + for i, v := range list.Values { + if v.Type() != StringType { + panic(fmt.Errorf("expected string in list, got %s", v.Type())) + } + + if sv, ok := v.(*String); ok && sv.Value == s { + list.Values = append(list.Values[:i], list.Values[i+1:]...) + return true + } + } + + return false +} + +// A Patch represents a region of a text buffer to be replaced [Start, End) and its Replacement +type Patch struct { + Start, End int + Replacement string +} + +// A PatchList is a list of sorted, non-overlapping Patch objects +type PatchList []Patch + +type PatchOverlapError error + +// Add adds a Patch to a PatchList. It returns a PatchOverlapError if the patch cannot be added. +func (list *PatchList) Add(start, end int, replacement string) error { + patch := Patch{start, end, replacement} + if patch.Start > patch.End { + return fmt.Errorf("invalid patch, start %d is after end %d", patch.Start, patch.End) + } + for _, p := range *list { + if (patch.Start >= p.Start && patch.Start < p.End) || + (patch.End >= p.Start && patch.End < p.End) || + (p.Start >= patch.Start && p.Start < patch.End) || + (p.Start == patch.Start && p.End == patch.End) { + return PatchOverlapError(fmt.Errorf("new patch %d-%d overlaps with existing patch %d-%d", + patch.Start, patch.End, p.Start, p.End)) + } + } + *list = append(*list, patch) + list.sort() + return nil +} + +func (list *PatchList) sort() { + sort.SliceStable(*list, + func(i, j int) bool { + return (*list)[i].Start < (*list)[j].Start + }) +} + +// Apply applies all the Patch objects in PatchList to the data from an input ReaderAt to an output Writer. +func (list *PatchList) Apply(in io.ReaderAt, out io.Writer) error { + var offset int64 + for _, patch := range *list { + toWrite := int64(patch.Start) - offset + written, err := io.Copy(out, io.NewSectionReader(in, offset, toWrite)) + if err != nil { + return err + } + offset += toWrite + if written != toWrite { + return fmt.Errorf("unexpected EOF at %d", offset) + } + + _, err = io.WriteString(out, patch.Replacement) + if err != nil { + return err + } + + offset += int64(patch.End - patch.Start) + } + _, err := io.Copy(out, io.NewSectionReader(in, offset, math.MaxInt64-offset)) + return err +} diff --git a/blueprint/parser/modify_test.go b/blueprint/parser/modify_test.go new file mode 100644 index 0000000..95fc293 --- /dev/null +++ b/blueprint/parser/modify_test.go @@ -0,0 +1,65 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bytes" + "testing" +) + +func TestPatchList(t *testing.T) { + expectOverlap := func(err error) { + t.Helper() + if _, ok := err.(PatchOverlapError); !ok { + t.Error("missing PatchOverlapError") + } + } + + expectOk := func(err error) { + t.Helper() + if err != nil { + t.Error(err) + } + } + + in := []byte("abcdefghijklmnopqrstuvwxyz") + + patchlist := PatchList{} + expectOk(patchlist.Add(0, 3, "ABC")) + expectOk(patchlist.Add(12, 15, "MNO")) + expectOk(patchlist.Add(24, 26, "Z")) + expectOk(patchlist.Add(15, 15, "_")) + + expectOverlap(patchlist.Add(0, 3, "x")) + expectOverlap(patchlist.Add(12, 13, "x")) + expectOverlap(patchlist.Add(13, 14, "x")) + expectOverlap(patchlist.Add(14, 15, "x")) + expectOverlap(patchlist.Add(11, 13, "x")) + expectOverlap(patchlist.Add(12, 15, "x")) + expectOverlap(patchlist.Add(11, 15, "x")) + expectOverlap(patchlist.Add(15, 15, "x")) + + if t.Failed() { + return + } + + buf := new(bytes.Buffer) + patchlist.Apply(bytes.NewReader(in), buf) + expected := "ABCdefghijklMNO_pqrstuvwxZ" + got := buf.String() + if got != expected { + t.Errorf("expected %q, got %q", expected, got) + } +} diff --git a/blueprint/parser/parser.go b/blueprint/parser/parser.go new file mode 100644 index 0000000..bb8817e --- /dev/null +++ b/blueprint/parser/parser.go @@ -0,0 +1,714 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "errors" + "fmt" + "io" + "sort" + "strconv" + "strings" + "text/scanner" +) + +var errTooManyErrors = errors.New("too many errors") + +const maxErrors = 1 + +type ParseError struct { + Err error + Pos scanner.Position +} + +func (e *ParseError) Error() string { + return fmt.Sprintf("%s: %s", e.Pos, e.Err) +} + +type File struct { + Name string + Defs []Definition + Comments []*CommentGroup +} + +func (f *File) Pos() scanner.Position { + return scanner.Position{ + Filename: f.Name, + Line: 1, + Column: 1, + Offset: 0, + } +} + +func (f *File) End() scanner.Position { + if len(f.Defs) > 0 { + return f.Defs[len(f.Defs)-1].End() + } + return noPos +} + +func parse(p *parser) (file *File, errs []error) { + defer func() { + if r := recover(); r != nil { + if r == errTooManyErrors { + errs = p.errors + return + } + panic(r) + } + }() + + defs := p.parseDefinitions() + p.accept(scanner.EOF) + errs = p.errors + comments := p.comments + + return &File{ + Name: p.scanner.Filename, + Defs: defs, + Comments: comments, + }, errs + +} + +func ParseAndEval(filename string, r io.Reader, scope *Scope) (file *File, errs []error) { + p := newParser(r, scope) + p.eval = true + p.scanner.Filename = filename + + return parse(p) +} + +func Parse(filename string, r io.Reader, scope *Scope) (file *File, errs []error) { + p := newParser(r, scope) + p.scanner.Filename = filename + + return parse(p) +} + +func ParseExpression(r io.Reader) (value Expression, errs []error) { + p := newParser(r, NewScope(nil)) + value = p.parseExpression() + p.accept(scanner.EOF) + errs = p.errors + return +} + +type parser struct { + scanner scanner.Scanner + tok rune + errors []error + scope *Scope + comments []*CommentGroup + eval bool +} + +func newParser(r io.Reader, scope *Scope) *parser { + p := &parser{} + p.scope = scope + p.scanner.Init(r) + p.scanner.Error = func(sc *scanner.Scanner, msg string) { + p.errorf(msg) + } + p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings | + scanner.ScanRawStrings | scanner.ScanComments + p.next() + return p +} + +func (p *parser) error(err error) { + pos := p.scanner.Position + if !pos.IsValid() { + pos = p.scanner.Pos() + } + err = &ParseError{ + Err: err, + Pos: pos, + } + p.errors = append(p.errors, err) + if len(p.errors) >= maxErrors { + panic(errTooManyErrors) + } +} + +func (p *parser) errorf(format string, args ...interface{}) { + p.error(fmt.Errorf(format, args...)) +} + +func (p *parser) accept(toks ...rune) bool { + for _, tok := range toks { + if p.tok != tok { + p.errorf("expected %s, found %s", scanner.TokenString(tok), + scanner.TokenString(p.tok)) + return false + } + p.next() + } + return true +} + +func (p *parser) next() { + if p.tok != scanner.EOF { + p.tok = p.scanner.Scan() + if p.tok == scanner.Comment { + var comments []*Comment + for p.tok == scanner.Comment { + lines := strings.Split(p.scanner.TokenText(), "\n") + if len(comments) > 0 && p.scanner.Position.Line > comments[len(comments)-1].End().Line+1 { + p.comments = append(p.comments, &CommentGroup{Comments: comments}) + comments = nil + } + comments = append(comments, &Comment{lines, p.scanner.Position}) + p.tok = p.scanner.Scan() + } + p.comments = append(p.comments, &CommentGroup{Comments: comments}) + } + } + return +} + +func (p *parser) parseDefinitions() (defs []Definition) { + for { + switch p.tok { + case scanner.Ident: + ident := p.scanner.TokenText() + pos := p.scanner.Position + + p.accept(scanner.Ident) + + switch p.tok { + case '+': + p.accept('+') + defs = append(defs, p.parseAssignment(ident, pos, "+=")) + case '=': + defs = append(defs, p.parseAssignment(ident, pos, "=")) + case '{', '(': + defs = append(defs, p.parseModule(ident, pos)) + default: + p.errorf("expected \"=\" or \"+=\" or \"{\" or \"(\", found %s", + scanner.TokenString(p.tok)) + } + case scanner.EOF: + return + default: + p.errorf("expected assignment or module definition, found %s", + scanner.TokenString(p.tok)) + return + } + } +} + +func (p *parser) parseAssignment(name string, namePos scanner.Position, + assigner string) (assignment *Assignment) { + + assignment = new(Assignment) + + pos := p.scanner.Position + if !p.accept('=') { + return + } + value := p.parseExpression() + + assignment.Name = name + assignment.NamePos = namePos + assignment.Value = value + assignment.OrigValue = value + assignment.EqualsPos = pos + assignment.Assigner = assigner + + if p.scope != nil { + if assigner == "+=" { + if old, local := p.scope.Get(assignment.Name); old == nil { + p.errorf("modified non-existent variable %q with +=", assignment.Name) + } else if !local { + p.errorf("modified non-local variable %q with +=", assignment.Name) + } else if old.Referenced { + p.errorf("modified variable %q with += after referencing", assignment.Name) + } else { + val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.EqualsPos) + if err != nil { + p.error(err) + } else { + old.Value = val + } + } + } else { + err := p.scope.Add(assignment) + if err != nil { + p.error(err) + } + } + } + + return +} + +func (p *parser) parseModule(typ string, typPos scanner.Position) *Module { + + compat := false + lbracePos := p.scanner.Position + if p.tok == '{' { + compat = true + } + + if !p.accept(p.tok) { + return nil + } + properties := p.parsePropertyList(true, compat) + rbracePos := p.scanner.Position + if !compat { + p.accept(')') + } else { + p.accept('}') + } + + return &Module{ + Type: typ, + TypePos: typPos, + Map: Map{ + Properties: properties, + LBracePos: lbracePos, + RBracePos: rbracePos, + }, + } +} + +func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) { + for p.tok == scanner.Ident { + property := p.parseProperty(isModule, compat) + properties = append(properties, property) + + if p.tok != ',' { + // There was no comma, so the list is done. + break + } + + p.accept(',') + } + + return +} + +func (p *parser) parseMapItemList() []*MapItem { + var items []*MapItem + // this is a map, not a struct, we only know we're at the end if we hit a '}' + for p.tok != '}' { + items = append(items, p.parseMapItem()) + + if p.tok != ',' { + // There was no comma, so the list is done. + break + } + p.accept(',') + } + return items +} + +func (p *parser) parseMapItem() *MapItem { + keyExpression := p.parseExpression() + if keyExpression.Type() != StringType { + p.errorf("only strings are supported as map keys: %s (%s)", keyExpression.Type(), keyExpression.String()) + } + key := keyExpression.(*String) + p.accept(':') + pos := p.scanner.Position + value := p.parseExpression() + return &MapItem{ + ColonPos: pos, + Key: key, + Value: value, + } +} + +func (p *parser) parseProperty(isModule, compat bool) (property *Property) { + property = new(Property) + + name := p.scanner.TokenText() + namePos := p.scanner.Position + p.accept(scanner.Ident) + pos := p.scanner.Position + + if isModule { + if compat { + if !p.accept(':') { + return + } + } else { + if !p.accept('=') { + return + } + } + } else { + if !p.accept(':') { + return + } + } + + value := p.parseExpression() + + property.Name = name + property.NamePos = namePos + property.Value = value + property.ColonPos = pos + + return +} + +func (p *parser) parseExpression() (value Expression) { + value = p.parseValue() + switch p.tok { + case '+': + return p.parseOperator(value) + case '-': + p.errorf("subtraction not supported: %s", p.scanner.String()) + return value + default: + return value + } +} + +func (p *parser) evaluateOperator(value1, value2 Expression, operator rune, + pos scanner.Position) (*Operator, error) { + + value := value1 + + if p.eval { + e1 := value1.Eval() + e2 := value2.Eval() + if e1.Type() != e2.Type() { + return nil, fmt.Errorf("mismatched type in operator %c: %s != %s", operator, + e1.Type(), e2.Type()) + } + + value = e1.Copy() + + switch operator { + case '+': + switch v := value.(type) { + case *String: + v.Value += e2.(*String).Value + case *Int64: + v.Value += e2.(*Int64).Value + v.Token = "" + case *List: + v.Values = append(v.Values, e2.(*List).Values...) + case *Map: + var err error + v.Properties, err = p.addMaps(v.Properties, e2.(*Map).Properties, pos) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("operator %c not supported on type %s", operator, v.Type()) + } + default: + panic("unknown operator " + string(operator)) + } + } + + return &Operator{ + Args: [2]Expression{value1, value2}, + Operator: operator, + OperatorPos: pos, + Value: value, + }, nil +} + +func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Property, error) { + ret := make([]*Property, 0, len(map1)) + + inMap1 := make(map[string]*Property) + inMap2 := make(map[string]*Property) + inBoth := make(map[string]*Property) + + for _, prop1 := range map1 { + inMap1[prop1.Name] = prop1 + } + + for _, prop2 := range map2 { + inMap2[prop2.Name] = prop2 + if _, ok := inMap1[prop2.Name]; ok { + inBoth[prop2.Name] = prop2 + } + } + + for _, prop1 := range map1 { + if prop2, ok := inBoth[prop1.Name]; ok { + var err error + newProp := *prop1 + newProp.Value, err = p.evaluateOperator(prop1.Value, prop2.Value, '+', pos) + if err != nil { + return nil, err + } + ret = append(ret, &newProp) + } else { + ret = append(ret, prop1) + } + } + + for _, prop2 := range map2 { + if _, ok := inBoth[prop2.Name]; !ok { + ret = append(ret, prop2) + } + } + + return ret, nil +} + +func (p *parser) parseOperator(value1 Expression) *Operator { + operator := p.tok + pos := p.scanner.Position + p.accept(operator) + + value2 := p.parseExpression() + + value, err := p.evaluateOperator(value1, value2, operator, pos) + if err != nil { + p.error(err) + return nil + } + + return value + +} + +func (p *parser) parseValue() (value Expression) { + switch p.tok { + case scanner.Ident: + return p.parseVariable() + case '-', scanner.Int: // Integer might have '-' sign ahead ('+' is only treated as operator now) + return p.parseIntValue() + case scanner.String, scanner.RawString: + return p.parseStringValue() + case '[': + return p.parseListValue() + case '{': + return p.parseMapValue() + default: + p.errorf("expected bool, list, or string value; found %s", + scanner.TokenString(p.tok)) + return + } +} + +func (p *parser) parseVariable() Expression { + var value Expression + + switch text := p.scanner.TokenText(); text { + case "true", "false": + value = &Bool{ + LiteralPos: p.scanner.Position, + Value: text == "true", + Token: text, + } + default: + if p.eval { + if assignment, local := p.scope.Get(text); assignment == nil { + p.errorf("variable %q is not set", text) + } else { + if local { + assignment.Referenced = true + } + value = assignment.Value + } + } else { + value = &NotEvaluated{} + } + value = &Variable{ + Name: text, + NamePos: p.scanner.Position, + Value: value, + } + } + + p.accept(scanner.Ident) + return value +} + +func (p *parser) parseStringValue() *String { + str, err := strconv.Unquote(p.scanner.TokenText()) + if err != nil { + p.errorf("couldn't parse string: %s", err) + return nil + } + + value := &String{ + LiteralPos: p.scanner.Position, + Value: str, + } + p.accept(p.tok) + return value +} + +func (p *parser) parseIntValue() *Int64 { + var str string + literalPos := p.scanner.Position + if p.tok == '-' { + str += string(p.tok) + p.accept(p.tok) + if p.tok != scanner.Int { + p.errorf("expected int; found %s", scanner.TokenString(p.tok)) + return nil + } + } + str += p.scanner.TokenText() + i, err := strconv.ParseInt(str, 10, 64) + if err != nil { + p.errorf("couldn't parse int: %s", err) + return nil + } + + value := &Int64{ + LiteralPos: literalPos, + Value: i, + Token: str, + } + p.accept(scanner.Int) + return value +} + +func (p *parser) parseListValue() *List { + lBracePos := p.scanner.Position + if !p.accept('[') { + return nil + } + + var elements []Expression + for p.tok != ']' { + element := p.parseExpression() + elements = append(elements, element) + + if p.tok != ',' { + // There was no comma, so the list is done. + break + } + + p.accept(',') + } + + rBracePos := p.scanner.Position + p.accept(']') + + return &List{ + LBracePos: lBracePos, + RBracePos: rBracePos, + Values: elements, + } +} + +func (p *parser) parseMapValue() *Map { + lBracePos := p.scanner.Position + if !p.accept('{') { + return nil + } + + var properties []*Property + var mapItems []*MapItem + // if the next item is an identifier, this is a property + if p.tok == scanner.Ident { + properties = p.parsePropertyList(false, false) + } else { + // otherwise, we assume that this is a map + mapItems = p.parseMapItemList() + } + + rBracePos := p.scanner.Position + p.accept('}') + + return &Map{ + LBracePos: lBracePos, + RBracePos: rBracePos, + Properties: properties, + MapItems: mapItems, + } +} + +type Scope struct { + vars map[string]*Assignment + inheritedVars map[string]*Assignment +} + +func NewScope(s *Scope) *Scope { + newScope := &Scope{ + vars: make(map[string]*Assignment), + inheritedVars: make(map[string]*Assignment), + } + + if s != nil { + for k, v := range s.vars { + newScope.inheritedVars[k] = v + } + for k, v := range s.inheritedVars { + newScope.inheritedVars[k] = v + } + } + + return newScope +} + +func (s *Scope) Add(assignment *Assignment) error { + if old, ok := s.vars[assignment.Name]; ok { + return fmt.Errorf("variable already set, previous assignment: %s", old) + } + + if old, ok := s.inheritedVars[assignment.Name]; ok { + return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old) + } + + s.vars[assignment.Name] = assignment + + return nil +} + +func (s *Scope) Remove(name string) { + delete(s.vars, name) + delete(s.inheritedVars, name) +} + +func (s *Scope) Get(name string) (*Assignment, bool) { + if a, ok := s.vars[name]; ok { + return a, true + } + + if a, ok := s.inheritedVars[name]; ok { + return a, false + } + + return nil, false +} + +func (s *Scope) String() string { + vars := []string{} + + for k := range s.vars { + vars = append(vars, k) + } + for k := range s.inheritedVars { + vars = append(vars, k) + } + + sort.Strings(vars) + + ret := []string{} + for _, v := range vars { + if assignment, ok := s.vars[v]; ok { + ret = append(ret, assignment.String()) + } else { + ret = append(ret, s.inheritedVars[v].String()) + } + } + + return strings.Join(ret, "\n") +} diff --git a/blueprint/parser/parser_test.go b/blueprint/parser/parser_test.go new file mode 100644 index 0000000..b32581e --- /dev/null +++ b/blueprint/parser/parser_test.go @@ -0,0 +1,1435 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bytes" + "reflect" + "strconv" + "strings" + "testing" + "text/scanner" +) + +func mkpos(offset, line, column int) scanner.Position { + return scanner.Position{ + Offset: offset, + Line: line, + Column: column, + } +} + +var validParseTestCases = []struct { + input string + defs []Definition + comments []*CommentGroup +}{ + {` + foo {} + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(8, 2, 8), + }, + }, + }, + nil, + }, + + {` + foo { + name: "abc", + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(27, 4, 3), + Properties: []*Property{ + { + Name: "name", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(16, 3, 8), + Value: &String{ + LiteralPos: mkpos(18, 3, 10), + Value: "abc", + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + foo { + isGood: true, + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(28, 4, 3), + Properties: []*Property{ + { + Name: "isGood", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(18, 3, 10), + Value: &Bool{ + LiteralPos: mkpos(20, 3, 12), + Value: true, + Token: "true", + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + foo { + num: 4, + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(22, 4, 3), + Properties: []*Property{ + { + Name: "num", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(15, 3, 7), + Value: &Int64{ + LiteralPos: mkpos(17, 3, 9), + Value: 4, + Token: "4", + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + foo { + stuff: ["asdf", "jkl;", "qwert", + "uiop", ` + "`bnm,\n`" + + `] + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(68, 6, 3), + Properties: []*Property{ + { + Name: "stuff", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(17, 3, 9), + Value: &List{ + LBracePos: mkpos(19, 3, 11), + RBracePos: mkpos(64, 5, 2), + Values: []Expression{ + &String{ + LiteralPos: mkpos(20, 3, 12), + Value: "asdf", + }, + &String{ + LiteralPos: mkpos(28, 3, 20), + Value: "jkl;", + }, + &String{ + LiteralPos: mkpos(36, 3, 28), + Value: "qwert", + }, + &String{ + LiteralPos: mkpos(49, 4, 5), + Value: "uiop", + }, + &String{ + LiteralPos: mkpos(57, 4, 13), + Value: "bnm,\n", + }, + }, + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + foo { + stuff: { + "key1": 1, + "key2": 2, + }, + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(59, 7, 3), + Properties: []*Property{ + { + Name: "stuff", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(17, 3, 9), + Value: &Map{ + LBracePos: mkpos(19, 3, 11), + RBracePos: mkpos(54, 6, 4), + MapItems: []*MapItem{ + &MapItem{ + ColonPos: mkpos(33, 4, 13), + Key: &String{ + LiteralPos: mkpos(25, 4, 5), + Value: "key1", + }, + Value: &Int64{ + LiteralPos: mkpos(33, 4, 13), + Value: 1, + Token: "1", + }, + }, + &MapItem{ + ColonPos: mkpos(48, 5, 13), + Key: &String{ + LiteralPos: mkpos(40, 5, 5), + Value: "key2", + }, + Value: &Int64{ + LiteralPos: mkpos(48, 5, 13), + Value: 2, + Token: "2", + }, + }, + }, + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + foo { + stuff: { + "key1": { + a: "abc", + }, + }, + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(65, 8, 3), + Properties: []*Property{ + { + Name: "stuff", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(17, 3, 9), + Value: &Map{ + LBracePos: mkpos(19, 3, 11), + RBracePos: mkpos(60, 7, 4), + MapItems: []*MapItem{ + &MapItem{ + ColonPos: mkpos(33, 4, 13), + Key: &String{ + LiteralPos: mkpos(25, 4, 5), + Value: "key1", + }, + Value: &Map{ + LBracePos: mkpos(33, 4, 13), + RBracePos: mkpos(54, 6, 5), + Properties: []*Property{ + &Property{ + Name: "a", + NamePos: mkpos(40, 5, 6), + ColonPos: mkpos(41, 5, 7), + Value: &String{ + LiteralPos: mkpos(43, 5, 9), + Value: "abc", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + nil, + }, + + { + ` + foo { + list_of_maps: [ + { + var: true, + name: "a", + }, + { + var: false, + name: "b", + }, + ], + } +`, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(127, 13, 3), + Properties: []*Property{ + { + Name: "list_of_maps", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(24, 3, 16), + Value: &List{ + LBracePos: mkpos(26, 3, 18), + RBracePos: mkpos(122, 12, 4), + Values: []Expression{ + &Map{ + LBracePos: mkpos(32, 4, 5), + RBracePos: mkpos(70, 7, 5), + Properties: []*Property{ + { + Name: "var", + NamePos: mkpos(39, 5, 6), + ColonPos: mkpos(42, 5, 9), + Value: &Bool{ + LiteralPos: mkpos(44, 5, 11), + Value: true, + Token: "true", + }, + }, + { + Name: "name", + NamePos: mkpos(55, 6, 6), + ColonPos: mkpos(59, 6, 10), + Value: &String{ + LiteralPos: mkpos(61, 6, 12), + Value: "a", + }, + }, + }, + }, + &Map{ + LBracePos: mkpos(77, 8, 5), + RBracePos: mkpos(116, 11, 5), + Properties: []*Property{ + { + Name: "var", + NamePos: mkpos(84, 9, 6), + ColonPos: mkpos(87, 9, 9), + Value: &Bool{ + LiteralPos: mkpos(89, 9, 11), + Value: false, + Token: "false", + }, + }, + { + Name: "name", + NamePos: mkpos(101, 10, 6), + ColonPos: mkpos(105, 10, 10), + Value: &String{ + LiteralPos: mkpos(107, 10, 12), + Value: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + nil, + }, + { + ` + foo { + list_of_lists: [ + [ "a", "b" ], + [ "c", "d" ] + ], + } +`, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(72, 7, 3), + Properties: []*Property{ + { + Name: "list_of_lists", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(25, 3, 17), + Value: &List{ + LBracePos: mkpos(27, 3, 19), + RBracePos: mkpos(67, 6, 4), + Values: []Expression{ + &List{ + LBracePos: mkpos(33, 4, 5), + RBracePos: mkpos(44, 4, 16), + Values: []Expression{ + &String{ + LiteralPos: mkpos(35, 4, 7), + Value: "a", + }, + &String{ + LiteralPos: mkpos(40, 4, 12), + Value: "b", + }, + }, + }, + &List{ + LBracePos: mkpos(51, 5, 5), + RBracePos: mkpos(62, 5, 16), + Values: []Expression{ + &String{ + LiteralPos: mkpos(53, 5, 7), + Value: "c", + }, + &String{ + LiteralPos: mkpos(58, 5, 12), + Value: "d", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + nil, + }, + {` + foo { + stuff: { + isGood: true, + name: "bar", + num: 36, + } + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(76, 8, 3), + Properties: []*Property{ + { + Name: "stuff", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(17, 3, 9), + Value: &Map{ + LBracePos: mkpos(19, 3, 11), + RBracePos: mkpos(72, 7, 4), + Properties: []*Property{ + { + Name: "isGood", + NamePos: mkpos(25, 4, 5), + ColonPos: mkpos(31, 4, 11), + Value: &Bool{ + LiteralPos: mkpos(33, 4, 13), + Value: true, + Token: "true", + }, + }, + { + Name: "name", + NamePos: mkpos(43, 5, 5), + ColonPos: mkpos(47, 5, 9), + Value: &String{ + LiteralPos: mkpos(49, 5, 11), + Value: "bar", + }, + }, + { + Name: "num", + NamePos: mkpos(60, 6, 5), + ColonPos: mkpos(63, 6, 8), + Value: &Int64{ + LiteralPos: mkpos(65, 6, 10), + Value: 36, + Token: "36", + }, + }, + }, + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + // comment1 + foo /* test */ { + // comment2 + isGood: true, // comment3 + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(17, 3, 3), + Map: Map{ + LBracePos: mkpos(32, 3, 18), + RBracePos: mkpos(81, 6, 3), + Properties: []*Property{ + { + Name: "isGood", + NamePos: mkpos(52, 5, 4), + ColonPos: mkpos(58, 5, 10), + Value: &Bool{ + LiteralPos: mkpos(60, 5, 12), + Value: true, + Token: "true", + }, + }, + }, + }, + }, + }, + []*CommentGroup{ + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"// comment1"}, + Slash: mkpos(3, 2, 3), + }, + }, + }, + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"/* test */"}, + Slash: mkpos(21, 3, 7), + }, + }, + }, + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"// comment2"}, + Slash: mkpos(37, 4, 4), + }, + }, + }, + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"// comment3"}, + Slash: mkpos(67, 5, 19), + }, + }, + }, + }, + }, + + {` + foo { + name: "abc", + num: 4, + } + + bar { + name: "def", + num: -5, + } + `, + []Definition{ + &Module{ + Type: "foo", + TypePos: mkpos(3, 2, 3), + Map: Map{ + LBracePos: mkpos(7, 2, 7), + RBracePos: mkpos(38, 5, 3), + Properties: []*Property{ + { + Name: "name", + NamePos: mkpos(12, 3, 4), + ColonPos: mkpos(16, 3, 8), + Value: &String{ + LiteralPos: mkpos(18, 3, 10), + Value: "abc", + }, + }, + { + Name: "num", + NamePos: mkpos(28, 4, 4), + ColonPos: mkpos(31, 4, 7), + Value: &Int64{ + LiteralPos: mkpos(33, 4, 9), + Value: 4, + Token: "4", + }, + }, + }, + }, + }, + &Module{ + Type: "bar", + TypePos: mkpos(43, 7, 3), + Map: Map{ + LBracePos: mkpos(47, 7, 7), + RBracePos: mkpos(79, 10, 3), + Properties: []*Property{ + { + Name: "name", + NamePos: mkpos(52, 8, 4), + ColonPos: mkpos(56, 8, 8), + Value: &String{ + LiteralPos: mkpos(58, 8, 10), + Value: "def", + }, + }, + { + Name: "num", + NamePos: mkpos(68, 9, 4), + ColonPos: mkpos(71, 9, 7), + Value: &Int64{ + LiteralPos: mkpos(73, 9, 9), + Value: -5, + Token: "-5", + }, + }, + }, + }, + }, + }, + nil, + }, + + {` + foo = "stuff" + bar = foo + baz = foo + bar + boo = baz + boo += foo + `, + []Definition{ + &Assignment{ + Name: "foo", + NamePos: mkpos(3, 2, 3), + EqualsPos: mkpos(7, 2, 7), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + OrigValue: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + Assigner: "=", + Referenced: true, + }, + &Assignment{ + Name: "bar", + NamePos: mkpos(19, 3, 3), + EqualsPos: mkpos(23, 3, 7), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + OrigValue: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + Assigner: "=", + Referenced: true, + }, + &Assignment{ + Name: "baz", + NamePos: mkpos(31, 4, 3), + EqualsPos: mkpos(35, 4, 7), + Value: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuffstuff", + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + }, + }, + }, + OrigValue: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuffstuff", + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + }, + }, + }, + Assigner: "=", + Referenced: true, + }, + &Assignment{ + Name: "boo", + NamePos: mkpos(49, 5, 3), + EqualsPos: mkpos(53, 5, 7), + Value: &Operator{ + Args: [2]Expression{ + &Variable{ + Name: "baz", + NamePos: mkpos(55, 5, 9), + Value: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuffstuff", + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + }, + }, + }, + }, + &Variable{ + Name: "foo", + NamePos: mkpos(68, 6, 10), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + }, + OperatorPos: mkpos(66, 6, 8), + Operator: '+', + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuffstuffstuff", + }, + }, + OrigValue: &Variable{ + Name: "baz", + NamePos: mkpos(55, 5, 9), + Value: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuffstuff", + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + }, + }, + }, + }, + Assigner: "=", + }, + &Assignment{ + Name: "boo", + NamePos: mkpos(61, 6, 3), + EqualsPos: mkpos(66, 6, 8), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(68, 6, 10), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + OrigValue: &Variable{ + Name: "foo", + NamePos: mkpos(68, 6, 10), + Value: &String{ + LiteralPos: mkpos(9, 2, 9), + Value: "stuff", + }, + }, + Assigner: "+=", + }, + }, + nil, + }, + + {` + baz = -4 + -5 + 6 + `, + []Definition{ + &Assignment{ + Name: "baz", + NamePos: mkpos(3, 2, 3), + EqualsPos: mkpos(7, 2, 7), + Value: &Operator{ + OperatorPos: mkpos(12, 2, 12), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: -3, + }, + Args: [2]Expression{ + &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: -4, + Token: "-4", + }, + &Operator{ + OperatorPos: mkpos(17, 2, 17), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(14, 2, 14), + Value: 1, + }, + Args: [2]Expression{ + &Int64{ + LiteralPos: mkpos(14, 2, 14), + Value: -5, + Token: "-5", + }, + &Int64{ + LiteralPos: mkpos(19, 2, 19), + Value: 6, + Token: "6", + }, + }, + }, + }, + }, + OrigValue: &Operator{ + OperatorPos: mkpos(12, 2, 12), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: -3, + }, + Args: [2]Expression{ + &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: -4, + Token: "-4", + }, + &Operator{ + OperatorPos: mkpos(17, 2, 17), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(14, 2, 14), + Value: 1, + }, + Args: [2]Expression{ + &Int64{ + LiteralPos: mkpos(14, 2, 14), + Value: -5, + Token: "-5", + }, + &Int64{ + LiteralPos: mkpos(19, 2, 19), + Value: 6, + Token: "6", + }, + }, + }, + }, + }, + Assigner: "=", + Referenced: false, + }, + }, + nil, + }, + + {` + foo = 1000000 + bar = foo + baz = foo + bar + boo = baz + boo += foo + `, + []Definition{ + &Assignment{ + Name: "foo", + NamePos: mkpos(3, 2, 3), + EqualsPos: mkpos(7, 2, 7), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + OrigValue: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + Assigner: "=", + Referenced: true, + }, + &Assignment{ + Name: "bar", + NamePos: mkpos(19, 3, 3), + EqualsPos: mkpos(23, 3, 7), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + OrigValue: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + Assigner: "=", + Referenced: true, + }, + &Assignment{ + Name: "baz", + NamePos: mkpos(31, 4, 3), + EqualsPos: mkpos(35, 4, 7), + Value: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 2000000, + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + }, + }, + }, + OrigValue: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 2000000, + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + }, + }, + }, + Assigner: "=", + Referenced: true, + }, + &Assignment{ + Name: "boo", + NamePos: mkpos(49, 5, 3), + EqualsPos: mkpos(53, 5, 7), + Value: &Operator{ + Args: [2]Expression{ + &Variable{ + Name: "baz", + NamePos: mkpos(55, 5, 9), + Value: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 2000000, + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + }, + }, + }, + }, + &Variable{ + Name: "foo", + NamePos: mkpos(68, 6, 10), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + }, + OperatorPos: mkpos(66, 6, 8), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 3000000, + }, + }, + OrigValue: &Variable{ + Name: "baz", + NamePos: mkpos(55, 5, 9), + Value: &Operator{ + OperatorPos: mkpos(41, 4, 13), + Operator: '+', + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 2000000, + }, + Args: [2]Expression{ + &Variable{ + Name: "foo", + NamePos: mkpos(37, 4, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + &Variable{ + Name: "bar", + NamePos: mkpos(43, 4, 15), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(25, 3, 9), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + }, + }, + }, + }, + Assigner: "=", + }, + &Assignment{ + Name: "boo", + NamePos: mkpos(61, 6, 3), + EqualsPos: mkpos(66, 6, 8), + Value: &Variable{ + Name: "foo", + NamePos: mkpos(68, 6, 10), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + OrigValue: &Variable{ + Name: "foo", + NamePos: mkpos(68, 6, 10), + Value: &Int64{ + LiteralPos: mkpos(9, 2, 9), + Value: 1000000, + Token: "1000000", + }, + }, + Assigner: "+=", + }, + }, + nil, + }, + + {` + // comment1 + // comment2 + + /* comment3 + comment4 */ + // comment5 + + /* comment6 */ /* comment7 */ // comment8 + `, + nil, + []*CommentGroup{ + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"// comment1"}, + Slash: mkpos(3, 2, 3), + }, + &Comment{ + Comment: []string{"// comment2"}, + Slash: mkpos(17, 3, 3), + }, + }, + }, + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"/* comment3", " comment4 */"}, + Slash: mkpos(32, 5, 3), + }, + &Comment{ + Comment: []string{"// comment5"}, + Slash: mkpos(63, 7, 3), + }, + }, + }, + { + Comments: []*Comment{ + &Comment{ + Comment: []string{"/* comment6 */"}, + Slash: mkpos(78, 9, 3), + }, + &Comment{ + Comment: []string{"/* comment7 */"}, + Slash: mkpos(93, 9, 18), + }, + &Comment{ + Comment: []string{"// comment8"}, + Slash: mkpos(108, 9, 33), + }, + }, + }, + }, + }, +} + +func TestParseValidInput(t *testing.T) { + for i, testCase := range validParseTestCases { + t.Run(strconv.Itoa(i), func(t *testing.T) { + r := bytes.NewBufferString(testCase.input) + file, errs := ParseAndEval("", r, NewScope(nil)) + if len(errs) != 0 { + t.Errorf("test case: %s", testCase.input) + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + if len(file.Defs) == len(testCase.defs) { + for i := range file.Defs { + if !reflect.DeepEqual(file.Defs[i], testCase.defs[i]) { + t.Errorf("test case: %s", testCase.input) + t.Errorf("incorrect definition %d:", i) + t.Errorf(" expected: %s", testCase.defs[i]) + t.Errorf(" got: %s", file.Defs[i]) + } + } + } else { + t.Errorf("test case: %s", testCase.input) + t.Errorf("length mismatch, expected %d definitions, got %d", + len(testCase.defs), len(file.Defs)) + } + + if len(file.Comments) == len(testCase.comments) { + for i := range file.Comments { + if !reflect.DeepEqual(file.Comments[i], testCase.comments[i]) { + t.Errorf("test case: %s", testCase.input) + t.Errorf("incorrect comment %d:", i) + t.Errorf(" expected: %s", testCase.comments[i]) + t.Errorf(" got: %s", file.Comments[i]) + } + } + } else { + t.Errorf("test case: %s", testCase.input) + t.Errorf("length mismatch, expected %d comments, got %d", + len(testCase.comments), len(file.Comments)) + } + }) + } +} + +// TODO: Test error strings + +func TestMapParserError(t *testing.T) { + input := + ` + foo { + stuff: { + 1: "value1", + 2: "value2", + }, + } + ` + expectedErr := `:4:6: only strings are supported as map keys: int64 ('\x01'@:4:5)` + _, errs := ParseAndEval("", bytes.NewBufferString(input), NewScope(nil)) + if len(errs) == 0 { + t.Fatalf("Expected errors, got none.") + } + for _, err := range errs { + if expectedErr != err.Error() { + t.Errorf("Unexpected err: %s", err) + } + } +} + +func TestParserEndPos(t *testing.T) { + in := ` + module { + string: "string", + stringexp: "string1" + "string2", + int: -1, + intexp: -1 + 2, + list: ["a", "b"], + listexp: ["c"] + ["d"], + multilinelist: [ + "e", + "f", + ], + map: { + prop: "abc", + }, + } + ` + + // Strip each line to make it easier to compute the previous "," from each property + lines := strings.Split(in, "\n") + for i := range lines { + lines[i] = strings.TrimSpace(lines[i]) + } + in = strings.Join(lines, "\n") + + r := bytes.NewBufferString(in) + + file, errs := ParseAndEval("", r, NewScope(nil)) + if len(errs) != 0 { + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + mod := file.Defs[0].(*Module) + modEnd := mkpos(len(in)-1, len(lines)-1, 2) + if mod.End() != modEnd { + t.Errorf("expected mod.End() %s, got %s", modEnd, mod.End()) + } + + nextPos := make([]scanner.Position, len(mod.Properties)) + for i := 0; i < len(mod.Properties)-1; i++ { + nextPos[i] = mod.Properties[i+1].Pos() + } + nextPos[len(mod.Properties)-1] = mod.RBracePos + + for i, cur := range mod.Properties { + endOffset := nextPos[i].Offset - len(",\n") + endLine := nextPos[i].Line - 1 + endColumn := len(lines[endLine-1]) // scanner.Position.Line is starts at 1 + endPos := mkpos(endOffset, endLine, endColumn) + if cur.End() != endPos { + t.Errorf("expected property %s End() %s@%d, got %s@%d", cur.Name, endPos, endPos.Offset, cur.End(), cur.End().Offset) + } + } +} + +func TestParserNotEvaluated(t *testing.T) { + // When parsing without evaluation, create variables correctly + scope := NewScope(nil) + input := "FOO=abc\n" + _, errs := Parse("", bytes.NewBufferString(input), scope) + if errs != nil { + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + assignment, found := scope.Get("FOO") + if !found { + t.Fatalf("Expected to find FOO after parsing %s", input) + } + if s := assignment.String(); strings.Contains(s, "PANIC") { + t.Errorf("Attempt to print FOO returned %s", s) + } +} diff --git a/blueprint/parser/printer.go b/blueprint/parser/printer.go new file mode 100644 index 0000000..f377505 --- /dev/null +++ b/blueprint/parser/printer.go @@ -0,0 +1,403 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "strconv" + "strings" + "text/scanner" + "unicode" +) + +var noPos scanner.Position + +type printer struct { + defs []Definition + comments []*CommentGroup + + curComment int + + pos scanner.Position + + pendingSpace bool + pendingNewline int + + output []byte + + indentList []int + wsBuf []byte + + skippedComments []*CommentGroup +} + +func newPrinter(file *File) *printer { + return &printer{ + defs: file.Defs, + comments: file.Comments, + indentList: []int{0}, + + // pendingNewLine is initialized to -1 to eat initial spaces if the first token is a comment + pendingNewline: -1, + + pos: scanner.Position{ + Line: 1, + }, + } +} + +func Print(file *File) ([]byte, error) { + p := newPrinter(file) + + for _, def := range p.defs { + p.printDef(def) + } + p.flush() + return p.output, nil +} + +func PrintExpression(expression Expression) ([]byte, error) { + dummyFile := &File{} + p := newPrinter(dummyFile) + p.printExpression(expression) + p.flush() + return p.output, nil +} + +func (p *printer) Print() ([]byte, error) { + for _, def := range p.defs { + p.printDef(def) + } + p.flush() + return p.output, nil +} + +func (p *printer) printDef(def Definition) { + if assignment, ok := def.(*Assignment); ok { + p.printAssignment(assignment) + } else if module, ok := def.(*Module); ok { + p.printModule(module) + } else { + panic("Unknown definition") + } +} + +func (p *printer) printAssignment(assignment *Assignment) { + p.printToken(assignment.Name, assignment.NamePos) + p.requestSpace() + p.printToken(assignment.Assigner, assignment.EqualsPos) + p.requestSpace() + p.printExpression(assignment.OrigValue) + p.requestNewline() +} + +func (p *printer) printModule(module *Module) { + p.printToken(module.Type, module.TypePos) + p.printMap(&module.Map) + p.requestDoubleNewline() +} + +func (p *printer) printExpression(value Expression) { + switch v := value.(type) { + case *Variable: + p.printToken(v.Name, v.NamePos) + case *Operator: + p.printOperator(v) + case *Bool: + var s string + if v.Value { + s = "true" + } else { + s = "false" + } + p.printToken(s, v.LiteralPos) + case *Int64: + p.printToken(strconv.FormatInt(v.Value, 10), v.LiteralPos) + case *String: + p.printToken(strconv.Quote(v.Value), v.LiteralPos) + case *List: + p.printList(v.Values, v.LBracePos, v.RBracePos) + case *Map: + p.printMap(v) + default: + panic(fmt.Errorf("bad property type: %s", value.Type())) + } +} + +func (p *printer) printList(list []Expression, pos, endPos scanner.Position) { + p.requestSpace() + p.printToken("[", pos) + if len(list) > 1 || pos.Line != endPos.Line || listHasMap(list) { + p.requestNewline() + p.indent(p.curIndent() + 4) + for _, value := range list { + p.printExpression(value) + p.printToken(",", noPos) + p.requestNewline() + } + p.unindent(endPos) + } else { + for _, value := range list { + p.printExpression(value) + } + } + p.printToken("]", endPos) +} + +func (p *printer) printMap(m *Map) { + p.requestSpace() + p.printToken("{", m.LBracePos) + if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line { + p.requestNewline() + p.indent(p.curIndent() + 4) + for _, prop := range m.Properties { + p.printProperty(prop) + p.printToken(",", noPos) + p.requestNewline() + } + p.unindent(m.RBracePos) + } + p.printToken("}", m.RBracePos) +} + +func (p *printer) printOperator(operator *Operator) { + p.printOperatorInternal(operator, true) +} + +func (p *printer) printOperatorInternal(operator *Operator, allowIndent bool) { + p.printExpression(operator.Args[0]) + p.requestSpace() + p.printToken(string(operator.Operator), operator.OperatorPos) + + indented := false + if operator.Args[0].End().Line == operator.Args[1].Pos().Line { + p.requestSpace() + } else { + if allowIndent { + indented = true + p.indent(p.curIndent() + 4) + } + p.requestNewline() + } + + if op, isOp := operator.Args[1].(*Operator); isOp { + p.printOperatorInternal(op, false) + } else { + p.printExpression(operator.Args[1]) + } + + if indented { + p.unindent(p.pos) + } +} + +func (p *printer) printProperty(property *Property) { + p.printToken(property.Name, property.NamePos) + p.printToken(":", property.ColonPos) + p.requestSpace() + p.printExpression(property.Value) +} + +// Print a single token, including any necessary comments or whitespace between +// this token and the previously printed token +func (p *printer) printToken(s string, pos scanner.Position) { + newline := p.pendingNewline != 0 + + if pos == noPos { + pos = p.pos + } + + if newline { + p.printEndOfLineCommentsBefore(pos) + p.requestNewlinesForPos(pos) + } + + p.printInLineCommentsBefore(pos) + + p.flushSpace() + + p.output = append(p.output, s...) + + p.pos = pos +} + +// Print any in-line (single line /* */) comments that appear _before_ pos +func (p *printer) printInLineCommentsBefore(pos scanner.Position) { + for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset { + c := p.comments[p.curComment] + if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 { + p.skippedComments = append(p.skippedComments, c) + } else { + p.printComment(c) + p.requestSpace() + } + p.curComment++ + } +} + +// Print any comments, including end of line comments, that appear _before_ the line specified +// by pos +func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) { + if len(p.skippedComments) > 0 { + for _, c := range p.skippedComments { + p.printComment(c) + } + p._requestNewline() + p.skippedComments = nil + } + for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line { + c := p.comments[p.curComment] + p.printComment(c) + p._requestNewline() + p.curComment++ + } +} + +// Compare the line numbers of the previous and current positions to determine whether extra +// newlines should be inserted. A second newline is allowed anywhere requestNewline() is called. +func (p *printer) requestNewlinesForPos(pos scanner.Position) bool { + if pos.Line > p.pos.Line { + p._requestNewline() + if pos.Line > p.pos.Line+1 { + p.pendingNewline = 2 + } + return true + } + + return false +} + +func (p *printer) requestSpace() { + p.pendingSpace = true +} + +// Ask for a newline to be inserted before the next token, but do not insert any comments. Used +// by the comment printers. +func (p *printer) _requestNewline() { + if p.pendingNewline == 0 { + p.pendingNewline = 1 + } +} + +// Ask for a newline to be inserted before the next token. Also inserts any end-of line comments +// for the current line +func (p *printer) requestNewline() { + pos := p.pos + pos.Line++ + p.printEndOfLineCommentsBefore(pos) + p._requestNewline() +} + +// Ask for two newlines to be inserted before the next token. Also inserts any end-of line comments +// for the current line +func (p *printer) requestDoubleNewline() { + p.requestNewline() + p.pendingNewline = 2 +} + +// Flush any pending whitespace, ignoring pending spaces if there is a pending newline +func (p *printer) flushSpace() { + if p.pendingNewline == 1 { + p.output = append(p.output, '\n') + p.pad(p.curIndent()) + } else if p.pendingNewline == 2 { + p.output = append(p.output, "\n\n"...) + p.pad(p.curIndent()) + } else if p.pendingSpace == true && p.pendingNewline != -1 { + p.output = append(p.output, ' ') + } + + p.pendingSpace = false + p.pendingNewline = 0 +} + +// Print a single comment, which may be a multi-line comment +func (p *printer) printComment(cg *CommentGroup) { + for _, comment := range cg.Comments { + if !p.requestNewlinesForPos(comment.Pos()) { + p.requestSpace() + } + for i, line := range comment.Comment { + line = strings.TrimRightFunc(line, unicode.IsSpace) + p.flushSpace() + if i != 0 { + lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) }) + lineIndent = max(lineIndent, p.curIndent()) + p.pad(lineIndent - p.curIndent()) + } + p.output = append(p.output, strings.TrimSpace(line)...) + if i < len(comment.Comment)-1 { + p._requestNewline() + } + } + p.pos = comment.End() + } +} + +// Print any comments that occur after the last token, and a trailing newline +func (p *printer) flush() { + for _, c := range p.skippedComments { + if !p.requestNewlinesForPos(c.Pos()) { + p.requestSpace() + } + p.printComment(c) + } + for p.curComment < len(p.comments) { + p.printComment(p.comments[p.curComment]) + p.curComment++ + } + p.output = append(p.output, '\n') +} + +// Print whitespace to pad from column l to column max +func (p *printer) pad(l int) { + if l > len(p.wsBuf) { + p.wsBuf = make([]byte, l) + for i := range p.wsBuf { + p.wsBuf[i] = ' ' + } + } + p.output = append(p.output, p.wsBuf[0:l]...) +} + +func (p *printer) indent(i int) { + p.indentList = append(p.indentList, i) +} + +func (p *printer) unindent(pos scanner.Position) { + p.printEndOfLineCommentsBefore(pos) + p.indentList = p.indentList[0 : len(p.indentList)-1] +} + +func (p *printer) curIndent() int { + return p.indentList[len(p.indentList)-1] +} + +func max(a, b int) int { + if a > b { + return a + } else { + return b + } +} + +func listHasMap(list []Expression) bool { + for _, value := range list { + if _, ok := value.(*Map); ok { + return true + } + } + return false +} diff --git a/blueprint/parser/printer_test.go b/blueprint/parser/printer_test.go new file mode 100644 index 0000000..c889b2a --- /dev/null +++ b/blueprint/parser/printer_test.go @@ -0,0 +1,485 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bytes" + "testing" +) + +var validPrinterTestCases = []struct { + input string + output string +}{ + { + input: ` +foo {} +`, + output: ` +foo {} +`, + }, + { + input: ` +foo(name= "abc",num= 4,) +`, + output: ` +foo { + name: "abc", + num: 4, +} +`, + }, + { + input: ` + foo { + stuff: ["asdf", "jkl;", "qwert", + "uiop", "bnm,"] + } + `, + output: ` +foo { + stuff: [ + "asdf", + "bnm,", + "jkl;", + "qwert", + "uiop", + ], +} +`, + }, + { + input: ` + var = "asdf" + foo { + stuff: ["asdf"] + var, + }`, + output: ` +var = "asdf" +foo { + stuff: ["asdf"] + var, +} +`, + }, + { + input: ` + var = "asdf" + foo { + stuff: [ + "asdf" + ] + var, + }`, + output: ` +var = "asdf" +foo { + stuff: [ + "asdf", + ] + var, +} +`, + }, + { + input: ` + var = "asdf" + foo { + stuff: ["asdf"] + var + ["qwert"], + }`, + output: ` +var = "asdf" +foo { + stuff: ["asdf"] + var + ["qwert"], +} +`, + }, + { + input: ` + foo { + stuff: { + isGood: true, + name: "bar", + num: 4, + } + } + `, + output: ` +foo { + stuff: { + isGood: true, + name: "bar", + num: 4, + }, +} +`, + }, + { + input: ` +// comment1 +foo { + // comment2 + isGood: true, // comment3 +} +`, + output: ` +// comment1 +foo { + // comment2 + isGood: true, // comment3 +} +`, + }, + { + input: ` +foo { + name: "abc", + num: 4, +} + +bar { + name: "def", + num: 5, +} + `, + output: ` +foo { + name: "abc", + num: 4, +} + +bar { + name: "def", + num: 5, +} +`, + }, + { + input: ` +foo { + bar: "b" + + "a" + + "z", +} +`, + output: ` +foo { + bar: "b" + + "a" + + "z", +} +`, + }, + { + input: ` +foo = "stuff" +bar = foo +baz = foo + bar +baz += foo +`, + output: ` +foo = "stuff" +bar = foo +baz = foo + bar +baz += foo +`, + }, + { + input: ` +foo = 100 +bar = foo +baz = foo + bar +baz += foo +`, + output: ` +foo = 100 +bar = foo +baz = foo + bar +baz += foo +`, + }, + { + input: ` +foo = "bar " + + "" + + "baz" +`, + output: ` +foo = "bar " + + "" + + "baz" +`, + }, + { + input: ` +//test +test /* test */ { + srcs: [ + /*"bootstrap/bootstrap.go", + "bootstrap/cleanup.go",*/ + "bootstrap/command.go", + "bootstrap/doc.go", //doc.go + "bootstrap/config.go", //config.go + ], + deps: ["libabc"], + incs: [] +} //test +//test +test2 { +} + + +//test3 +`, + output: ` +//test +test /* test */ { + srcs: [ + /*"bootstrap/bootstrap.go", + "bootstrap/cleanup.go",*/ + "bootstrap/command.go", + "bootstrap/config.go", //config.go + "bootstrap/doc.go", //doc.go + ], + deps: ["libabc"], + incs: [], +} //test +//test + +test2 { +} + +//test3 +`, + }, + { + input: ` +// test +module // test + + { + srcs + : [ + "src1.c", + "src2.c", + ], +//test +} +//test2 +`, + output: ` +// test +module { // test + + srcs: [ + "src1.c", + "src2.c", + ], + //test +} + +//test2 +`, + }, + { + input: ` +/*test { + test: true, +}*/ + +test { +/*test: true,*/ +} + +// This +/* Is *//* A */ // A +// A + +// Multiline +// Comment + +test {} + +// This +/* Is */ +// A +// Trailing + +// Multiline +// Comment +`, + output: ` +/*test { + test: true, +}*/ + +test { + /*test: true,*/ +} + +// This +/* Is */ /* A */ // A +// A + +// Multiline +// Comment + +test {} + +// This +/* Is */ +// A +// Trailing + +// Multiline +// Comment +`, + }, + { + input: ` +test // test + +// test +{ +} +`, + output: ` +test { // test + +// test + +} +`, + }, + { + input: ` +// test +stuff { + namespace: "google", + string_vars: [ + { + var: "one", + values: [ "one_a", "one_b",], + }, + { + var: "two", + values: [ "two_a", "two_b", ], + }, + ], +}`, + output: ` +// test +stuff { + namespace: "google", + string_vars: [ + { + var: "one", + values: [ + "one_a", + "one_b", + ], + }, + { + var: "two", + values: [ + "two_a", + "two_b", + ], + }, + ], +} +`, + }, + { + input: ` +// test +stuff { + namespace: "google", + list_of_lists: [ + [ "a", "b" ], + [ "c", "d" ], + ], +} +`, + output: ` +// test +stuff { + namespace: "google", + list_of_lists: [ + [ + "a", + "b", + ], + [ + "c", + "d", + ], + ], +} +`, + }, + { + input: ` +// test +stuff { + namespace: "google", + list_of_structs: [{ key1: "a", key2: "b" }], +} +`, + output: ` +// test +stuff { + namespace: "google", + list_of_structs: [ + { + key1: "a", + key2: "b", + }, + ], +} +`, + }, +} + +func TestPrinter(t *testing.T) { + for _, testCase := range validPrinterTestCases { + in := testCase.input[1:] + expected := testCase.output[1:] + + r := bytes.NewBufferString(in) + file, errs := Parse("", r, NewScope(nil)) + if len(errs) != 0 { + t.Errorf("test case: %s", in) + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + SortLists(file) + + got, err := Print(file) + if err != nil { + t.Errorf("test case: %s", in) + t.Errorf("unexpected error: %s", err) + t.FailNow() + } + + if string(got) != expected { + t.Errorf("test case: %s", in) + t.Errorf(" expected: %s", expected) + t.Errorf(" got: %s", string(got)) + } + } +} diff --git a/blueprint/parser/sort.go b/blueprint/parser/sort.go new file mode 100644 index 0000000..0379d45 --- /dev/null +++ b/blueprint/parser/sort.go @@ -0,0 +1,290 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "sort" + "strconv" + "strings" + "text/scanner" +) + +// numericStringLess compares two strings, returning a lexicographical comparison unless the first +// difference occurs in a sequence of 1 or more numeric characters, in which case it returns the +// numerical comparison of the two numbers. +func numericStringLess(a, b string) bool { + isNumeric := func(r rune) bool { return r >= '0' && r <= '9' } + isNotNumeric := func(r rune) bool { return !isNumeric(r) } + + minLength := len(a) + if len(b) < minLength { + minLength = len(b) + } + + byteIndex := 0 + numberStartIndex := -1 + + var aByte, bByte byte + + // Start with a byte comparison to find where the strings differ. + for ; byteIndex < minLength; byteIndex++ { + aByte, bByte = a[byteIndex], b[byteIndex] + if aByte != bByte { + break + } + byteIsNumeric := isNumeric(rune(aByte)) + if numberStartIndex != -1 && !byteIsNumeric { + numberStartIndex = -1 + } else if numberStartIndex == -1 && byteIsNumeric { + numberStartIndex = byteIndex + } + } + + // Handle the case where we reached the end of one or both strings without finding a difference. + if byteIndex == minLength { + if len(a) < len(b) { + // Reached the end of a. a is a prefix of b. + return true + } else { + // Reached the end of b. b is a prefix of a or b is equal to a. + return false + } + } + + aByteNumeric := isNumeric(rune(aByte)) + bByteNumeric := isNumeric(rune(bByte)) + + if (aByteNumeric || bByteNumeric) && !(aByteNumeric && bByteNumeric) && numberStartIndex != -1 { + // Only one of aByte and bByte is a number, but the previous byte was a number. That means + // one is a longer number with the same prefix, which must be numerically larger. If bByte + // is a number then the number in b is numerically larger than the number in a. + return bByteNumeric + } + + // If the bytes are both numbers do a numeric comparison. + if aByteNumeric && bByteNumeric { + // Extract the numbers from each string, starting from the first number after the last + // non-number. This won't be invalid utf8 because we are only looking for the bytes + //'0'-'9', which can only occur as single-byte runes in utf8. + if numberStartIndex == -1 { + numberStartIndex = byteIndex + } + aNumberString := a[numberStartIndex:] + bNumberString := b[numberStartIndex:] + + // Find the first non-number in each, using the full length if there isn't one. + endANumbers := strings.IndexFunc(aNumberString, isNotNumeric) + endBNumbers := strings.IndexFunc(bNumberString, isNotNumeric) + if endANumbers == -1 { + endANumbers = len(aNumberString) + } + if endBNumbers == -1 { + endBNumbers = len(bNumberString) + } + + // Convert each to an int. + aNumber, err := strconv.Atoi(aNumberString[:endANumbers]) + if err != nil { + panic(fmt.Errorf("failed to convert %q from %q to number: %w", + aNumberString[:endANumbers], a, err)) + } + bNumber, err := strconv.Atoi(bNumberString[:endBNumbers]) + if err != nil { + panic(fmt.Errorf("failed to convert %q from %q to number: %w", + bNumberString[:endBNumbers], b, err)) + } + // Do a numeric comparison. + return aNumber < bNumber + } + + // At least one is not a number, do a byte comparison. + return aByte < bByte +} + +func SortLists(file *File) { + for _, def := range file.Defs { + if assignment, ok := def.(*Assignment); ok { + sortListsInValue(assignment.Value, file) + } else if module, ok := def.(*Module); ok { + for _, prop := range module.Properties { + sortListsInValue(prop.Value, file) + } + } + } + sort.Sort(commentsByOffset(file.Comments)) +} + +func SortList(file *File, list *List) { + if !isListOfPrimitives(list.Values) { + return + } + for i := 0; i < len(list.Values); i++ { + // Find a set of values on contiguous lines + line := list.Values[i].Pos().Line + var j int + for j = i + 1; j < len(list.Values); j++ { + if list.Values[j].Pos().Line > line+1 { + break + } + line = list.Values[j].Pos().Line + } + + nextPos := list.End() + if j < len(list.Values) { + nextPos = list.Values[j].Pos() + } + sortSubList(list.Values[i:j], nextPos, file) + i = j - 1 + } +} + +func ListIsSorted(list *List) bool { + for i := 0; i < len(list.Values); i++ { + // Find a set of values on contiguous lines + line := list.Values[i].Pos().Line + var j int + for j = i + 1; j < len(list.Values); j++ { + if list.Values[j].Pos().Line > line+1 { + break + } + line = list.Values[j].Pos().Line + } + + if !subListIsSorted(list.Values[i:j]) { + return false + } + i = j - 1 + } + + return true +} + +func sortListsInValue(value Expression, file *File) { + switch v := value.(type) { + case *Variable: + // Nothing + case *Operator: + sortListsInValue(v.Args[0], file) + sortListsInValue(v.Args[1], file) + case *Map: + for _, p := range v.Properties { + sortListsInValue(p.Value, file) + } + case *List: + SortList(file, v) + } +} + +func sortSubList(values []Expression, nextPos scanner.Position, file *File) { + if !isListOfPrimitives(values) { + return + } + l := make([]elem, len(values)) + for i, v := range values { + s, ok := v.(*String) + if !ok { + panic("list contains non-string element") + } + n := nextPos + if i < len(values)-1 { + n = values[i+1].Pos() + } + l[i] = elem{s.Value, i, v.Pos(), n} + } + + sort.SliceStable(l, func(i, j int) bool { + return numericStringLess(l[i].s, l[j].s) + }) + + copyValues := append([]Expression{}, values...) + copyComments := make([]*CommentGroup, len(file.Comments)) + for i := range file.Comments { + cg := *file.Comments[i] + cg.Comments = make([]*Comment, len(cg.Comments)) + for j := range file.Comments[i].Comments { + c := *file.Comments[i].Comments[j] + cg.Comments[j] = &c + } + copyComments[i] = &cg + } + + curPos := values[0].Pos() + for i, e := range l { + values[i] = copyValues[e.i] + values[i].(*String).LiteralPos = curPos + for j, c := range copyComments { + if c.Pos().Offset > e.pos.Offset && c.Pos().Offset < e.nextPos.Offset { + file.Comments[j].Comments[0].Slash.Line = curPos.Line + file.Comments[j].Comments[0].Slash.Offset += values[i].Pos().Offset - e.pos.Offset + } + } + + curPos.Offset += e.nextPos.Offset - e.pos.Offset + curPos.Line++ + } +} + +func subListIsSorted(values []Expression) bool { + if !isListOfPrimitives(values) { + return true + } + prev := "" + for _, v := range values { + s, ok := v.(*String) + if !ok { + panic("list contains non-string element") + } + if prev != "" && numericStringLess(s.Value, prev) { + return false + } + prev = s.Value + } + + return true +} + +type elem struct { + s string + i int + pos scanner.Position + nextPos scanner.Position +} + +type commentsByOffset []*CommentGroup + +func (l commentsByOffset) Len() int { + return len(l) +} + +func (l commentsByOffset) Less(i, j int) bool { + return l[i].Pos().Offset < l[j].Pos().Offset +} + +func (l commentsByOffset) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func isListOfPrimitives(values []Expression) bool { + if len(values) == 0 { + return true + } + switch values[0].Type() { + case BoolType, StringType, Int64Type: + return true + default: + return false + } +} diff --git a/blueprint/parser/sort_test.go b/blueprint/parser/sort_test.go new file mode 100644 index 0000000..0a9e7fc --- /dev/null +++ b/blueprint/parser/sort_test.go @@ -0,0 +1,96 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import "testing" + +func Test_numericStringLess(t *testing.T) { + type args struct { + a string + b string + } + tests := []struct { + a, b string + }{ + {"a", "b"}, + {"aa", "ab"}, + {"aaa", "aba"}, + + {"1", "2"}, + {"1", "11"}, + {"2", "11"}, + {"1", "12"}, + + {"12", "101"}, + {"11", "102"}, + + {"0", "1"}, + {"0", "01"}, + {"1", "02"}, + {"01", "002"}, + {"001", "02"}, + } + + oneTest := func(a, b string, want bool) { + t.Helper() + if got := numericStringLess(a, b); got != want { + t.Errorf("want numericStringLess(%v, %v) = %v, got %v", a, b, want, got) + } + } + + for _, tt := range tests { + t.Run(tt.a+"<"+tt.b, func(t *testing.T) { + // a should be less than b + oneTest(tt.a, tt.b, true) + // b should not be less than a + oneTest(tt.b, tt.a, false) + // a should not be less than a + oneTest(tt.a, tt.a, false) + // b should not be less than b + oneTest(tt.b, tt.b, false) + + // The same should be true both strings are prefixed with an "a" + oneTest("a"+tt.a, "a"+tt.b, true) + oneTest("a"+tt.b, "a"+tt.a, false) + oneTest("a"+tt.a, "a"+tt.a, false) + oneTest("a"+tt.b, "a"+tt.b, false) + + // The same should be true both strings are suffixed with an "a" + oneTest(tt.a+"a", tt.b+"a", true) + oneTest(tt.b+"a", tt.a+"a", false) + oneTest(tt.a+"a", tt.a+"a", false) + oneTest(tt.b+"a", tt.b+"a", false) + + // The same should be true both strings are suffixed with a "1" + oneTest(tt.a+"1", tt.b+"1", true) + oneTest(tt.b+"1", tt.a+"1", false) + oneTest(tt.a+"1", tt.a+"1", false) + oneTest(tt.b+"1", tt.b+"1", false) + + // The same should be true both strings are prefixed with a "0" + oneTest("0"+tt.a, "0"+tt.b, true) + oneTest("0"+tt.b, "0"+tt.a, false) + oneTest("0"+tt.a, "0"+tt.a, false) + oneTest("0"+tt.b, "0"+tt.b, false) + + // The same should be true both strings are suffixed with a "0" + oneTest(tt.a+"0", tt.b+"0", true) + oneTest(tt.b+"0", tt.a+"0", false) + oneTest(tt.a+"0", tt.a+"0", false) + oneTest(tt.b+"0", tt.b+"0", false) + + }) + } +} diff --git a/blueprint/pathtools/fs.go b/blueprint/pathtools/fs.go new file mode 100644 index 0000000..b959289 --- /dev/null +++ b/blueprint/pathtools/fs.go @@ -0,0 +1,629 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pathtools + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "syscall" + "time" +) + +// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go" + +type ShouldFollowSymlinks bool + +const ( + FollowSymlinks = ShouldFollowSymlinks(true) + DontFollowSymlinks = ShouldFollowSymlinks(false) +) + +var OsFs FileSystem = &osFs{} + +func MockFs(files map[string][]byte) FileSystem { + fs := &mockFs{ + files: make(map[string][]byte, len(files)), + dirs: make(map[string]bool), + symlinks: make(map[string]string), + all: []string(nil), + } + + for f, b := range files { + if tokens := strings.SplitN(f, "->", 2); len(tokens) == 2 { + fs.symlinks[strings.TrimSpace(tokens[0])] = strings.TrimSpace(tokens[1]) + continue + } + + fs.files[filepath.Clean(f)] = b + dir := filepath.Dir(f) + for dir != "." && dir != "/" { + fs.dirs[dir] = true + dir = filepath.Dir(dir) + } + fs.dirs[dir] = true + } + + fs.dirs["."] = true + fs.dirs["/"] = true + + for f := range fs.files { + fs.all = append(fs.all, f) + } + + for d := range fs.dirs { + fs.all = append(fs.all, d) + } + + for s := range fs.symlinks { + fs.all = append(fs.all, s) + } + + sort.Strings(fs.all) + + return fs +} + +type ReaderAtSeekerCloser interface { + io.Reader + io.ReaderAt + io.Seeker + io.Closer +} + +type FileSystem interface { + // Open opens a file for reading. Follows symlinks. + Open(name string) (ReaderAtSeekerCloser, error) + + // Exists returns whether the file exists and whether it is a directory. Follows symlinks. + Exists(name string) (bool, bool, error) + + Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) + glob(pattern string) (matches []string, err error) + + // IsDir returns true if the path points to a directory, false it it points to a file. Follows symlinks. + // Returns os.ErrNotExist if the path does not exist or is a symlink to a path that does not exist. + IsDir(name string) (bool, error) + + // IsSymlink returns true if the path points to a symlink, even if that symlink points to a path that does + // not exist. Returns os.ErrNotExist if the path does not exist. + IsSymlink(name string) (bool, error) + + // Lstat returns info on a file without following symlinks. + Lstat(name string) (os.FileInfo, error) + + // Lstat returns info on a file. + Stat(name string) (os.FileInfo, error) + + // ListDirsRecursive returns a list of all the directories in a path, following symlinks if requested. + ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) + + // ReadDirNames returns a list of everything in a directory. + ReadDirNames(name string) ([]string, error) + + // Readlink returns the destination of the named symbolic link. + Readlink(name string) (string, error) +} + +// osFs implements FileSystem using the local disk. +type osFs struct { + srcDir string + openFilesChan chan bool +} + +func NewOsFs(path string) FileSystem { + // Darwin has a default limit of 256 open files, rate limit open files to 200 + limit := 200 + return &osFs{ + srcDir: path, + openFilesChan: make(chan bool, limit), + } +} + +func (fs *osFs) acquire() { + if fs.openFilesChan != nil { + fs.openFilesChan <- true + } +} + +func (fs *osFs) release() { + if fs.openFilesChan != nil { + <-fs.openFilesChan + } +} + +func (fs *osFs) toAbs(path string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(fs.srcDir, path) +} + +func (fs *osFs) removeSrcDirPrefix(path string) string { + if fs.srcDir == "" { + return path + } + rel, err := filepath.Rel(fs.srcDir, path) + if err != nil { + panic(fmt.Errorf("unexpected failure in removeSrcDirPrefix filepath.Rel(%s, %s): %s", + fs.srcDir, path, err)) + } + if strings.HasPrefix(rel, "../") { + panic(fmt.Errorf("unexpected relative path outside directory in removeSrcDirPrefix filepath.Rel(%s, %s): %s", + fs.srcDir, path, rel)) + } + return rel +} + +func (fs *osFs) removeSrcDirPrefixes(paths []string) []string { + if fs.srcDir != "" { + for i, path := range paths { + paths[i] = fs.removeSrcDirPrefix(path) + } + } + return paths +} + +// OsFile wraps an os.File to also release open file descriptors semaphore on close +type OsFile struct { + *os.File + fs *osFs +} + +// Close closes file and releases the open file descriptor semaphore +func (f *OsFile) Close() error { + err := f.File.Close() + f.fs.release() + return err +} + +func (fs *osFs) Open(name string) (ReaderAtSeekerCloser, error) { + fs.acquire() + f, err := os.Open(fs.toAbs(name)) + if err != nil { + return nil, err + } + return &OsFile{f, fs}, nil +} + +func (fs *osFs) Exists(name string) (bool, bool, error) { + fs.acquire() + defer fs.release() + stat, err := os.Stat(fs.toAbs(name)) + if err == nil { + return true, stat.IsDir(), nil + } else if os.IsNotExist(err) { + return false, false, nil + } else { + return false, false, err + } +} + +func (fs *osFs) IsDir(name string) (bool, error) { + fs.acquire() + defer fs.release() + info, err := os.Stat(fs.toAbs(name)) + if err != nil { + return false, err + } + return info.IsDir(), nil +} + +func (fs *osFs) IsSymlink(name string) (bool, error) { + fs.acquire() + defer fs.release() + if info, err := os.Lstat(fs.toAbs(name)); err != nil { + return false, err + } else { + return info.Mode()&os.ModeSymlink != 0, nil + } +} + +func (fs *osFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) { + return startGlob(fs, pattern, excludes, follow) +} + +func (fs *osFs) glob(pattern string) ([]string, error) { + fs.acquire() + defer fs.release() + paths, err := filepath.Glob(fs.toAbs(pattern)) + fs.removeSrcDirPrefixes(paths) + return paths, err +} + +func (fs *osFs) Lstat(path string) (stats os.FileInfo, err error) { + fs.acquire() + defer fs.release() + return os.Lstat(fs.toAbs(path)) +} + +func (fs *osFs) Stat(path string) (stats os.FileInfo, err error) { + fs.acquire() + defer fs.release() + return os.Stat(fs.toAbs(path)) +} + +// Returns a list of all directories under dir +func (fs *osFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) { + return listDirsRecursive(fs, name, follow) +} + +func (fs *osFs) ReadDirNames(name string) ([]string, error) { + fs.acquire() + defer fs.release() + dir, err := os.Open(fs.toAbs(name)) + if err != nil { + return nil, err + } + defer dir.Close() + + contents, err := dir.Readdirnames(-1) + if err != nil { + return nil, err + } + + sort.Strings(contents) + return contents, nil +} + +func (fs *osFs) Readlink(name string) (string, error) { + fs.acquire() + defer fs.release() + return os.Readlink(fs.toAbs(name)) +} + +type mockFs struct { + files map[string][]byte + dirs map[string]bool + symlinks map[string]string + all []string +} + +func (m *mockFs) followSymlinks(name string) string { + dir, file := quickSplit(name) + if dir != "." && dir != "/" { + dir = m.followSymlinks(dir) + } + name = filepath.Join(dir, file) + + for i := 0; i < 255; i++ { + i++ + if i > 255 { + panic("symlink loop") + } + to, exists := m.symlinks[name] + if !exists { + break + } + if filepath.IsAbs(to) { + name = to + } else { + name = filepath.Join(dir, to) + } + } + return name +} + +func (m *mockFs) Open(name string) (ReaderAtSeekerCloser, error) { + name = filepath.Clean(name) + name = m.followSymlinks(name) + if f, ok := m.files[name]; ok { + return struct { + io.Closer + *bytes.Reader + }{ + ioutil.NopCloser(nil), + bytes.NewReader(f), + }, nil + } + + return nil, &os.PathError{ + Op: "open", + Path: name, + Err: os.ErrNotExist, + } +} + +func (m *mockFs) Exists(name string) (bool, bool, error) { + name = filepath.Clean(name) + name = m.followSymlinks(name) + if _, ok := m.files[name]; ok { + return ok, false, nil + } + if _, ok := m.dirs[name]; ok { + return ok, true, nil + } + return false, false, nil +} + +func (m *mockFs) IsDir(name string) (bool, error) { + dir := filepath.Dir(name) + if dir != "." && dir != "/" { + isDir, err := m.IsDir(dir) + + if serr, ok := err.(*os.SyscallError); ok && serr.Err == syscall.ENOTDIR { + isDir = false + } else if err != nil { + return false, err + } + + if !isDir { + return false, os.NewSyscallError("stat "+name, syscall.ENOTDIR) + } + } + + name = filepath.Clean(name) + name = m.followSymlinks(name) + + if _, ok := m.dirs[name]; ok { + return true, nil + } + if _, ok := m.files[name]; ok { + return false, nil + } + return false, os.ErrNotExist +} + +func (m *mockFs) IsSymlink(name string) (bool, error) { + dir, file := quickSplit(name) + dir = m.followSymlinks(dir) + name = filepath.Join(dir, file) + + if _, isSymlink := m.symlinks[name]; isSymlink { + return true, nil + } + if _, isDir := m.dirs[name]; isDir { + return false, nil + } + if _, isFile := m.files[name]; isFile { + return false, nil + } + return false, os.ErrNotExist +} + +func (m *mockFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) { + return startGlob(m, pattern, excludes, follow) +} + +func unescapeGlob(s string) string { + i := 0 + for i < len(s) { + if s[i] == '\\' { + s = s[:i] + s[i+1:] + } else { + i++ + } + } + return s +} + +func (m *mockFs) glob(pattern string) ([]string, error) { + dir, file := quickSplit(pattern) + + dir = unescapeGlob(dir) + toDir := m.followSymlinks(dir) + + var matches []string + for _, f := range m.all { + fDir, fFile := quickSplit(f) + if toDir == fDir { + match, err := filepath.Match(file, fFile) + if err != nil { + return nil, err + } + if (f == "." || f == "/") && f != pattern { + // filepath.Glob won't return "." or "/" unless the pattern was "." or "/" + match = false + } + if match { + matches = append(matches, filepath.Join(dir, fFile)) + } + } + } + return matches, nil +} + +type mockStat struct { + name string + size int64 + mode os.FileMode +} + +func (ms *mockStat) Name() string { return ms.name } +func (ms *mockStat) IsDir() bool { return ms.Mode().IsDir() } +func (ms *mockStat) Size() int64 { return ms.size } +func (ms *mockStat) Mode() os.FileMode { return ms.mode } +func (ms *mockStat) ModTime() time.Time { return time.Time{} } +func (ms *mockStat) Sys() interface{} { return nil } + +func (m *mockFs) Lstat(name string) (os.FileInfo, error) { + dir, file := quickSplit(name) + dir = m.followSymlinks(dir) + name = filepath.Join(dir, file) + + ms := mockStat{ + name: file, + } + + if symlink, isSymlink := m.symlinks[name]; isSymlink { + ms.mode = os.ModeSymlink + ms.size = int64(len(symlink)) + } else if _, isDir := m.dirs[name]; isDir { + ms.mode = os.ModeDir + } else if _, isFile := m.files[name]; isFile { + ms.mode = 0 + ms.size = int64(len(m.files[name])) + } else { + return nil, os.ErrNotExist + } + + return &ms, nil +} + +func (m *mockFs) Stat(name string) (os.FileInfo, error) { + name = filepath.Clean(name) + origName := name + name = m.followSymlinks(name) + + ms := mockStat{ + name: filepath.Base(origName), + size: int64(len(m.files[name])), + } + + if _, isDir := m.dirs[name]; isDir { + ms.mode = os.ModeDir + } else if _, isFile := m.files[name]; isFile { + ms.mode = 0 + ms.size = int64(len(m.files[name])) + } else { + return nil, os.ErrNotExist + } + + return &ms, nil +} + +func (m *mockFs) ReadDirNames(name string) ([]string, error) { + name = filepath.Clean(name) + name = m.followSymlinks(name) + + exists, isDir, err := m.Exists(name) + if err != nil { + return nil, err + } + if !exists { + return nil, os.ErrNotExist + } + if !isDir { + return nil, os.NewSyscallError("readdir", syscall.ENOTDIR) + } + + var ret []string + for _, f := range m.all { + dir, file := quickSplit(f) + if dir == name && len(file) > 0 && file[0] != '.' { + ret = append(ret, file) + } + } + return ret, nil +} + +func (m *mockFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) ([]string, error) { + return listDirsRecursive(m, name, follow) +} + +func (m *mockFs) Readlink(name string) (string, error) { + dir, file := quickSplit(name) + dir = m.followSymlinks(dir) + + origName := name + name = filepath.Join(dir, file) + + if dest, isSymlink := m.symlinks[name]; isSymlink { + return dest, nil + } + + if exists, _, err := m.Exists(name); err != nil { + return "", err + } else if !exists { + return "", os.ErrNotExist + } else { + return "", os.NewSyscallError("readlink: "+origName, syscall.EINVAL) + } +} + +func listDirsRecursive(fs FileSystem, name string, follow ShouldFollowSymlinks) ([]string, error) { + name = filepath.Clean(name) + + isDir, err := fs.IsDir(name) + if err != nil { + return nil, err + } + + if !isDir { + return nil, nil + } + + dirs := []string{name} + + subDirs, err := listDirsRecursiveRelative(fs, name, follow, 0) + if err != nil { + return nil, err + } + + for _, d := range subDirs { + dirs = append(dirs, filepath.Join(name, d)) + } + + return dirs, nil +} + +func listDirsRecursiveRelative(fs FileSystem, name string, follow ShouldFollowSymlinks, depth int) ([]string, error) { + depth++ + if depth > 255 { + return nil, fmt.Errorf("too many symlinks") + } + contents, err := fs.ReadDirNames(name) + if err != nil { + return nil, err + } + + var dirs []string + for _, f := range contents { + if f[0] == '.' { + continue + } + f = filepath.Join(name, f) + var info os.FileInfo + if follow == DontFollowSymlinks { + info, err = fs.Lstat(f) + if err != nil { + continue + } + if info.Mode()&os.ModeSymlink != 0 { + continue + } + } else { + info, err = fs.Stat(f) + if err != nil { + continue + } + } + if info.IsDir() { + dirs = append(dirs, f) + subDirs, err := listDirsRecursiveRelative(fs, f, follow, depth) + if err != nil { + return nil, err + } + for _, s := range subDirs { + dirs = append(dirs, filepath.Join(f, s)) + } + } + } + + for i, d := range dirs { + rel, err := filepath.Rel(name, d) + if err != nil { + return nil, err + } + dirs[i] = rel + } + + return dirs, nil +} diff --git a/blueprint/pathtools/fs_test.go b/blueprint/pathtools/fs_test.go new file mode 100644 index 0000000..3b4d4d0 --- /dev/null +++ b/blueprint/pathtools/fs_test.go @@ -0,0 +1,567 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pathtools + +import ( + "os" + "path/filepath" + "reflect" + "syscall" + "testing" +) + +const testdataDir = "testdata/dangling" + +func symlinkMockFs() *mockFs { + files := []string{ + "a/a/a", + "a/a/f -> ../../f", + "b -> a", + "c -> a/a", + "d -> c", + "e -> a/a/a", + "dangling -> missing", + "f", + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + mockFiles[filepath.Join(pwd, "testdata", f)] = nil + } + + return MockFs(mockFiles).(*mockFs) +} + +func TestMockFs_followSymlinks(t *testing.T) { + + testCases := []struct { + from, to string + }{ + {".", "."}, + {"/", "/"}, + + {"a", "a"}, + {"a/a", "a/a"}, + {"a/a/a", "a/a/a"}, + {"a/a/f", "f"}, + + {"b", "a"}, + {"b/a", "a/a"}, + {"b/a/a", "a/a/a"}, + {"b/a/f", "f"}, + + {"c/a", "a/a/a"}, + {"c/f", "f"}, + + {"d/a", "a/a/a"}, + {"d/f", "f"}, + + {"e", "a/a/a"}, + + {"f", "f"}, + + {"dangling", "missing"}, + + {"a/missing", "a/missing"}, + {"b/missing", "a/missing"}, + {"c/missing", "a/a/missing"}, + {"d/missing", "a/a/missing"}, + {"e/missing", "a/a/a/missing"}, + {"dangling/missing", "missing/missing"}, + + {"a/missing/missing", "a/missing/missing"}, + {"b/missing/missing", "a/missing/missing"}, + {"c/missing/missing", "a/a/missing/missing"}, + {"d/missing/missing", "a/a/missing/missing"}, + {"e/missing/missing", "a/a/a/missing/missing"}, + {"dangling/missing/missing", "missing/missing/missing"}, + } + + mock := symlinkMockFs() + + for _, test := range testCases { + t.Run(test.from, func(t *testing.T) { + got := mock.followSymlinks(test.from) + if got != test.to { + t.Errorf("want: %v, got %v", test.to, got) + } + }) + } +} + +func runTestFs(t *testing.T, f func(t *testing.T, fs FileSystem, dir string)) { + mock := symlinkMockFs() + wd, _ := os.Getwd() + absTestDataDir := filepath.Join(wd, testdataDir) + + run := func(t *testing.T, fs FileSystem) { + t.Run("relpath", func(t *testing.T) { + f(t, fs, "") + }) + t.Run("abspath", func(t *testing.T) { + f(t, fs, absTestDataDir) + }) + } + + t.Run("mock", func(t *testing.T) { + f(t, mock, "") + }) + + t.Run("os", func(t *testing.T) { + os.Chdir(absTestDataDir) + defer os.Chdir(wd) + run(t, OsFs) + }) + + t.Run("os relative srcDir", func(t *testing.T) { + run(t, NewOsFs(testdataDir)) + }) + + t.Run("os absolute srcDir", func(t *testing.T) { + os.Chdir("/") + defer os.Chdir(wd) + run(t, NewOsFs(filepath.Join(wd, testdataDir))) + }) + +} + +func TestFs_IsDir(t *testing.T) { + testCases := []struct { + name string + isDir bool + err error + }{ + {"a", true, nil}, + {"a/a", true, nil}, + {"a/a/a", false, nil}, + {"a/a/f", false, nil}, + + {"b", true, nil}, + {"b/a", true, nil}, + {"b/a/a", false, nil}, + {"b/a/f", false, nil}, + + {"c", true, nil}, + {"c/a", false, nil}, + {"c/f", false, nil}, + + {"d", true, nil}, + {"d/a", false, nil}, + {"d/f", false, nil}, + + {"e", false, nil}, + + {"f", false, nil}, + + {"dangling", false, os.ErrNotExist}, + + {"a/missing", false, os.ErrNotExist}, + {"b/missing", false, os.ErrNotExist}, + {"c/missing", false, os.ErrNotExist}, + {"d/missing", false, os.ErrNotExist}, + {"e/missing", false, syscall.ENOTDIR}, + {"dangling/missing", false, os.ErrNotExist}, + + {"a/missing/missing", false, os.ErrNotExist}, + {"b/missing/missing", false, os.ErrNotExist}, + {"c/missing/missing", false, os.ErrNotExist}, + {"d/missing/missing", false, os.ErrNotExist}, + {"e/missing/missing", false, syscall.ENOTDIR}, + {"dangling/missing/missing", false, os.ErrNotExist}, + + {"c/f/missing", false, syscall.ENOTDIR}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := fs.IsDir(filepath.Join(dir, test.name)) + checkErr(t, test.err, err) + if got != test.isDir { + t.Errorf("want: %v, got %v", test.isDir, got) + } + }) + } + }) +} + +func TestFs_ListDirsRecursiveFollowSymlinks(t *testing.T) { + testCases := []struct { + name string + dirs []string + err error + }{ + {".", []string{".", "a", "a/a", "b", "b/a", "c", "d"}, nil}, + + {"a", []string{"a", "a/a"}, nil}, + {"a/a", []string{"a/a"}, nil}, + {"a/a/a", nil, nil}, + + {"b", []string{"b", "b/a"}, nil}, + {"b/a", []string{"b/a"}, nil}, + {"b/a/a", nil, nil}, + + {"c", []string{"c"}, nil}, + {"c/a", nil, nil}, + + {"d", []string{"d"}, nil}, + {"d/a", nil, nil}, + + {"e", nil, nil}, + + {"dangling", nil, os.ErrNotExist}, + + {"missing", nil, os.ErrNotExist}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := fs.ListDirsRecursive(filepath.Join(dir, test.name), FollowSymlinks) + checkErr(t, test.err, err) + want := append([]string(nil), test.dirs...) + for i := range want { + want[i] = filepath.Join(dir, want[i]) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("want: %v, got %v", want, got) + } + }) + } + }) +} + +func TestFs_ListDirsRecursiveDontFollowSymlinks(t *testing.T) { + testCases := []struct { + name string + dirs []string + err error + }{ + {".", []string{".", "a", "a/a"}, nil}, + + {"a", []string{"a", "a/a"}, nil}, + {"a/a", []string{"a/a"}, nil}, + {"a/a/a", nil, nil}, + + {"b", []string{"b", "b/a"}, nil}, + {"b/a", []string{"b/a"}, nil}, + {"b/a/a", nil, nil}, + + {"c", []string{"c"}, nil}, + {"c/a", nil, nil}, + + {"d", []string{"d"}, nil}, + {"d/a", nil, nil}, + + {"e", nil, nil}, + + {"dangling", nil, os.ErrNotExist}, + + {"missing", nil, os.ErrNotExist}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := fs.ListDirsRecursive(filepath.Join(dir, test.name), DontFollowSymlinks) + checkErr(t, test.err, err) + want := append([]string(nil), test.dirs...) + for i := range want { + want[i] = filepath.Join(dir, want[i]) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("want: %v, got %v", want, got) + } + }) + } + }) +} + +func TestFs_Readlink(t *testing.T) { + testCases := []struct { + from, to string + err error + }{ + {".", "", syscall.EINVAL}, + {"/", "", syscall.EINVAL}, + + {"a", "", syscall.EINVAL}, + {"a/a", "", syscall.EINVAL}, + {"a/a/a", "", syscall.EINVAL}, + {"a/a/f", "../../f", nil}, + + {"b", "a", nil}, + {"b/a", "", syscall.EINVAL}, + {"b/a/a", "", syscall.EINVAL}, + {"b/a/f", "../../f", nil}, + + {"c", "a/a", nil}, + {"c/a", "", syscall.EINVAL}, + {"c/f", "../../f", nil}, + + {"d/a", "", syscall.EINVAL}, + {"d/f", "../../f", nil}, + + {"e", "a/a/a", nil}, + + {"f", "", syscall.EINVAL}, + + {"dangling", "missing", nil}, + + {"a/missing", "", os.ErrNotExist}, + {"b/missing", "", os.ErrNotExist}, + {"c/missing", "", os.ErrNotExist}, + {"d/missing", "", os.ErrNotExist}, + {"e/missing", "", os.ErrNotExist}, + {"dangling/missing", "", os.ErrNotExist}, + + {"a/missing/missing", "", os.ErrNotExist}, + {"b/missing/missing", "", os.ErrNotExist}, + {"c/missing/missing", "", os.ErrNotExist}, + {"d/missing/missing", "", os.ErrNotExist}, + {"e/missing/missing", "", os.ErrNotExist}, + {"dangling/missing/missing", "", os.ErrNotExist}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.from, func(t *testing.T) { + got, err := fs.Readlink(test.from) + checkErr(t, test.err, err) + if got != test.to { + t.Errorf("fs.Readlink(%q) want: %q, got %q", test.from, test.to, got) + } + }) + } + }) +} + +func TestFs_Lstat(t *testing.T) { + testCases := []struct { + name string + mode os.FileMode + size int64 + err error + }{ + {".", os.ModeDir, 0, nil}, + {"/", os.ModeDir, 0, nil}, + + {"a", os.ModeDir, 0, nil}, + {"a/a", os.ModeDir, 0, nil}, + {"a/a/a", 0, 0, nil}, + {"a/a/f", os.ModeSymlink, 7, nil}, + + {"b", os.ModeSymlink, 1, nil}, + {"b/a", os.ModeDir, 0, nil}, + {"b/a/a", 0, 0, nil}, + {"b/a/f", os.ModeSymlink, 7, nil}, + + {"c", os.ModeSymlink, 3, nil}, + {"c/a", 0, 0, nil}, + {"c/f", os.ModeSymlink, 7, nil}, + + {"d/a", 0, 0, nil}, + {"d/f", os.ModeSymlink, 7, nil}, + + {"e", os.ModeSymlink, 5, nil}, + + {"f", 0, 0, nil}, + + {"dangling", os.ModeSymlink, 7, nil}, + + {"a/missing", 0, 0, os.ErrNotExist}, + {"b/missing", 0, 0, os.ErrNotExist}, + {"c/missing", 0, 0, os.ErrNotExist}, + {"d/missing", 0, 0, os.ErrNotExist}, + {"e/missing", 0, 0, os.ErrNotExist}, + {"dangling/missing", 0, 0, os.ErrNotExist}, + + {"a/missing/missing", 0, 0, os.ErrNotExist}, + {"b/missing/missing", 0, 0, os.ErrNotExist}, + {"c/missing/missing", 0, 0, os.ErrNotExist}, + {"d/missing/missing", 0, 0, os.ErrNotExist}, + {"e/missing/missing", 0, 0, os.ErrNotExist}, + {"dangling/missing/missing", 0, 0, os.ErrNotExist}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := fs.Lstat(filepath.Join(dir, test.name)) + checkErr(t, test.err, err) + if err != nil { + return + } + if got.Mode()&os.ModeType != test.mode { + t.Errorf("fs.Lstat(%q).Mode()&os.ModeType want: %x, got %x", + test.name, test.mode, got.Mode()&os.ModeType) + } + if test.mode == 0 && got.Size() != test.size { + t.Errorf("fs.Lstat(%q).Size() want: %d, got %d", test.name, test.size, got.Size()) + } + }) + } + }) +} + +func TestFs_Stat(t *testing.T) { + testCases := []struct { + name string + mode os.FileMode + size int64 + err error + }{ + {".", os.ModeDir, 0, nil}, + {"/", os.ModeDir, 0, nil}, + + {"a", os.ModeDir, 0, nil}, + {"a/a", os.ModeDir, 0, nil}, + {"a/a/a", 0, 0, nil}, + {"a/a/f", 0, 0, nil}, + + {"b", os.ModeDir, 0, nil}, + {"b/a", os.ModeDir, 0, nil}, + {"b/a/a", 0, 0, nil}, + {"b/a/f", 0, 0, nil}, + + {"c", os.ModeDir, 0, nil}, + {"c/a", 0, 0, nil}, + {"c/f", 0, 0, nil}, + + {"d/a", 0, 0, nil}, + {"d/f", 0, 0, nil}, + + {"e", 0, 0, nil}, + + {"f", 0, 0, nil}, + + {"dangling", 0, 0, os.ErrNotExist}, + + {"a/missing", 0, 0, os.ErrNotExist}, + {"b/missing", 0, 0, os.ErrNotExist}, + {"c/missing", 0, 0, os.ErrNotExist}, + {"d/missing", 0, 0, os.ErrNotExist}, + {"e/missing", 0, 0, os.ErrNotExist}, + {"dangling/missing", 0, 0, os.ErrNotExist}, + + {"a/missing/missing", 0, 0, os.ErrNotExist}, + {"b/missing/missing", 0, 0, os.ErrNotExist}, + {"c/missing/missing", 0, 0, os.ErrNotExist}, + {"d/missing/missing", 0, 0, os.ErrNotExist}, + {"e/missing/missing", 0, 0, os.ErrNotExist}, + {"dangling/missing/missing", 0, 0, os.ErrNotExist}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := fs.Stat(filepath.Join(dir, test.name)) + checkErr(t, test.err, err) + if err != nil { + return + } + if got.Mode()&os.ModeType != test.mode { + t.Errorf("fs.Stat(%q).Mode()&os.ModeType want: %x, got %x", + test.name, test.mode, got.Mode()&os.ModeType) + } + if test.mode == 0 && got.Size() != test.size { + t.Errorf("fs.Stat(%q).Size() want: %d, got %d", test.name, test.size, got.Size()) + } + }) + } + }) +} + +func TestMockFs_glob(t *testing.T) { + testCases := []struct { + pattern string + files []string + }{ + {"*", []string{"a", "b", "c", "d", "dangling", "e", "f"}}, + {"./*", []string{"a", "b", "c", "d", "dangling", "e", "f"}}, + {"a", []string{"a"}}, + {"a/a", []string{"a/a"}}, + {"a/*", []string{"a/a"}}, + {"a/a/a", []string{"a/a/a"}}, + {"a/a/f", []string{"a/a/f"}}, + {"a/a/*", []string{"a/a/a", "a/a/f"}}, + + {"b", []string{"b"}}, + {"b/a", []string{"b/a"}}, + {"b/*", []string{"b/a"}}, + {"b/a/a", []string{"b/a/a"}}, + {"b/a/f", []string{"b/a/f"}}, + {"b/a/*", []string{"b/a/a", "b/a/f"}}, + + {"c", []string{"c"}}, + {"c/a", []string{"c/a"}}, + {"c/f", []string{"c/f"}}, + {"c/*", []string{"c/a", "c/f"}}, + + {"d", []string{"d"}}, + {"d/a", []string{"d/a"}}, + {"d/f", []string{"d/f"}}, + {"d/*", []string{"d/a", "d/f"}}, + + {"e", []string{"e"}}, + + {"dangling", []string{"dangling"}}, + + {"missing", nil}, + } + + runTestFs(t, func(t *testing.T, fs FileSystem, dir string) { + for _, test := range testCases { + t.Run(test.pattern, func(t *testing.T) { + got, err := fs.glob(test.pattern) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(got, test.files) { + t.Errorf("want: %v, got %v", test.files, got) + } + }) + } + }) +} + +func syscallError(err error) error { + if serr, ok := err.(*os.SyscallError); ok { + return serr.Err.(syscall.Errno) + } else if serr, ok := err.(syscall.Errno); ok { + return serr + } else { + return nil + } +} + +func checkErr(t *testing.T, want, got error) { + t.Helper() + if (got != nil) != (want != nil) { + t.Fatalf("want: %v, got %v", want, got) + } + + if os.IsNotExist(got) == os.IsNotExist(want) { + return + } + + if syscallError(got) == syscallError(want) { + return + } + + t.Fatalf("want: %v, got %v", want, got) +} diff --git a/blueprint/pathtools/glob.go b/blueprint/pathtools/glob.go new file mode 100644 index 0000000..5b2d685 --- /dev/null +++ b/blueprint/pathtools/glob.go @@ -0,0 +1,432 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pathtools + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +var GlobMultipleRecursiveErr = errors.New("pattern contains multiple '**'") +var GlobLastRecursiveErr = errors.New("pattern has '**' as last path element") +var GlobInvalidRecursiveErr = errors.New("pattern contains other characters between '**' and path separator") + +// GlobResult is a container holding the results of a call to Glob. +type GlobResult struct { + // Pattern is the pattern that was passed to Glob. + Pattern string + // Excludes is the list of excludes that were passed to Glob. + Excludes []string + + // Matches is the list of files or directories that matched the pattern but not the excludes. + Matches []string + + // Deps is the list of files or directories that must be depended on to regenerate the glob. + Deps []string +} + +// FileList returns the list of files matched by a glob for writing to an output file. +func (result GlobResult) FileList() []byte { + return []byte(strings.Join(result.Matches, "\n") + "\n") +} + +// MultipleGlobResults is a list of GlobResult structs. +type MultipleGlobResults []GlobResult + +// FileList returns the list of files matched by a list of multiple globs for writing to an output file. +func (results MultipleGlobResults) FileList() []byte { + multipleMatches := make([][]string, len(results)) + for i, result := range results { + multipleMatches[i] = result.Matches + } + buf, err := json.Marshal(multipleMatches) + if err != nil { + panic(fmt.Errorf("failed to marshal glob results to json: %w", err)) + } + return buf +} + +// Deps returns the deps from all of the GlobResults. +func (results MultipleGlobResults) Deps() []string { + var deps []string + for _, result := range results { + deps = append(deps, result.Deps...) + } + return deps +} + +// Glob returns the list of files and directories that match the given pattern +// but do not match the given exclude patterns, along with the list of +// directories and other dependencies that were searched to construct the file +// list. The supported glob and exclude patterns are equivalent to +// filepath.Glob, with an extension that recursive glob (** matching zero or +// more complete path entries) is supported. Any directories in the matches +// list will have a '/' suffix. +// +// In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps +// should be used instead, as they will automatically set up dependencies +// to rerun the primary builder when the list of matching files changes. +func Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) { + return startGlob(OsFs, pattern, excludes, follow) +} + +func startGlob(fs FileSystem, pattern string, excludes []string, + follow ShouldFollowSymlinks) (GlobResult, error) { + + if filepath.Base(pattern) == "**" { + return GlobResult{}, GlobLastRecursiveErr + } + + matches, deps, err := glob(fs, pattern, false, follow) + + if err != nil { + return GlobResult{}, err + } + + matches, err = filterExcludes(matches, excludes) + if err != nil { + return GlobResult{}, err + } + + // If the pattern has wildcards, we added dependencies on the + // containing directories to know about changes. + // + // If the pattern didn't have wildcards, and didn't find matches, the + // most specific found directories were added. + // + // But if it didn't have wildcards, and did find a match, no + // dependencies were added, so add the match itself to detect when it + // is removed. + if !isWild(pattern) { + deps = append(deps, matches...) + } + + for i, match := range matches { + var info os.FileInfo + if follow == DontFollowSymlinks { + info, err = fs.Lstat(match) + } else { + info, err = fs.Stat(match) + if err != nil && os.IsNotExist(err) { + // ErrNotExist from Stat may be due to a dangling symlink, retry with lstat. + info, err = fs.Lstat(match) + } + } + if err != nil { + return GlobResult{}, err + } + + if info.IsDir() { + matches[i] = match + "/" + } + } + + return GlobResult{ + Pattern: pattern, + Excludes: excludes, + Matches: matches, + Deps: deps, + }, nil +} + +// glob is a recursive helper function to handle globbing each level of the pattern individually, +// allowing searched directories to be tracked. Also handles the recursive glob pattern, **. +func glob(fs FileSystem, pattern string, hasRecursive bool, + follow ShouldFollowSymlinks) (matches, dirs []string, err error) { + + if !isWild(pattern) { + // If there are no wilds in the pattern, check whether the file exists or not. + // Uses filepath.Glob instead of manually statting to get consistent results. + pattern = filepath.Clean(pattern) + matches, err = fs.glob(pattern) + if err != nil { + return matches, dirs, err + } + + if len(matches) == 0 { + // Some part of the non-wild pattern didn't exist. Add the last existing directory + // as a dependency. + var matchDirs []string + for len(matchDirs) == 0 { + pattern = filepath.Dir(pattern) + matchDirs, err = fs.glob(pattern) + if err != nil { + return matches, dirs, err + } + } + dirs = append(dirs, matchDirs...) + } + return matches, dirs, err + } + + dir, file := quickSplit(pattern) + + if file == "**" { + if hasRecursive { + return matches, dirs, GlobMultipleRecursiveErr + } + hasRecursive = true + } else if strings.Contains(file, "**") { + return matches, dirs, GlobInvalidRecursiveErr + } + + dirMatches, dirs, err := glob(fs, dir, hasRecursive, follow) + if err != nil { + return nil, nil, err + } + + for _, m := range dirMatches { + isDir, err := fs.IsDir(m) + if os.IsNotExist(err) { + if isSymlink, _ := fs.IsSymlink(m); isSymlink { + return nil, nil, fmt.Errorf("dangling symlink: %s", m) + } + } + if err != nil { + return nil, nil, fmt.Errorf("unexpected error after glob: %s", err) + } + + if isDir { + if file == "**" { + recurseDirs, err := fs.ListDirsRecursive(m, follow) + if err != nil { + return nil, nil, err + } + matches = append(matches, recurseDirs...) + } else { + dirs = append(dirs, m) + newMatches, err := fs.glob(filepath.Join(MatchEscape(m), file)) + if err != nil { + return nil, nil, err + } + if file[0] != '.' { + newMatches = filterDotFiles(newMatches) + } + matches = append(matches, newMatches...) + } + } + } + + return matches, dirs, nil +} + +// Faster version of dir, file := filepath.Dir(path), filepath.File(path) with no allocations +// Similar to filepath.Split, but returns "." if dir is empty and trims trailing slash if dir is +// not "/". Returns ".", "" if path is "." +func quickSplit(path string) (dir, file string) { + if path == "." { + return ".", "" + } + dir, file = filepath.Split(path) + switch dir { + case "": + dir = "." + case "/": + // Nothing + default: + dir = dir[:len(dir)-1] + } + return dir, file +} + +func isWild(pattern string) bool { + return strings.ContainsAny(pattern, "*?[") +} + +// Filters the strings in matches based on the glob patterns in excludes. Hierarchical (a/*) and +// recursive (**) glob patterns are supported. +func filterExcludes(matches []string, excludes []string) ([]string, error) { + if len(excludes) == 0 { + return matches, nil + } + + var ret []string +matchLoop: + for _, m := range matches { + for _, e := range excludes { + exclude, err := Match(e, m) + if err != nil { + return nil, err + } + if exclude { + continue matchLoop + } + } + ret = append(ret, m) + } + + return ret, nil +} + +// filterDotFiles filters out files that start with '.' +func filterDotFiles(matches []string) []string { + ret := make([]string, 0, len(matches)) + + for _, match := range matches { + _, name := filepath.Split(match) + if name[0] == '.' { + continue + } + ret = append(ret, match) + } + + return ret +} + +// Match returns true if name matches pattern using the same rules as filepath.Match, but supporting +// recursive globs (**). +func Match(pattern, name string) (bool, error) { + if filepath.Base(pattern) == "**" { + return false, GlobLastRecursiveErr + } + + patternDir := pattern[len(pattern)-1] == '/' + nameDir := name[len(name)-1] == '/' + + if patternDir != nameDir { + return false, nil + } + + if nameDir { + name = name[:len(name)-1] + pattern = pattern[:len(pattern)-1] + } + + for { + var patternFile, nameFile string + pattern, patternFile = filepath.Dir(pattern), filepath.Base(pattern) + + if patternFile == "**" { + if strings.Contains(pattern, "**") { + return false, GlobMultipleRecursiveErr + } + // Test if the any prefix of name matches the part of the pattern before ** + for { + if name == "." || name == "/" { + return name == pattern, nil + } + if match, err := filepath.Match(pattern, name); err != nil { + return false, err + } else if match { + return true, nil + } + name = filepath.Dir(name) + } + } else if strings.Contains(patternFile, "**") { + return false, GlobInvalidRecursiveErr + } + + name, nameFile = filepath.Dir(name), filepath.Base(name) + + if nameFile == "." && patternFile == "." { + return true, nil + } else if nameFile == "/" && patternFile == "/" { + return true, nil + } else if nameFile == "." || patternFile == "." || nameFile == "/" || patternFile == "/" { + return false, nil + } + + match, err := filepath.Match(patternFile, nameFile) + if err != nil || !match { + return match, err + } + } +} + +// IsGlob returns true if the pattern contains any glob characters (*, ?, or [). +func IsGlob(pattern string) bool { + return strings.IndexAny(pattern, "*?[") >= 0 +} + +// HasGlob returns true if any string in the list contains any glob characters (*, ?, or [). +func HasGlob(in []string) bool { + for _, s := range in { + if IsGlob(s) { + return true + } + } + + return false +} + +// WriteFileIfChanged wraps ioutil.WriteFile, but only writes the file if +// the files does not already exist with identical contents. This can be used +// along with ninja restat rules to skip rebuilding downstream rules if no +// changes were made by a rule. +func WriteFileIfChanged(filename string, data []byte, perm os.FileMode) error { + var isChanged bool + + dir := filepath.Dir(filename) + err := os.MkdirAll(dir, 0777) + if err != nil { + return err + } + + info, err := os.Stat(filename) + if err != nil { + if os.IsNotExist(err) { + // The file does not exist yet. + isChanged = true + } else { + return err + } + } else { + if info.Size() != int64(len(data)) { + isChanged = true + } else { + oldData, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + if len(oldData) != len(data) { + isChanged = true + } else { + for i := range data { + if oldData[i] != data[i] { + isChanged = true + break + } + } + } + } + } + + if isChanged { + err = ioutil.WriteFile(filename, data, perm) + if err != nil { + return err + } + } + + return nil +} + +var matchEscaper = strings.NewReplacer( + `*`, `\*`, + `?`, `\?`, + `[`, `\[`, + `]`, `\]`, +) + +// MatchEscape returns its inputs with characters that would be interpreted by +func MatchEscape(s string) string { + return matchEscaper.Replace(s) +} diff --git a/blueprint/pathtools/glob_test.go b/blueprint/pathtools/glob_test.go new file mode 100644 index 0000000..37af483 --- /dev/null +++ b/blueprint/pathtools/glob_test.go @@ -0,0 +1,976 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pathtools + +import ( + "os" + "path/filepath" + "reflect" + "strings" + "testing" +) + +var pwd, _ = os.Getwd() + +type globTestCase struct { + pattern string + matches []string + excludes []string + deps []string + err error +} + +var globTestCases = []globTestCase{ + // Current directory tests + { + pattern: "*", + matches: []string{"a/", "b/", "c/", "d.ext", "e.ext"}, + deps: []string{"."}, + }, + { + pattern: "*.ext", + matches: []string{"d.ext", "e.ext"}, + deps: []string{"."}, + }, + { + pattern: "*/a", + matches: []string{"a/a/", "b/a"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "*/*/a", + matches: []string{"a/a/a"}, + deps: []string{".", "a", "b", "c", "a/a", "a/b", "c/f", "c/g", "c/h"}, + }, + { + pattern: "*/a/a", + matches: []string{"a/a/a"}, + deps: []string{".", "a", "b", "c", "a/a"}, + }, + { + pattern: "c/*/?", + matches: []string{"c/h/h"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "c/*/[gh]*", + matches: []string{"c/g/g.ext", "c/h/h"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "c/*/[fgh]*", + matches: []string{"c/f/f.ext", "c/g/g.ext", "c/h/h"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "c/*/[f-h]*", + matches: []string{"c/f/f.ext", "c/g/g.ext", "c/h/h"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + // ./ directory tests + { + pattern: "./*", + matches: []string{"a/", "b/", "c/", "d.ext", "e.ext"}, + deps: []string{"."}, + }, + { + pattern: "./*.ext", + matches: []string{"d.ext", "e.ext"}, + deps: []string{"."}, + }, + { + pattern: "./*/a", + matches: []string{"a/a/", "b/a"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "./[ac]/a", + matches: []string{"a/a/"}, + deps: []string{".", "a", "c"}, + }, + + // subdirectory tests + { + pattern: "c/*/*.ext", + matches: []string{"c/f/f.ext", "c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "a/*/a", + matches: []string{"a/a/a"}, + deps: []string{"a", "a/a", "a/b"}, + }, + + // absolute tests + { + pattern: filepath.Join(pwd, "testdata/glob/c/*/*.ext"), + matches: []string{ + filepath.Join(pwd, "testdata/glob/c/f/f.ext"), + filepath.Join(pwd, "testdata/glob/c/g/g.ext"), + }, + deps: []string{ + filepath.Join(pwd, "testdata/glob/c"), + filepath.Join(pwd, "testdata/glob/c/f"), + filepath.Join(pwd, "testdata/glob/c/g"), + filepath.Join(pwd, "testdata/glob/c/h"), + }, + }, + + // no-wild tests + { + pattern: "a", + matches: []string{"a/"}, + deps: []string{"a"}, + }, + { + pattern: "a/a", + matches: []string{"a/a/"}, + deps: []string{"a/a"}, + }, + + // clean tests + { + pattern: "./c/*/*.ext", + matches: []string{"c/f/f.ext", "c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "c/../c/*/*.ext", + matches: []string{"c/f/f.ext", "c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + + // recursive tests + { + pattern: "**/a", + matches: []string{"a/", "a/a/", "a/a/a", "b/a"}, + deps: []string{".", "a", "a/a", "a/b", "b", "c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "a/**/a", + matches: []string{"a/a/", "a/a/a"}, + deps: []string{"a", "a/a", "a/b"}, + }, + { + pattern: "a/**/*", + matches: []string{"a/a/", "a/b/", "a/a/a", "a/b/b"}, + deps: []string{"a", "a/a", "a/b"}, + }, + + // absolute recursive tests + { + pattern: filepath.Join(pwd, "testdata/glob/**/*.ext"), + matches: []string{ + filepath.Join(pwd, "testdata/glob/d.ext"), + filepath.Join(pwd, "testdata/glob/e.ext"), + filepath.Join(pwd, "testdata/glob/c/f/f.ext"), + filepath.Join(pwd, "testdata/glob/c/g/g.ext"), + }, + deps: []string{ + filepath.Join(pwd, "testdata/glob"), + filepath.Join(pwd, "testdata/glob/a"), + filepath.Join(pwd, "testdata/glob/a/a"), + filepath.Join(pwd, "testdata/glob/a/b"), + filepath.Join(pwd, "testdata/glob/b"), + filepath.Join(pwd, "testdata/glob/c"), + filepath.Join(pwd, "testdata/glob/c/f"), + filepath.Join(pwd, "testdata/glob/c/g"), + filepath.Join(pwd, "testdata/glob/c/h"), + }, + }, + + // recursive error tests + { + pattern: "**/**/*", + err: GlobMultipleRecursiveErr, + }, + { + pattern: "a/**/**/*", + err: GlobMultipleRecursiveErr, + }, + { + pattern: "**/a/**/*", + err: GlobMultipleRecursiveErr, + }, + { + pattern: "**/**/a/*", + err: GlobMultipleRecursiveErr, + }, + { + pattern: "a/**", + err: GlobLastRecursiveErr, + }, + { + pattern: "**/**", + err: GlobLastRecursiveErr, + }, + { + pattern: "a**/", + err: GlobInvalidRecursiveErr, + }, + { + pattern: "**a/", + err: GlobInvalidRecursiveErr, + }, + + // exclude tests + { + pattern: "*.ext", + excludes: []string{"d.ext"}, + matches: []string{"e.ext"}, + deps: []string{"."}, + }, + { + pattern: "*/*", + excludes: []string{"a/b"}, + matches: []string{"a/a/", "b/a", "c/c", "c/f/", "c/g/", "c/h/"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "*/*", + excludes: []string{"a/b", "c/c"}, + matches: []string{"a/a/", "b/a", "c/f/", "c/g/", "c/h/"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "*/*", + excludes: []string{"c/*", "*/a"}, + matches: []string{"a/b/"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "*/*", + excludes: []string{"*/*"}, + matches: nil, + deps: []string{".", "a", "b", "c"}, + }, + + // absolute exclude tests + { + pattern: filepath.Join(pwd, "testdata/glob/c/*/*.ext"), + excludes: []string{filepath.Join(pwd, "testdata/glob/c/*/f.ext")}, + matches: []string{ + filepath.Join(pwd, "testdata/glob/c/g/g.ext"), + }, + deps: []string{ + filepath.Join(pwd, "testdata/glob/c"), + filepath.Join(pwd, "testdata/glob/c/f"), + filepath.Join(pwd, "testdata/glob/c/g"), + filepath.Join(pwd, "testdata/glob/c/h"), + }, + }, + { + pattern: filepath.Join(pwd, "testdata/glob/c/*/*.ext"), + excludes: []string{filepath.Join(pwd, "testdata/glob/c/f/*.ext")}, + matches: []string{ + filepath.Join(pwd, "testdata/glob/c/g/g.ext"), + }, + deps: []string{ + filepath.Join(pwd, "testdata/glob/c"), + filepath.Join(pwd, "testdata/glob/c/f"), + filepath.Join(pwd, "testdata/glob/c/g"), + filepath.Join(pwd, "testdata/glob/c/h"), + }, + }, + + // recursive exclude tests + { + pattern: "*.ext", + excludes: []string{"**/*.ext"}, + matches: nil, + deps: []string{"."}, + }, + { + pattern: "*/*", + excludes: []string{"**/b"}, + matches: []string{"a/a/", "b/a", "c/c", "c/f/", "c/g/", "c/h/"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "*/*", + excludes: []string{"a/**/*"}, + matches: []string{"b/a", "c/c", "c/f/", "c/g/", "c/h/"}, + deps: []string{".", "a", "b", "c"}, + }, + { + pattern: "**/*", + excludes: []string{"**/*"}, + matches: nil, + deps: []string{".", "a", "a/a", "a/b", "b", "c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "*/*/*", + excludes: []string{"a/**/a"}, + matches: []string{"a/b/b", "c/f/f.ext", "c/g/g.ext", "c/h/h"}, + deps: []string{".", "a", "b", "c", "a/a", "a/b", "c/f", "c/g", "c/h"}, + }, + { + pattern: "*/*/*", + excludes: []string{"**/a"}, + matches: []string{"a/b/b", "c/f/f.ext", "c/g/g.ext", "c/h/h"}, + deps: []string{".", "a", "b", "c", "a/a", "a/b", "c/f", "c/g", "c/h"}, + }, + { + pattern: "c/*/*.ext", + excludes: []string{"c/**/f.ext"}, + matches: []string{"c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + + // absoulte recursive exclude tests + { + pattern: filepath.Join(pwd, "testdata/glob/c/*/*.ext"), + excludes: []string{filepath.Join(pwd, "testdata/glob/**/f.ext")}, + matches: []string{ + filepath.Join(pwd, "testdata/glob/c/g/g.ext"), + }, + deps: []string{ + filepath.Join(pwd, "testdata/glob/c"), + filepath.Join(pwd, "testdata/glob/c/f"), + filepath.Join(pwd, "testdata/glob/c/g"), + filepath.Join(pwd, "testdata/glob/c/h"), + }, + }, + + // clean exclude tests + { + pattern: "./c/*/*.ext", + excludes: []string{"./c/*/f.ext"}, + matches: []string{"c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "c/*/*.ext", + excludes: []string{"./c/*/f.ext"}, + matches: []string{"c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + { + pattern: "./c/*/*.ext", + excludes: []string{"c/*/f.ext"}, + matches: []string{"c/g/g.ext"}, + deps: []string{"c", "c/f", "c/g", "c/h"}, + }, + + // non-existant non-wild path tests + { + pattern: "d/*", + matches: nil, + deps: []string{"."}, + }, + { + pattern: "d", + matches: nil, + deps: []string{"."}, + }, + { + pattern: "a/d/*", + matches: nil, + deps: []string{"a"}, + }, + { + pattern: "a/d", + matches: nil, + deps: []string{"a"}, + }, + { + pattern: "a/a/d/*", + matches: nil, + deps: []string{"a/a"}, + }, + { + pattern: "a/a/d", + matches: nil, + deps: []string{"a/a"}, + }, + { + pattern: "a/d/a/*", + matches: nil, + deps: []string{"a"}, + }, + { + pattern: "a/d/a", + matches: nil, + deps: []string{"a"}, + }, + { + pattern: "a/d/a/*/a", + matches: nil, + deps: []string{"a"}, + }, + { + pattern: "a/d/a/**/a", + matches: nil, + deps: []string{"a"}, + }, + + // recursive exclude error tests + { + pattern: "**/*", + excludes: []string{"**/**/*"}, + err: GlobMultipleRecursiveErr, + }, + { + pattern: "**/*", + excludes: []string{"a/**/**/*"}, + err: GlobMultipleRecursiveErr, + }, + { + pattern: "**/*", + excludes: []string{"**/a/**/*"}, + err: GlobMultipleRecursiveErr, + }, + { + pattern: "**/*", + excludes: []string{"**/**/a/*"}, + err: GlobMultipleRecursiveErr, + }, + { + pattern: "**/*", + excludes: []string{"a/**"}, + err: GlobLastRecursiveErr, + }, + { + pattern: "**/*", + excludes: []string{"**/**"}, + err: GlobLastRecursiveErr, + }, + + // If names are excluded by default, but referenced explicitly, they should return results + { + pattern: ".test/*", + matches: []string{".test/a"}, + deps: []string{".test"}, + }, + { + pattern: ".t*/a", + matches: []string{".test/a"}, + deps: []string{".", ".test"}, + }, + { + pattern: ".*/.*", + matches: []string{".test/.ing"}, + deps: []string{".", ".test"}, + }, + { + pattern: ".t*", + matches: []string{".test/", ".testing"}, + deps: []string{"."}, + }, +} + +func TestMockGlob(t *testing.T) { + files := []string{ + "a/a/a", + "a/b/b", + "b/a", + "c/c", + "c/f/f.ext", + "c/g/g.ext", + "c/h/h", + "d.ext", + "e.ext", + ".test/a", + ".testing", + ".test/.ing", + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + mockFiles[filepath.Join(pwd, "testdata/glob", f)] = nil + } + + mock := MockFs(mockFiles) + + for _, testCase := range globTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, mock, testCase, FollowSymlinks) + }) + } +} + +func TestGlob(t *testing.T) { + os.Chdir("testdata/glob") + defer os.Chdir("../..") + for _, testCase := range globTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, OsFs, testCase, FollowSymlinks) + }) + } +} + +var globEscapeTestCases = []globTestCase{ + { + pattern: `**/*`, + matches: []string{`*`, `**/`, `?`, `a/`, `b`, `**/*`, `**/a`, `**/b/`, `**/b/b`, `a/a`}, + deps: []string{`.`, `**`, `**/b`, `a`}, + }, + { + pattern: `**/\*`, + matches: []string{`*`, `**/*`}, + deps: []string{`.`, `**`, `**/b`, `a`}, + }, + { + pattern: `\*\*/*`, + matches: []string{`**/*`, `**/a`, `**/b/`}, + deps: []string{`.`, `**`}, + }, + { + pattern: `\*\*/**/*`, + matches: []string{`**/*`, `**/a`, `**/b/`, `**/b/b`}, + deps: []string{`.`, `**`, `**/b`}, + }, +} + +func TestMockGlobEscapes(t *testing.T) { + files := []string{ + `*`, + `**/*`, + `**/a`, + `**/b/b`, + `?`, + `a/a`, + `b`, + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + } + + mock := MockFs(mockFiles) + + for _, testCase := range globEscapeTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, mock, testCase, FollowSymlinks) + }) + } + +} + +func TestGlobEscapes(t *testing.T) { + os.Chdir("testdata/escapes") + defer os.Chdir("../..") + for _, testCase := range globEscapeTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, OsFs, testCase, FollowSymlinks) + }) + } + +} + +var globSymlinkTestCases = []globTestCase{ + { + pattern: `**/*`, + matches: []string{"a/", "b/", "c/", "d/", "e", "a/a/", "a/a/a", "b/a/", "b/a/a", "c/a", "d/a"}, + deps: []string{".", "a", "a/a", "b", "b/a", "c", "d"}, + }, + { + pattern: `b/**/*`, + matches: []string{"b/a/", "b/a/a"}, + deps: []string{"b", "b/a"}, + }, +} + +func TestMockGlobSymlinks(t *testing.T) { + files := []string{ + "a/a/a", + "b -> a", + "c -> a/a", + "d -> c", + "e -> a/a/a", + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + } + + mock := MockFs(mockFiles) + + for _, testCase := range globSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, mock, testCase, FollowSymlinks) + }) + } +} + +func TestGlobSymlinks(t *testing.T) { + os.Chdir("testdata/symlinks") + defer os.Chdir("../..") + + for _, testCase := range globSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, OsFs, testCase, FollowSymlinks) + }) + } +} + +var globDontFollowSymlinkTestCases = []globTestCase{ + { + pattern: `**/*`, + matches: []string{"a/", "b", "c", "d", "e", "a/a/", "a/a/a"}, + deps: []string{".", "a", "a/a"}, + }, + { + pattern: `b/**/*`, + matches: []string{"b/a/", "b/a/a"}, + deps: []string{"b", "b/a"}, + }, +} + +func TestMockGlobDontFollowSymlinks(t *testing.T) { + files := []string{ + "a/a/a", + "b -> a", + "c -> a/a", + "d -> c", + "e -> a/a/a", + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + } + + mock := MockFs(mockFiles) + + for _, testCase := range globDontFollowSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, mock, testCase, DontFollowSymlinks) + }) + } +} + +func TestGlobDontFollowSymlinks(t *testing.T) { + os.Chdir("testdata/symlinks") + defer os.Chdir("../..") + + for _, testCase := range globDontFollowSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, OsFs, testCase, DontFollowSymlinks) + }) + } +} + +var globDontFollowDanglingSymlinkTestCases = []globTestCase{ + { + pattern: `**/*`, + matches: []string{"a/", "b", "c", "d", "dangling", "e", "f", "a/a/", "a/a/a", "a/a/f"}, + deps: []string{".", "a", "a/a"}, + }, + { + pattern: `dangling`, + matches: []string{"dangling"}, + deps: []string{"dangling"}, + }, +} + +func TestMockGlobDontFollowDanglingSymlinks(t *testing.T) { + files := []string{ + "a/a/a", + "a/a/f -> ../../f", + "b -> a", + "c -> a/a", + "d -> c", + "e -> a/a/a", + "f", + "dangling -> missing", + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + } + + mock := MockFs(mockFiles) + + for _, testCase := range globDontFollowDanglingSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, mock, testCase, DontFollowSymlinks) + }) + } +} + +func TestGlobDontFollowDanglingSymlinks(t *testing.T) { + os.Chdir("testdata/dangling") + defer os.Chdir("../..") + + for _, testCase := range globDontFollowDanglingSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, OsFs, testCase, DontFollowSymlinks) + }) + } +} + +var globFollowDanglingSymlinkTestCases = []globTestCase{ + { + pattern: `**/*`, + matches: []string{"a/", "b/", "c/", "d/", "dangling", "e", "f", "a/a/", "a/a/a", "a/a/f", "b/a/", "b/a/a", "b/a/f", "c/a", "c/f", "d/a", "d/f"}, + deps: []string{".", "a", "a/a", "b", "b/a", "c", "d"}, + }, + { + pattern: `dangling`, + matches: []string{"dangling"}, + deps: []string{"dangling"}, + }, +} + +func TestMockGlobFollowDanglingSymlinks(t *testing.T) { + files := []string{ + "a/a/a", + "a/a/f -> ../../f", + "b -> a", + "c -> a/a", + "d -> c", + "e -> a/a/a", + "f", + "dangling -> missing", + } + + mockFiles := make(map[string][]byte) + + for _, f := range files { + mockFiles[f] = nil + } + + mock := MockFs(mockFiles) + + for _, testCase := range globFollowDanglingSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, mock, testCase, FollowSymlinks) + }) + } +} + +func TestGlobFollowDanglingSymlinks(t *testing.T) { + os.Chdir("testdata/dangling") + defer os.Chdir("../..") + + for _, testCase := range globFollowDanglingSymlinkTestCases { + t.Run(testCase.pattern, func(t *testing.T) { + testGlob(t, OsFs, testCase, FollowSymlinks) + }) + } +} + +func testGlob(t *testing.T, fs FileSystem, testCase globTestCase, follow ShouldFollowSymlinks) { + t.Helper() + result, err := fs.Glob(testCase.pattern, testCase.excludes, follow) + if err != testCase.err { + if err == nil { + t.Fatalf("missing error: %s", testCase.err) + } else { + t.Fatalf("error: %s", err) + } + return + } + + if !reflect.DeepEqual(result.Matches, testCase.matches) { + t.Errorf("incorrect matches list:") + t.Errorf(" pattern: %q", testCase.pattern) + if testCase.excludes != nil { + t.Errorf("excludes: %q", testCase.excludes) + } + t.Errorf(" got: %#v", result.Matches) + t.Errorf("expected: %#v", testCase.matches) + } + if !reflect.DeepEqual(result.Deps, testCase.deps) { + t.Errorf("incorrect deps list:") + t.Errorf(" pattern: %q", testCase.pattern) + if testCase.excludes != nil { + t.Errorf("excludes: %q", testCase.excludes) + } + t.Errorf(" got: %#v", result.Deps) + t.Errorf("expected: %#v", testCase.deps) + } +} + +func TestMatch(t *testing.T) { + testCases := []struct { + pattern, name string + match bool + }{ + {"a/*", "b/", false}, + {"a/*", "b/a", false}, + {"a/*", "b/b/", false}, + {"a/*", "b/b/c", false}, + {"a/**/*", "b/", false}, + {"a/**/*", "b/a", false}, + {"a/**/*", "b/b/", false}, + {"a/**/*", "b/b/c", false}, + + {"a/*", "a/", false}, + {"a/*", "a/a", true}, + {"a/*", "a/b/", false}, + {"a/*", "a/b/c", false}, + + {"a/*/", "a/", false}, + {"a/*/", "a/a", false}, + {"a/*/", "a/b/", true}, + {"a/*/", "a/b/c", false}, + + {"a/**/*", "a/", false}, + {"a/**/*", "a/a", true}, + {"a/**/*", "a/b/", false}, + {"a/**/*", "a/b/c", true}, + + {"a/**/*/", "a/", false}, + {"a/**/*/", "a/a", false}, + {"a/**/*/", "a/b/", true}, + {"a/**/*/", "a/b/c", false}, + + {"**/*", "a/", false}, + {"**/*", "a/a", true}, + {"**/*", "a/b/", false}, + {"**/*", "a/b/c", true}, + + {"**/*/", "a/", true}, + {"**/*/", "a/a", false}, + {"**/*/", "a/b/", true}, + {"**/*/", "a/b/c", false}, + + {`a/\*\*/\*`, `a/**/*`, true}, + {`a/\*\*/\*`, `a/a/*`, false}, + {`a/\*\*/\*`, `a/**/a`, false}, + {`a/\*\*/\*`, `a/a/a`, false}, + + {`a/**/\*`, `a/**/*`, true}, + {`a/**/\*`, `a/a/*`, true}, + {`a/**/\*`, `a/**/a`, false}, + {`a/**/\*`, `a/a/a`, false}, + + {`a/\*\*/*`, `a/**/*`, true}, + {`a/\*\*/*`, `a/a/*`, false}, + {`a/\*\*/*`, `a/**/a`, true}, + {`a/\*\*/*`, `a/a/a`, false}, + + {`*/**/a`, `a/a/a`, true}, + {`*/**/a`, `*/a/a`, true}, + {`*/**/a`, `a/**/a`, true}, + {`*/**/a`, `*/**/a`, true}, + + {`\*/\*\*/a`, `a/a/a`, false}, + {`\*/\*\*/a`, `*/a/a`, false}, + {`\*/\*\*/a`, `a/**/a`, false}, + {`\*/\*\*/a`, `*/**/a`, true}, + + {`a/?`, `a/?`, true}, + {`a/?`, `a/a`, true}, + {`a/\?`, `a/?`, true}, + {`a/\?`, `a/a`, false}, + + {`a/?`, `a/?`, true}, + {`a/?`, `a/a`, true}, + {`a/\?`, `a/?`, true}, + {`a/\?`, `a/a`, false}, + + {`a/[a-c]`, `a/b`, true}, + {`a/[abc]`, `a/b`, true}, + + {`a/\[abc]`, `a/b`, false}, + {`a/\[abc]`, `a/[abc]`, true}, + + {`a/\[abc\]`, `a/b`, false}, + {`a/\[abc\]`, `a/[abc]`, true}, + + {`a/?`, `a/?`, true}, + {`a/?`, `a/a`, true}, + {`a/\?`, `a/?`, true}, + {`a/\?`, `a/a`, false}, + + {"/a/*", "/a/", false}, + {"/a/*", "/a/a", true}, + {"/a/*", "/a/b/", false}, + {"/a/*", "/a/b/c", false}, + + {"/a/*/", "/a/", false}, + {"/a/*/", "/a/a", false}, + {"/a/*/", "/a/b/", true}, + {"/a/*/", "/a/b/c", false}, + + {"/a/**/*", "/a/", false}, + {"/a/**/*", "/a/a", true}, + {"/a/**/*", "/a/b/", false}, + {"/a/**/*", "/a/b/c", true}, + + {"/**/*", "/a/", false}, + {"/**/*", "/a/a", true}, + {"/**/*", "/a/b/", false}, + {"/**/*", "/a/b/c", true}, + + {"/**/*/", "/a/", true}, + {"/**/*/", "/a/a", false}, + {"/**/*/", "/a/b/", true}, + {"/**/*/", "/a/b/c", false}, + + {`a`, `/a`, false}, + {`/a`, `a`, false}, + {`*`, `/a`, false}, + {`/*`, `a`, false}, + {`**/*`, `/a`, false}, + {`/**/*`, `a`, false}, + } + + for _, test := range testCases { + t.Run(test.pattern+","+test.name, func(t *testing.T) { + match, err := Match(test.pattern, test.name) + if err != nil { + t.Fatal(err) + } + if match != test.match { + t.Errorf("want: %v, got %v", test.match, match) + } + }) + } + + // Run the same test cases through Glob + for _, test := range testCases { + // Glob and Match disagree on matching directories + if strings.HasSuffix(test.name, "/") || strings.HasSuffix(test.pattern, "/") { + continue + } + t.Run("glob:"+test.pattern+","+test.name, func(t *testing.T) { + mockFiles := map[string][]byte{ + test.name: nil, + } + + mock := MockFs(mockFiles) + + result, err := mock.Glob(test.pattern, nil, DontFollowSymlinks) + t.Log(test.name, test.pattern, result.Matches) + if err != nil { + t.Fatal(err) + } + + match := false + for _, x := range result.Matches { + if x == test.name { + match = true + } + } + + if match != test.match { + t.Errorf("want: %v, got %v", test.match, match) + } + }) + } +} diff --git a/blueprint/pathtools/lists.go b/blueprint/pathtools/lists.go new file mode 100644 index 0000000..e1838b3 --- /dev/null +++ b/blueprint/pathtools/lists.go @@ -0,0 +1,49 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pathtools + +import ( + "path/filepath" + "strings" +) + +// PrefixPaths returns a list of paths consisting of prefix joined with each +// element of paths. The resulting paths are "clean" in the filepath.Clean +// sense. +func PrefixPaths(paths []string, prefix string) []string { + result := make([]string, len(paths)) + for i, path := range paths { + result[i] = filepath.Join(prefix, path) + } + return result +} + +func ReplaceExtensions(paths []string, extension string) []string { + result := make([]string, len(paths)) + for i, path := range paths { + result[i] = ReplaceExtension(path, extension) + } + return result +} + +// ReplaceExtension changes the file extension. If the file does not have an +// extension, the new extension is appended. +func ReplaceExtension(path string, extension string) string { + oldExt := filepath.Ext(path) + if oldExt != "" { + path = strings.TrimSuffix(path, oldExt) + } + return path + "." + extension +} diff --git a/blueprint/pathtools/lists_test.go b/blueprint/pathtools/lists_test.go new file mode 100644 index 0000000..cce8786 --- /dev/null +++ b/blueprint/pathtools/lists_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package pathtools + +import ( + "testing" +) + +func TestLists_ReplaceExtension(t *testing.T) { + + testCases := []struct { + from, ext, to string + }{ + {"1.jpg", "png", "1.png"}, + {"1", "png", "1.png"}, + {"1.", "png", "1.png"}, + {"2.so", "so.1", "2.so.1"}, + {"/out/.test/1.png", "jpg", "/out/.test/1.jpg"}, + {"/out/.test/1", "jpg", "/out/.test/1.jpg"}, + } + + for _, test := range testCases { + t.Run(test.from, func(t *testing.T) { + got := ReplaceExtension(test.from, test.ext) + if got != test.to { + t.Errorf("ReplaceExtension(%v, %v) = %v; want: %v", test.from, test.ext, got, test.to) + } + }) + } +} diff --git a/blueprint/pathtools/testdata/dangling/a/a/a b/blueprint/pathtools/testdata/dangling/a/a/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/dangling/a/a/f b/blueprint/pathtools/testdata/dangling/a/a/f new file mode 120000 index 0000000..8a1f8dd --- /dev/null +++ b/blueprint/pathtools/testdata/dangling/a/a/f @@ -0,0 +1 @@ +../../f \ No newline at end of file diff --git a/blueprint/pathtools/testdata/dangling/b b/blueprint/pathtools/testdata/dangling/b new file mode 120000 index 0000000..2e65efe --- /dev/null +++ b/blueprint/pathtools/testdata/dangling/b @@ -0,0 +1 @@ +a \ No newline at end of file diff --git a/blueprint/pathtools/testdata/dangling/c b/blueprint/pathtools/testdata/dangling/c new file mode 120000 index 0000000..de35cbe --- /dev/null +++ b/blueprint/pathtools/testdata/dangling/c @@ -0,0 +1 @@ +a/a \ No newline at end of file diff --git a/blueprint/pathtools/testdata/dangling/d b/blueprint/pathtools/testdata/dangling/d new file mode 120000 index 0000000..3410062 --- /dev/null +++ b/blueprint/pathtools/testdata/dangling/d @@ -0,0 +1 @@ +c \ No newline at end of file diff --git a/blueprint/pathtools/testdata/dangling/dangling b/blueprint/pathtools/testdata/dangling/dangling new file mode 120000 index 0000000..6eab79a --- /dev/null +++ b/blueprint/pathtools/testdata/dangling/dangling @@ -0,0 +1 @@ +missing \ No newline at end of file diff --git a/blueprint/pathtools/testdata/dangling/e b/blueprint/pathtools/testdata/dangling/e new file mode 120000 index 0000000..6bf79dd --- /dev/null +++ b/blueprint/pathtools/testdata/dangling/e @@ -0,0 +1 @@ +a/a/a \ No newline at end of file diff --git a/blueprint/pathtools/testdata/dangling/f b/blueprint/pathtools/testdata/dangling/f new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/* b/blueprint/pathtools/testdata/escapes/* new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/**/* b/blueprint/pathtools/testdata/escapes/**/* new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/**/a b/blueprint/pathtools/testdata/escapes/**/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/**/b/b b/blueprint/pathtools/testdata/escapes/**/b/b new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/? b/blueprint/pathtools/testdata/escapes/? new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/a/a b/blueprint/pathtools/testdata/escapes/a/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/escapes/b b/blueprint/pathtools/testdata/escapes/b new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/.test/.ing b/blueprint/pathtools/testdata/glob/.test/.ing new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/.test/a b/blueprint/pathtools/testdata/glob/.test/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/.testing b/blueprint/pathtools/testdata/glob/.testing new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/a/a/a b/blueprint/pathtools/testdata/glob/a/a/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/a/b/b b/blueprint/pathtools/testdata/glob/a/b/b new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/b/a b/blueprint/pathtools/testdata/glob/b/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/c/c b/blueprint/pathtools/testdata/glob/c/c new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/c/f/f.ext b/blueprint/pathtools/testdata/glob/c/f/f.ext new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/c/g/g.ext b/blueprint/pathtools/testdata/glob/c/g/g.ext new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/c/h/h b/blueprint/pathtools/testdata/glob/c/h/h new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/d.ext b/blueprint/pathtools/testdata/glob/d.ext new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/glob/e.ext b/blueprint/pathtools/testdata/glob/e.ext new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/symlinks/a/a/a b/blueprint/pathtools/testdata/symlinks/a/a/a new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/pathtools/testdata/symlinks/b b/blueprint/pathtools/testdata/symlinks/b new file mode 120000 index 0000000..2e65efe --- /dev/null +++ b/blueprint/pathtools/testdata/symlinks/b @@ -0,0 +1 @@ +a \ No newline at end of file diff --git a/blueprint/pathtools/testdata/symlinks/c b/blueprint/pathtools/testdata/symlinks/c new file mode 120000 index 0000000..de35cbe --- /dev/null +++ b/blueprint/pathtools/testdata/symlinks/c @@ -0,0 +1 @@ +a/a \ No newline at end of file diff --git a/blueprint/pathtools/testdata/symlinks/d b/blueprint/pathtools/testdata/symlinks/d new file mode 120000 index 0000000..3410062 --- /dev/null +++ b/blueprint/pathtools/testdata/symlinks/d @@ -0,0 +1 @@ +c \ No newline at end of file diff --git a/blueprint/pathtools/testdata/symlinks/e b/blueprint/pathtools/testdata/symlinks/e new file mode 120000 index 0000000..6bf79dd --- /dev/null +++ b/blueprint/pathtools/testdata/symlinks/e @@ -0,0 +1 @@ +a/a/a \ No newline at end of file diff --git a/blueprint/proptools/clone.go b/blueprint/proptools/clone.go new file mode 100644 index 0000000..f464fa6 --- /dev/null +++ b/blueprint/proptools/clone.go @@ -0,0 +1,304 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" + "sync" +) + +// CloneProperties takes a reflect.Value of a pointer to a struct and returns a reflect.Value +// of a pointer to a new struct that copies of the values for its fields. It recursively clones +// struct pointers and interfaces that contain struct pointers. +func CloneProperties(structValue reflect.Value) reflect.Value { + if !isStructPtr(structValue.Type()) { + panic(fmt.Errorf("CloneProperties expected *struct, got %s", structValue.Type())) + } + result := reflect.New(structValue.Type().Elem()) + copyProperties(result.Elem(), structValue.Elem()) + return result +} + +// CopyProperties takes destination and source reflect.Values of a pointer to structs and returns +// copies each field from the source into the destination. It recursively copies struct pointers +// and interfaces that contain struct pointers. +func CopyProperties(dstValue, srcValue reflect.Value) { + if !isStructPtr(dstValue.Type()) { + panic(fmt.Errorf("CopyProperties expected dstValue *struct, got %s", dstValue.Type())) + } + if !isStructPtr(srcValue.Type()) { + panic(fmt.Errorf("CopyProperties expected srcValue *struct, got %s", srcValue.Type())) + } + copyProperties(dstValue.Elem(), srcValue.Elem()) +} + +func copyProperties(dstValue, srcValue reflect.Value) { + typ := dstValue.Type() + if srcValue.Type() != typ { + panic(fmt.Errorf("can't copy mismatching types (%s <- %s)", + dstValue.Kind(), srcValue.Kind())) + } + + for i, field := range typeFields(typ) { + if field.PkgPath != "" { + panic(fmt.Errorf("can't copy a private field %q", field.Name)) + } + + srcFieldValue := srcValue.Field(i) + dstFieldValue := dstValue.Field(i) + dstFieldInterfaceValue := reflect.Value{} + origDstFieldValue := dstFieldValue + + switch srcFieldValue.Kind() { + case reflect.Bool, reflect.String, reflect.Int, reflect.Uint: + dstFieldValue.Set(srcFieldValue) + case reflect.Struct: + copyProperties(dstFieldValue, srcFieldValue) + case reflect.Slice: + if !srcFieldValue.IsNil() { + if srcFieldValue != dstFieldValue { + newSlice := reflect.MakeSlice(field.Type, srcFieldValue.Len(), + srcFieldValue.Len()) + reflect.Copy(newSlice, srcFieldValue) + dstFieldValue.Set(newSlice) + } + } else { + dstFieldValue.Set(srcFieldValue) + } + case reflect.Map: + if !srcFieldValue.IsNil() { + newMap := reflect.MakeMap(field.Type) + + iter := srcFieldValue.MapRange() + for iter.Next() { + newMap.SetMapIndex(iter.Key(), iter.Value()) + } + dstFieldValue.Set(newMap) + } else { + dstFieldValue.Set(srcFieldValue) + } + case reflect.Interface: + if srcFieldValue.IsNil() { + dstFieldValue.Set(srcFieldValue) + break + } + + srcFieldValue = srcFieldValue.Elem() + + if !isStructPtr(srcFieldValue.Type()) { + panic(fmt.Errorf("can't clone field %q: expected interface to contain *struct, found %s", + field.Name, srcFieldValue.Type())) + } + + if dstFieldValue.IsNil() || dstFieldValue.Elem().Type() != srcFieldValue.Type() { + // We can't use the existing destination allocation, so + // clone a new one. + newValue := reflect.New(srcFieldValue.Type()).Elem() + dstFieldValue.Set(newValue) + dstFieldInterfaceValue = dstFieldValue + dstFieldValue = newValue + } else { + dstFieldValue = dstFieldValue.Elem() + } + fallthrough + case reflect.Ptr: + if srcFieldValue.IsNil() { + origDstFieldValue.Set(srcFieldValue) + break + } + + switch srcFieldValue.Elem().Kind() { + case reflect.Struct: + if !dstFieldValue.IsNil() { + // Re-use the existing allocation. + copyProperties(dstFieldValue.Elem(), srcFieldValue.Elem()) + break + } else { + newValue := CloneProperties(srcFieldValue) + if dstFieldInterfaceValue.IsValid() { + dstFieldInterfaceValue.Set(newValue) + } else { + origDstFieldValue.Set(newValue) + } + } + case reflect.Bool, reflect.Int64, reflect.String: + newValue := reflect.New(srcFieldValue.Elem().Type()) + newValue.Elem().Set(srcFieldValue.Elem()) + origDstFieldValue.Set(newValue) + default: + panic(fmt.Errorf("can't clone pointer field %q type %s", + field.Name, srcFieldValue.Type())) + } + default: + panic(fmt.Errorf("unexpected type for property struct field %q: %s", + field.Name, srcFieldValue.Type())) + } + } +} + +// ZeroProperties takes a reflect.Value of a pointer to a struct and replaces all of its fields +// with zero values, recursing into struct, pointer to struct and interface fields. +func ZeroProperties(structValue reflect.Value) { + if !isStructPtr(structValue.Type()) { + panic(fmt.Errorf("ZeroProperties expected *struct, got %s", structValue.Type())) + } + zeroProperties(structValue.Elem()) +} + +func zeroProperties(structValue reflect.Value) { + typ := structValue.Type() + + for i, field := range typeFields(typ) { + if field.PkgPath != "" { + // The field is not exported so just skip it. + continue + } + + fieldValue := structValue.Field(i) + + switch fieldValue.Kind() { + case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint, reflect.Map: + fieldValue.Set(reflect.Zero(fieldValue.Type())) + case reflect.Interface: + if fieldValue.IsNil() { + break + } + + // We leave the pointer intact and zero out the struct that's + // pointed to. + fieldValue = fieldValue.Elem() + if !isStructPtr(fieldValue.Type()) { + panic(fmt.Errorf("can't zero field %q: expected interface to contain *struct, found %s", + field.Name, fieldValue.Type())) + } + fallthrough + case reflect.Ptr: + switch fieldValue.Type().Elem().Kind() { + case reflect.Struct: + if fieldValue.IsNil() { + break + } + zeroProperties(fieldValue.Elem()) + case reflect.Bool, reflect.Int64, reflect.String: + fieldValue.Set(reflect.Zero(fieldValue.Type())) + default: + panic(fmt.Errorf("can't zero field %q: points to a %s", + field.Name, fieldValue.Elem().Kind())) + } + case reflect.Struct: + zeroProperties(fieldValue) + default: + panic(fmt.Errorf("unexpected kind for property struct field %q: %s", + field.Name, fieldValue.Kind())) + } + } +} + +// CloneEmptyProperties takes a reflect.Value of a pointer to a struct and returns a reflect.Value +// of a pointer to a new struct that has the zero values for its fields. It recursively clones +// struct pointers and interfaces that contain struct pointers. +func CloneEmptyProperties(structValue reflect.Value) reflect.Value { + if !isStructPtr(structValue.Type()) { + panic(fmt.Errorf("CloneEmptyProperties expected *struct, got %s", structValue.Type())) + } + result := reflect.New(structValue.Type().Elem()) + cloneEmptyProperties(result.Elem(), structValue.Elem()) + return result +} + +func cloneEmptyProperties(dstValue, srcValue reflect.Value) { + typ := srcValue.Type() + for i, field := range typeFields(typ) { + if field.PkgPath != "" { + // The field is not exported so just skip it. + continue + } + + srcFieldValue := srcValue.Field(i) + dstFieldValue := dstValue.Field(i) + dstFieldInterfaceValue := reflect.Value{} + + switch srcFieldValue.Kind() { + case reflect.Bool, reflect.String, reflect.Slice, reflect.Map, reflect.Int, reflect.Uint: + // Nothing + case reflect.Struct: + cloneEmptyProperties(dstFieldValue, srcFieldValue) + case reflect.Interface: + if srcFieldValue.IsNil() { + break + } + + srcFieldValue = srcFieldValue.Elem() + if !isStructPtr(srcFieldValue.Type()) { + panic(fmt.Errorf("can't clone empty field %q: expected interface to contain *struct, found %s", + field.Name, srcFieldValue.Type())) + } + + newValue := reflect.New(srcFieldValue.Type()).Elem() + dstFieldValue.Set(newValue) + dstFieldInterfaceValue = dstFieldValue + dstFieldValue = newValue + fallthrough + case reflect.Ptr: + switch srcFieldValue.Type().Elem().Kind() { + case reflect.Struct: + if srcFieldValue.IsNil() { + break + } + newValue := CloneEmptyProperties(srcFieldValue) + if dstFieldInterfaceValue.IsValid() { + dstFieldInterfaceValue.Set(newValue) + } else { + dstFieldValue.Set(newValue) + } + case reflect.Bool, reflect.Int64, reflect.String: + // Nothing + default: + panic(fmt.Errorf("can't clone empty field %q: points to a %s", + field.Name, srcFieldValue.Elem().Kind())) + } + + default: + panic(fmt.Errorf("unexpected kind for property struct field %q: %s", + field.Name, srcFieldValue.Kind())) + } + } +} + +var typeFieldCache sync.Map + +func typeFields(typ reflect.Type) []reflect.StructField { + // reflect.Type.Field allocates a []int{} to hold the index every time it is called, which ends up + // being a significant portion of the GC pressure. It can't reuse the same one in case a caller + // modifies the backing array through the slice. Since we don't modify it, cache the result + // locally to reduce allocations. + + // Fast path + if typeFields, ok := typeFieldCache.Load(typ); ok { + return typeFields.([]reflect.StructField) + } + + // Slow path + typeFields := make([]reflect.StructField, typ.NumField()) + + for i := range typeFields { + typeFields[i] = typ.Field(i) + } + + typeFieldCache.Store(typ, typeFields) + + return typeFields +} diff --git a/blueprint/proptools/clone_test.go b/blueprint/proptools/clone_test.go new file mode 100644 index 0000000..137882a --- /dev/null +++ b/blueprint/proptools/clone_test.go @@ -0,0 +1,556 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" + "testing" +) + +var clonePropertiesTestCases = []struct { + in interface{} + out interface{} + err error +}{ + // Valid inputs + + { + // Clone bool + in: &struct{ B1, B2 bool }{ + B1: true, + B2: false, + }, + out: &struct{ B1, B2 bool }{ + B1: true, + B2: false, + }, + }, + { + // Clone strings + in: &struct{ S string }{ + S: "string1", + }, + out: &struct{ S string }{ + S: "string1", + }, + }, + { + // Clone slice + in: &struct{ S []string }{ + S: []string{"string1"}, + }, + out: &struct{ S []string }{ + S: []string{"string1"}, + }, + }, + { + // Clone empty slice + in: &struct{ S []string }{ + S: []string{}, + }, + out: &struct{ S []string }{ + S: []string{}, + }, + }, + { + // Clone nil slice + in: &struct{ S []string }{}, + out: &struct{ S []string }{}, + }, + { + // Clone slice of structs + in: &struct{ S []struct{ T string } }{ + S: []struct{ T string }{ + {"string1"}, {"string2"}, + }, + }, + out: &struct{ S []struct{ T string } }{ + S: []struct{ T string }{ + {"string1"}, {"string2"}, + }, + }, + }, + { + // Clone map + in: &struct{ S map[string]string }{ + S: map[string]string{"key": "string1"}, + }, + out: &struct{ S map[string]string }{ + S: map[string]string{"key": "string1"}, + }, + }, + { + // Clone empty map + in: &struct{ S map[string]string }{ + S: map[string]string{}, + }, + out: &struct{ S map[string]string }{ + S: map[string]string{}, + }, + }, + { + // Clone nil map + in: &struct{ S map[string]string }{}, + out: &struct{ S map[string]string }{}, + }, + { + // Clone pointer to bool + in: &struct{ B1, B2 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + }, + out: &struct{ B1, B2 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + }, + }, + { + // Clone pointer to string + in: &struct{ S *string }{ + S: StringPtr("string1"), + }, + out: &struct{ S *string }{ + S: StringPtr("string1"), + }, + }, + { + // Clone pointer to int64 + in: &struct{ S *int64 }{ + S: Int64Ptr(5), + }, + out: &struct{ S *int64 }{ + S: Int64Ptr(5), + }, + }, + { + // Clone struct + in: &struct{ S struct{ S string } }{ + S: struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S struct{ S string } }{ + S: struct{ S string }{ + S: "string1", + }, + }, + }, + { + // Clone struct pointer + in: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + { + // Clone interface + in: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + { + // Clone nested interface + in: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + out: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + }, { + // Empty struct + in: &struct{}{}, + out: &struct{}{}, + }, + { + // Interface nil + in: &struct{ S interface{} }{ + S: nil, + }, + out: &struct{ S interface{} }{ + S: nil, + }, + }, + { + // Interface pointer to nil + in: &struct{ S interface{} }{ + S: (*struct{ S string })(nil), + }, + out: &struct{ S interface{} }{ + S: (*struct{ S string })(nil), + }, + }, + { + // Pointer nil + in: &struct{ S *struct{} }{ + S: nil, + }, + out: &struct{ S *struct{} }{ + S: nil, + }, + }, + { + // Anonymous struct + in: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1", + I: Int64Ptr(55), + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string2", + I: Int64Ptr(5), + }, + }, + }, + out: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1", + I: Int64Ptr(55), + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string2", + I: Int64Ptr(5), + }, + }, + }, + }, + { + // Anonymous interface + in: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct{ S string }{ + S: "string1", + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct{ S string }{ + S: "string2", + }, + }, + }, + out: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct{ S string }{ + S: "string1", + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct{ S string }{ + S: "string2", + }, + }, + }, + }, +} + +type EmbeddedStruct struct { + S string + I *int64 +} +type EmbeddedInterface interface{} + +func TestCloneProperties(t *testing.T) { + for _, testCase := range clonePropertiesTestCases { + testString := fmt.Sprintf("%s", testCase.in) + + got := CloneProperties(reflect.ValueOf(testCase.in)).Interface() + + if !reflect.DeepEqual(testCase.out, got) { + t.Errorf("test case %s", testString) + t.Errorf("incorrect output") + t.Errorf(" expected: %#v", testCase.out) + t.Errorf(" got: %#v", got) + } + if testCase.out == got { + t.Errorf("test case %s", testString) + t.Errorf("items should be cloned, not the original") + t.Errorf(" expected: %s", testCase.out) + t.Errorf(" got: %s", got) + } + } +} + +var cloneEmptyPropertiesTestCases = []struct { + in interface{} + out interface{} + err error +}{ + // Valid inputs + + { + // Clone bool + in: &struct{ B1, B2 bool }{ + B1: true, + B2: false, + }, + out: &struct{ B1, B2 bool }{}, + }, + { + // Clone strings + in: &struct{ S string }{ + S: "string1", + }, + out: &struct{ S string }{}, + }, + { + // Clone slice + in: &struct{ S []string }{ + S: []string{"string1"}, + }, + out: &struct{ S []string }{}, + }, + { + // Clone empty slice + in: &struct{ S []string }{ + S: []string{}, + }, + out: &struct{ S []string }{}, + }, + { + // Clone nil slice + in: &struct{ S []string }{}, + out: &struct{ S []string }{}, + }, + { + // Clone slice of structs + in: &struct{ S []struct{ T string } }{ + S: []struct{ T string }{ + {"string1"}, {"string2"}, + }, + }, + out: &struct{ S []struct{ T string } }{ + S: []struct{ T string }(nil), + }, + }, + { + // Clone pointer to bool + in: &struct{ B1, B2 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + }, + out: &struct{ B1, B2 *bool }{}, + }, + { + // Clone pointer to int64 + in: &struct{ B1, B2 *int64 }{ + B1: Int64Ptr(5), + B2: Int64Ptr(4), + }, + out: &struct{ B1, B2 *int64 }{}, + }, + { + // Clone pointer to string + in: &struct{ S *string }{ + S: StringPtr("string1"), + }, + out: &struct{ S *string }{}, + }, + { + // Clone struct + in: &struct{ S struct{ S string } }{ + S: struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S struct{ S string } }{ + S: struct{ S string }{}, + }, + }, + { + // Clone struct pointer + in: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{}, + }, + }, + { + // Clone interface + in: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S interface{} }{ + S: &struct{ S string }{}, + }, + }, + { + // Clone nested interface + in: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + out: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{}, + }, + }, + }, + { + // Empty struct + in: &struct{}{}, + out: &struct{}{}, + }, + { + // Interface nil + in: &struct{ S interface{} }{ + S: nil, + }, + out: &struct{ S interface{} }{}, + }, + { + // Interface pointer to nil + in: &struct{ S interface{} }{ + S: (*struct{ S string })(nil), + }, + out: &struct{ S interface{} }{ + S: (*struct{ S string })(nil), + }, + }, + { + // Pointer nil + in: &struct{ S *struct{} }{ + S: nil, + }, + out: &struct{ S *struct{} }{}, + }, + { + // Anonymous struct + in: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1", + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string2", + }, + }, + }, + out: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{}, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{}, + }, + }, + }, + { + // Anonymous interface + in: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct{ S string }{ + S: "string1", + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct{ S string }{ + S: "string2", + }, + }, + }, + out: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct{ S string }{}, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct{ S string }{}, + }, + }, + }, +} + +func TestCloneEmptyProperties(t *testing.T) { + for _, testCase := range cloneEmptyPropertiesTestCases { + testString := fmt.Sprintf("%#v", testCase.in) + + got := CloneEmptyProperties(reflect.ValueOf(testCase.in)).Interface() + + if !reflect.DeepEqual(testCase.out, got) { + t.Errorf("test case %s", testString) + t.Errorf("incorrect output") + t.Errorf(" expected: %#v", testCase.out) + t.Errorf(" got: %#v", got) + } + } +} + +func TestZeroProperties(t *testing.T) { + for _, testCase := range cloneEmptyPropertiesTestCases { + testString := fmt.Sprintf("%#v", testCase.in) + + got := CloneProperties(reflect.ValueOf(testCase.in)).Interface() + ZeroProperties(reflect.ValueOf(got)) + + if !reflect.DeepEqual(testCase.out, got) { + t.Errorf("test case %s", testString) + t.Errorf("incorrect output") + t.Errorf(" expected: %#v", testCase.out) + t.Errorf(" got: %#v", got) + } + } +} diff --git a/blueprint/proptools/escape.go b/blueprint/proptools/escape.go new file mode 100644 index 0000000..4ef10f0 --- /dev/null +++ b/blueprint/proptools/escape.go @@ -0,0 +1,125 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import "strings" + +// NinjaEscapeList takes a slice of strings that may contain characters that are meaningful to ninja +// ($), and escapes each string so they will be passed to bash. It is not necessary on input, +// output, or dependency names, those are handled by ModuleContext.Build. It is generally required +// on strings from properties in Blueprint files that are used as Args to ModuleContext.Build. A +// new slice containing the escaped strings is returned. +func NinjaEscapeList(slice []string) []string { + slice = append([]string(nil), slice...) + for i, s := range slice { + slice[i] = NinjaEscape(s) + } + return slice +} + +// NinjaEscapeList takes a string that may contain characters that are meaningful to ninja +// ($), and escapes it so it will be passed to bash. It is not necessary on input, +// output, or dependency names, those are handled by ModuleContext.Build. It is generally required +// on strings from properties in Blueprint files that are used as Args to ModuleContext.Build. A +// new slice containing the escaped strings is returned. +func NinjaEscape(s string) string { + return ninjaEscaper.Replace(s) +} + +var ninjaEscaper = strings.NewReplacer( + "$", "$$") + +// ShellEscapeList takes a slice of strings that may contain characters that are meaningful to bash and +// escapes them if necessary by wrapping them in single quotes, and replacing internal single quotes with +// '\'' (one single quote to end the quoting, a shell-escaped single quote to insert a real single +// quote, and then a single quote to restarting quoting. A new slice containing the escaped strings +// is returned. +func ShellEscapeList(slice []string) []string { + slice = append([]string(nil), slice...) + + for i, s := range slice { + slice[i] = ShellEscape(s) + } + return slice +} + +func ShellEscapeListIncludingSpaces(slice []string) []string { + slice = append([]string(nil), slice...) + + for i, s := range slice { + slice[i] = ShellEscapeIncludingSpaces(s) + } + return slice +} + +func shellUnsafeChar(r rune) bool { + switch { + case 'A' <= r && r <= 'Z', + 'a' <= r && r <= 'z', + '0' <= r && r <= '9', + r == '_', + r == '+', + r == '-', + r == '=', + r == '.', + r == ',', + r == '/': + return false + default: + return true + } +} + +// ShellEscape takes string that may contain characters that are meaningful to bash and +// escapes it if necessary by wrapping it in single quotes, and replacing internal single quotes with +// '\'' (one single quote to end the quoting, a shell-escaped single quote to insert a real single +// quote, and then a single quote to restarting quoting. +func ShellEscape(s string) string { + shellUnsafeCharNotSpace := func(r rune) bool { + return r != ' ' && shellUnsafeChar(r) + } + + if strings.IndexFunc(s, shellUnsafeCharNotSpace) == -1 { + // No escaping necessary + return s + } + + return `'` + singleQuoteReplacer.Replace(s) + `'` +} + +// ShellEscapeIncludingSpaces escapes the input `s` in a similar way to ShellEscape except that +// this treats spaces as meaningful characters. +func ShellEscapeIncludingSpaces(s string) string { + if strings.IndexFunc(s, shellUnsafeChar) == -1 { + // No escaping necessary + return s + } + + return `'` + singleQuoteReplacer.Replace(s) + `'` +} + +func NinjaAndShellEscapeList(slice []string) []string { + return ShellEscapeList(NinjaEscapeList(slice)) +} + +func NinjaAndShellEscapeListIncludingSpaces(slice []string) []string { + return ShellEscapeListIncludingSpaces(NinjaEscapeList(slice)) +} + +func NinjaAndShellEscape(s string) string { + return ShellEscape(NinjaEscape(s)) +} + +var singleQuoteReplacer = strings.NewReplacer(`'`, `'\''`) diff --git a/blueprint/proptools/escape_test.go b/blueprint/proptools/escape_test.go new file mode 100644 index 0000000..5823a05 --- /dev/null +++ b/blueprint/proptools/escape_test.go @@ -0,0 +1,169 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "os/exec" + "testing" +) + +type escapeTestCase struct { + name string + in string + out string +} + +var ninjaEscapeTestCase = []escapeTestCase{ + { + name: "no escaping", + in: `test`, + out: `test`, + }, + { + name: "leading $", + in: `$test`, + out: `$$test`, + }, + { + name: "trailing $", + in: `test$`, + out: `test$$`, + }, + { + name: "leading and trailing $", + in: `$test$`, + out: `$$test$$`, + }, +} + +var shellEscapeTestCase = []escapeTestCase{ + { + name: "no escaping", + in: `test`, + out: `test`, + }, + { + name: "leading $", + in: `$test`, + out: `'$test'`, + }, + { + name: "trailing $", + in: `test$`, + out: `'test$'`, + }, + { + name: "leading and trailing $", + in: `$test$`, + out: `'$test$'`, + }, + { + name: "single quote", + in: `'`, + out: `''\'''`, + }, + { + name: "multiple single quote", + in: `''`, + out: `''\'''\'''`, + }, + { + name: "double quote", + in: `""`, + out: `'""'`, + }, + { + name: "ORIGIN", + in: `-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs`, + out: `'-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs'`, + }, +} + +var shellEscapeIncludingSpacesTestCase = []escapeTestCase{ + { + name: "no escaping", + in: `test`, + out: `test`, + }, + { + name: "spacing", + in: `arg1 arg2`, + out: `'arg1 arg2'`, + }, + { + name: "single quote", + in: `'arg'`, + out: `''\''arg'\'''`, + }, +} + +func TestNinjaEscaping(t *testing.T) { + for _, testCase := range ninjaEscapeTestCase { + got := NinjaEscape(testCase.in) + if got != testCase.out { + t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got) + } + } +} + +func TestShellEscaping(t *testing.T) { + for _, testCase := range shellEscapeTestCase { + got := ShellEscape(testCase.in) + if got != testCase.out { + t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got) + } + } +} + +func TestShellEscapeIncludingSpaces(t *testing.T) { + for _, testCase := range shellEscapeIncludingSpacesTestCase { + got := ShellEscapeIncludingSpaces(testCase.in) + if got != testCase.out { + t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got) + } + } +} + +func TestExternalShellEscaping(t *testing.T) { + if testing.Short() { + return + } + for _, testCase := range shellEscapeTestCase { + cmd := "echo -n " + ShellEscape(testCase.in) + got, err := exec.Command("/bin/sh", "-c", cmd).Output() + if err != nil { + t.Error(err) + } + if string(got) != testCase.in { + t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.in, got) + } + } +} + +func TestExternalShellEscapeIncludingSpaces(t *testing.T) { + if testing.Short() { + return + } + for _, testCase := range shellEscapeIncludingSpacesTestCase { + cmd := "echo -n " + ShellEscapeIncludingSpaces(testCase.in) + got, err := exec.Command("/bin/sh", "-c", cmd).Output() + if err != nil { + t.Error(err) + } + if string(got) != testCase.in { + t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.in, got) + } + } +} diff --git a/blueprint/proptools/extend.go b/blueprint/proptools/extend.go new file mode 100644 index 0000000..4e2f498 --- /dev/null +++ b/blueprint/proptools/extend.go @@ -0,0 +1,577 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" +) + +// AppendProperties appends the values of properties in the property struct src to the property +// struct dst. dst and src must be the same type, and both must be pointers to structs. Properties +// tagged `blueprint:"mutated"` are skipped. +// +// The filter function can prevent individual properties from being appended by returning false, or +// abort AppendProperties with an error by returning an error. Passing nil for filter will append +// all properties. +// +// An error returned by AppendProperties that applies to a specific property will be an +// *ExtendPropertyError, and can have the property name and error extracted from it. +// +// The append operation is defined as appending strings and slices of strings normally, OR-ing bool +// values, replacing non-nil pointers to booleans or strings, and recursing into +// embedded structs, pointers to structs, and interfaces containing +// pointers to structs. Appending the zero value of a property will always be a no-op. +func AppendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error { + return extendProperties(dst, src, filter, OrderAppend) +} + +// PrependProperties prepends the values of properties in the property struct src to the property +// struct dst. dst and src must be the same type, and both must be pointers to structs. Properties +// tagged `blueprint:"mutated"` are skipped. +// +// The filter function can prevent individual properties from being prepended by returning false, or +// abort PrependProperties with an error by returning an error. Passing nil for filter will prepend +// all properties. +// +// An error returned by PrependProperties that applies to a specific property will be an +// *ExtendPropertyError, and can have the property name and error extracted from it. +// +// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing +// bool values, replacing non-nil pointers to booleans or strings, and recursing into +// embedded structs, pointers to structs, and interfaces containing +// pointers to structs. Prepending the zero value of a property will always be a no-op. +func PrependProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error { + return extendProperties(dst, src, filter, OrderPrepend) +} + +// AppendMatchingProperties appends the values of properties in the property struct src to the +// property structs in dst. dst and src do not have to be the same type, but every property in src +// must be found in at least one property in dst. dst must be a slice of pointers to structs, and +// src must be a pointer to a struct. Properties tagged `blueprint:"mutated"` are skipped. +// +// The filter function can prevent individual properties from being appended by returning false, or +// abort AppendProperties with an error by returning an error. Passing nil for filter will append +// all properties. +// +// An error returned by AppendMatchingProperties that applies to a specific property will be an +// *ExtendPropertyError, and can have the property name and error extracted from it. +// +// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool +// values, replacing pointers to booleans or strings whether they are nil or not, and recursing into +// embedded structs, pointers to structs, and interfaces containing +// pointers to structs. Appending the zero value of a property will always be a no-op. +func AppendMatchingProperties(dst []interface{}, src interface{}, + filter ExtendPropertyFilterFunc) error { + return extendMatchingProperties(dst, src, filter, OrderAppend) +} + +// PrependMatchingProperties prepends the values of properties in the property struct src to the +// property structs in dst. dst and src do not have to be the same type, but every property in src +// must be found in at least one property in dst. dst must be a slice of pointers to structs, and +// src must be a pointer to a struct. Properties tagged `blueprint:"mutated"` are skipped. +// +// The filter function can prevent individual properties from being prepended by returning false, or +// abort PrependProperties with an error by returning an error. Passing nil for filter will prepend +// all properties. +// +// An error returned by PrependProperties that applies to a specific property will be an +// *ExtendPropertyError, and can have the property name and error extracted from it. +// +// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing +// bool values, replacing nil pointers to booleans or strings, and recursing into +// embedded structs, pointers to structs, and interfaces containing +// pointers to structs. Prepending the zero value of a property will always be a no-op. +func PrependMatchingProperties(dst []interface{}, src interface{}, + filter ExtendPropertyFilterFunc) error { + return extendMatchingProperties(dst, src, filter, OrderPrepend) +} + +// ExtendProperties appends or prepends the values of properties in the property struct src to the +// property struct dst. dst and src must be the same type, and both must be pointers to structs. +// Properties tagged `blueprint:"mutated"` are skipped. +// +// The filter function can prevent individual properties from being appended or prepended by +// returning false, or abort ExtendProperties with an error by returning an error. Passing nil for +// filter will append or prepend all properties. +// +// The order function is called on each non-filtered property to determine if it should be appended +// or prepended. +// +// An error returned by ExtendProperties that applies to a specific property will be an +// *ExtendPropertyError, and can have the property name and error extracted from it. +// +// The append operation is defined as appending strings and slices of strings normally, OR-ing bool +// values, replacing non-nil pointers to booleans or strings, and recursing into +// embedded structs, pointers to structs, and interfaces containing +// pointers to structs. Appending or prepending the zero value of a property will always be a +// no-op. +func ExtendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc, + order ExtendPropertyOrderFunc) error { + return extendProperties(dst, src, filter, order) +} + +// ExtendMatchingProperties appends or prepends the values of properties in the property struct src +// to the property structs in dst. dst and src do not have to be the same type, but every property +// in src must be found in at least one property in dst. dst must be a slice of pointers to +// structs, and src must be a pointer to a struct. Properties tagged `blueprint:"mutated"` are +// skipped. +// +// The filter function can prevent individual properties from being appended or prepended by +// returning false, or abort ExtendMatchingProperties with an error by returning an error. Passing +// nil for filter will append or prepend all properties. +// +// The order function is called on each non-filtered property to determine if it should be appended +// or prepended. +// +// An error returned by ExtendMatchingProperties that applies to a specific property will be an +// *ExtendPropertyError, and can have the property name and error extracted from it. +// +// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool +// values, replacing non-nil pointers to booleans or strings, and recursing into +// embedded structs, pointers to structs, and interfaces containing +// pointers to structs. Appending or prepending the zero value of a property will always be a +// no-op. +func ExtendMatchingProperties(dst []interface{}, src interface{}, + filter ExtendPropertyFilterFunc, order ExtendPropertyOrderFunc) error { + return extendMatchingProperties(dst, src, filter, order) +} + +type Order int + +const ( + Append Order = iota + Prepend + Replace +) + +type ExtendPropertyFilterFunc func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (bool, error) + +type ExtendPropertyOrderFunc func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (Order, error) + +func OrderAppend(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (Order, error) { + return Append, nil +} + +func OrderPrepend(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (Order, error) { + return Prepend, nil +} + +func OrderReplace(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (Order, error) { + return Replace, nil +} + +type ExtendPropertyError struct { + Err error + Property string +} + +func (e *ExtendPropertyError) Error() string { + return fmt.Sprintf("can't extend property %q: %s", e.Property, e.Err) +} + +func extendPropertyErrorf(property string, format string, a ...interface{}) *ExtendPropertyError { + return &ExtendPropertyError{ + Err: fmt.Errorf(format, a...), + Property: property, + } +} + +func extendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc, + order ExtendPropertyOrderFunc) error { + + srcValue, err := getStruct(src) + if err != nil { + if _, ok := err.(getStructEmptyError); ok { + return nil + } + return err + } + + dstValue, err := getOrCreateStruct(dst) + if err != nil { + return err + } + + if dstValue.Type() != srcValue.Type() { + return fmt.Errorf("expected matching types for dst and src, got %T and %T", dst, src) + } + + dstValues := []reflect.Value{dstValue} + + return extendPropertiesRecursive(dstValues, srcValue, "", filter, true, order) +} + +func extendMatchingProperties(dst []interface{}, src interface{}, filter ExtendPropertyFilterFunc, + order ExtendPropertyOrderFunc) error { + + srcValue, err := getStruct(src) + if err != nil { + if _, ok := err.(getStructEmptyError); ok { + return nil + } + return err + } + + dstValues := make([]reflect.Value, len(dst)) + for i := range dst { + var err error + dstValues[i], err = getOrCreateStruct(dst[i]) + if err != nil { + return err + } + } + + return extendPropertiesRecursive(dstValues, srcValue, "", filter, false, order) +} + +func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value, + prefix string, filter ExtendPropertyFilterFunc, sameTypes bool, + orderFunc ExtendPropertyOrderFunc) error { + + dstValuesCopied := false + + srcType := srcValue.Type() + for i, srcField := range typeFields(srcType) { + if ShouldSkipProperty(srcField) { + continue + } + + propertyName := prefix + PropertyNameForField(srcField.Name) + srcFieldValue := srcValue.Field(i) + + // Step into source interfaces + if srcFieldValue.Kind() == reflect.Interface { + if srcFieldValue.IsNil() { + continue + } + + srcFieldValue = srcFieldValue.Elem() + + if srcFieldValue.Kind() != reflect.Ptr { + return extendPropertyErrorf(propertyName, "interface not a pointer") + } + } + + // Step into source pointers to structs + if isStructPtr(srcFieldValue.Type()) { + if srcFieldValue.IsNil() { + continue + } + + srcFieldValue = srcFieldValue.Elem() + } + + found := false + var recurse []reflect.Value + // Use an iteration loop so elements can be added to the end of dstValues inside the loop. + for j := 0; j < len(dstValues); j++ { + dstValue := dstValues[j] + dstType := dstValue.Type() + var dstField reflect.StructField + + dstFields := typeFields(dstType) + if dstType == srcType { + dstField = dstFields[i] + } else { + var ok bool + for _, field := range dstFields { + if field.Name == srcField.Name { + dstField = field + ok = true + } else if IsEmbedded(field) { + embeddedDstValue := dstValue.FieldByIndex(field.Index) + if isStructPtr(embeddedDstValue.Type()) { + if embeddedDstValue.IsNil() { + newEmbeddedDstValue := reflect.New(embeddedDstValue.Type().Elem()) + embeddedDstValue.Set(newEmbeddedDstValue) + } + embeddedDstValue = embeddedDstValue.Elem() + } + if !isStruct(embeddedDstValue.Type()) { + return extendPropertyErrorf(propertyName, "%s is not a struct (%s)", + prefix+field.Name, embeddedDstValue.Type()) + } + // The destination struct contains an embedded struct, add it to the list + // of destinations to consider. Make a copy of dstValues if necessary + // to avoid modifying the backing array of an input parameter. + if !dstValuesCopied { + dstValues = append([]reflect.Value(nil), dstValues...) + dstValuesCopied = true + } + dstValues = append(dstValues, embeddedDstValue) + } + } + if !ok { + continue + } + } + + found = true + + dstFieldValue := dstValue.FieldByIndex(dstField.Index) + origDstFieldValue := dstFieldValue + + // Step into destination interfaces + if dstFieldValue.Kind() == reflect.Interface { + if dstFieldValue.IsNil() { + return extendPropertyErrorf(propertyName, "nilitude mismatch") + } + + dstFieldValue = dstFieldValue.Elem() + + if dstFieldValue.Kind() != reflect.Ptr { + return extendPropertyErrorf(propertyName, "interface not a pointer") + } + } + + // Step into destination pointers to structs + if isStructPtr(dstFieldValue.Type()) { + if dstFieldValue.IsNil() { + dstFieldValue = reflect.New(dstFieldValue.Type().Elem()) + origDstFieldValue.Set(dstFieldValue) + } + + dstFieldValue = dstFieldValue.Elem() + } + + switch srcFieldValue.Kind() { + case reflect.Struct: + if sameTypes && dstFieldValue.Type() != srcFieldValue.Type() { + return extendPropertyErrorf(propertyName, "mismatched types %s and %s", + dstFieldValue.Type(), srcFieldValue.Type()) + } + + // Recursively extend the struct's fields. + recurse = append(recurse, dstFieldValue) + continue + case reflect.Bool, reflect.String, reflect.Slice, reflect.Map: + if srcFieldValue.Type() != dstFieldValue.Type() { + return extendPropertyErrorf(propertyName, "mismatched types %s and %s", + dstFieldValue.Type(), srcFieldValue.Type()) + } + case reflect.Ptr: + if srcFieldValue.Type() != dstFieldValue.Type() { + return extendPropertyErrorf(propertyName, "mismatched types %s and %s", + dstFieldValue.Type(), srcFieldValue.Type()) + } + switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind { + case reflect.Bool, reflect.Int64, reflect.String, reflect.Struct: + // Nothing + default: + return extendPropertyErrorf(propertyName, "pointer is a %s", ptrKind) + } + default: + return extendPropertyErrorf(propertyName, "unsupported kind %s", + srcFieldValue.Kind()) + } + + dstFieldInterface := dstFieldValue.Interface() + srcFieldInterface := srcFieldValue.Interface() + + if filter != nil { + b, err := filter(propertyName, dstField, srcField, + dstFieldInterface, srcFieldInterface) + if err != nil { + return &ExtendPropertyError{ + Property: propertyName, + Err: err, + } + } + if !b { + continue + } + } + + order := Append + if orderFunc != nil { + var err error + order, err = orderFunc(propertyName, dstField, srcField, + dstFieldInterface, srcFieldInterface) + if err != nil { + return &ExtendPropertyError{ + Property: propertyName, + Err: err, + } + } + } + + ExtendBasicType(dstFieldValue, srcFieldValue, order) + } + + if len(recurse) > 0 { + err := extendPropertiesRecursive(recurse, srcFieldValue, + propertyName+".", filter, sameTypes, orderFunc) + if err != nil { + return err + } + } else if !found { + return extendPropertyErrorf(propertyName, "failed to find property to extend") + } + } + + return nil +} + +func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) { + prepend := order == Prepend + + switch srcFieldValue.Kind() { + case reflect.Bool: + // Boolean OR + dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Bool() || dstFieldValue.Bool())) + case reflect.String: + if prepend { + dstFieldValue.SetString(srcFieldValue.String() + + dstFieldValue.String()) + } else { + dstFieldValue.SetString(dstFieldValue.String() + + srcFieldValue.String()) + } + case reflect.Slice: + if srcFieldValue.IsNil() { + break + } + + newSlice := reflect.MakeSlice(srcFieldValue.Type(), 0, + dstFieldValue.Len()+srcFieldValue.Len()) + if prepend { + newSlice = reflect.AppendSlice(newSlice, srcFieldValue) + newSlice = reflect.AppendSlice(newSlice, dstFieldValue) + } else if order == Append { + newSlice = reflect.AppendSlice(newSlice, dstFieldValue) + newSlice = reflect.AppendSlice(newSlice, srcFieldValue) + } else { + // replace + newSlice = reflect.AppendSlice(newSlice, srcFieldValue) + } + dstFieldValue.Set(newSlice) + case reflect.Map: + if srcFieldValue.IsNil() { + break + } + var mapValue reflect.Value + // for append/prepend, maintain keys from original value + // for replace, replace entire map + if order == Replace || dstFieldValue.IsNil() { + mapValue = srcFieldValue + } else { + mapValue = dstFieldValue + + iter := srcFieldValue.MapRange() + for iter.Next() { + dstValue := dstFieldValue.MapIndex(iter.Key()) + if prepend { + // if the key exists in the map, keep the original value. + if !dstValue.IsValid() { + // otherwise, add the new value + mapValue.SetMapIndex(iter.Key(), iter.Value()) + } + } else { + // For append, replace the original value. + mapValue.SetMapIndex(iter.Key(), iter.Value()) + } + } + } + dstFieldValue.Set(mapValue) + case reflect.Ptr: + if srcFieldValue.IsNil() { + break + } + + switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind { + case reflect.Bool: + if prepend { + if dstFieldValue.IsNil() { + dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool()))) + } + } else { + // For append, replace the original value. + dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool()))) + } + case reflect.Int64: + if prepend { + if dstFieldValue.IsNil() { + // Int() returns Int64 + dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int()))) + } + } else { + // For append, replace the original value. + // Int() returns Int64 + dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int()))) + } + case reflect.String: + if prepend { + if dstFieldValue.IsNil() { + dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String()))) + } + } else { + // For append, replace the original value. + dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String()))) + } + default: + panic(fmt.Errorf("unexpected pointer kind %s", ptrKind)) + } + } +} + +// ShouldSkipProperty indicates whether a property should be skipped in processing. +func ShouldSkipProperty(structField reflect.StructField) bool { + return structField.PkgPath != "" || // The field is not exported so just skip it. + HasTag(structField, "blueprint", "mutated") // The field is not settable in a blueprint file +} + +// IsEmbedded indicates whether a property is embedded. This is useful for determining nesting name +// as the name of the embedded field is _not_ used in blueprint files. +func IsEmbedded(structField reflect.StructField) bool { + return structField.Name == "BlueprintEmbed" || structField.Anonymous +} + +type getStructEmptyError struct{} + +func (getStructEmptyError) Error() string { return "interface containing nil pointer" } + +func getOrCreateStruct(in interface{}) (reflect.Value, error) { + value, err := getStruct(in) + if _, ok := err.(getStructEmptyError); ok { + value := reflect.ValueOf(in) + newValue := reflect.New(value.Type().Elem()) + value.Set(newValue) + } + + return value, err +} + +func getStruct(in interface{}) (reflect.Value, error) { + value := reflect.ValueOf(in) + if !isStructPtr(value.Type()) { + return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %s", value.Type()) + } + if value.IsNil() { + return reflect.Value{}, getStructEmptyError{} + } + value = value.Elem() + return value, nil +} diff --git a/blueprint/proptools/extend_test.go b/blueprint/proptools/extend_test.go new file mode 100644 index 0000000..d2dac72 --- /dev/null +++ b/blueprint/proptools/extend_test.go @@ -0,0 +1,1841 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "errors" + "fmt" + "reflect" + "strings" + "testing" +) + +type appendPropertyTestCase struct { + name string + dst interface{} + src interface{} + out interface{} + order Order // default is Append + filter ExtendPropertyFilterFunc + err error +} + +func appendPropertiesTestCases() []appendPropertyTestCase { + return []appendPropertyTestCase{ + // Valid inputs + + { + name: "Append bool", + dst: &struct{ B1, B2, B3, B4 bool }{ + B1: true, + B2: false, + B3: true, + B4: false, + }, + src: &struct{ B1, B2, B3, B4 bool }{ + B1: true, + B2: true, + B3: false, + B4: false, + }, + out: &struct{ B1, B2, B3, B4 bool }{ + B1: true, + B2: true, + B3: true, + B4: false, + }, + }, + { + name: "Prepend bool", + dst: &struct{ B1, B2, B3, B4 bool }{ + B1: true, + B2: false, + B3: true, + B4: false, + }, + src: &struct{ B1, B2, B3, B4 bool }{ + B1: true, + B2: true, + B3: false, + B4: false, + }, + out: &struct{ B1, B2, B3, B4 bool }{ + B1: true, + B2: true, + B3: true, + B4: false, + }, + order: Prepend, + }, + { + name: "Append strings", + dst: &struct{ S string }{ + S: "string1", + }, + src: &struct{ S string }{ + S: "string2", + }, + out: &struct{ S string }{ + S: "string1string2", + }, + }, + { + name: "Prepend strings", + dst: &struct{ S string }{ + S: "string1", + }, + src: &struct{ S string }{ + S: "string2", + }, + out: &struct{ S string }{ + S: "string2string1", + }, + order: Prepend, + }, + { + name: "Append pointer to bool", + dst: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + B3: nil, + B4: BoolPtr(true), + B5: BoolPtr(false), + B6: nil, + B7: BoolPtr(true), + B8: BoolPtr(false), + B9: nil, + }, + src: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ + B1: nil, + B2: nil, + B3: nil, + B4: BoolPtr(true), + B5: BoolPtr(true), + B6: BoolPtr(true), + B7: BoolPtr(false), + B8: BoolPtr(false), + B9: BoolPtr(false), + }, + out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + B3: nil, + B4: BoolPtr(true), + B5: BoolPtr(true), + B6: BoolPtr(true), + B7: BoolPtr(false), + B8: BoolPtr(false), + B9: BoolPtr(false), + }, + }, + { + name: "Prepend pointer to bool", + dst: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + B3: nil, + B4: BoolPtr(true), + B5: BoolPtr(false), + B6: nil, + B7: BoolPtr(true), + B8: BoolPtr(false), + B9: nil, + }, + src: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ + B1: nil, + B2: nil, + B3: nil, + B4: BoolPtr(true), + B5: BoolPtr(true), + B6: BoolPtr(true), + B7: BoolPtr(false), + B8: BoolPtr(false), + B9: BoolPtr(false), + }, + out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ + B1: BoolPtr(true), + B2: BoolPtr(false), + B3: nil, + B4: BoolPtr(true), + B5: BoolPtr(false), + B6: BoolPtr(true), + B7: BoolPtr(true), + B8: BoolPtr(false), + B9: BoolPtr(false), + }, + order: Prepend, + }, + { + name: "Append pointer to integer", + dst: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ + I1: Int64Ptr(55), + I2: Int64Ptr(-3), + I3: nil, + I4: Int64Ptr(100), + I5: Int64Ptr(33), + I6: nil, + I7: Int64Ptr(77), + I8: Int64Ptr(0), + I9: nil, + }, + src: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ + I1: nil, + I2: nil, + I3: nil, + I4: Int64Ptr(1), + I5: Int64Ptr(-2), + I6: Int64Ptr(8), + I7: Int64Ptr(9), + I8: Int64Ptr(10), + I9: Int64Ptr(11), + }, + out: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ + I1: Int64Ptr(55), + I2: Int64Ptr(-3), + I3: nil, + I4: Int64Ptr(1), + I5: Int64Ptr(-2), + I6: Int64Ptr(8), + I7: Int64Ptr(9), + I8: Int64Ptr(10), + I9: Int64Ptr(11), + }, + }, + { + name: "Prepend pointer to integer", + dst: &struct{ I1, I2, I3 *int64 }{ + I1: Int64Ptr(55), + I3: nil, + }, + src: &struct{ I1, I2, I3 *int64 }{ + I2: Int64Ptr(33), + }, + out: &struct{ I1, I2, I3 *int64 }{ + I1: Int64Ptr(55), + I2: Int64Ptr(33), + I3: nil, + }, + order: Prepend, + }, + { + name: "Append pointer to strings", + dst: &struct{ S1, S2, S3, S4 *string }{ + S1: StringPtr("string1"), + S2: StringPtr("string2"), + }, + src: &struct{ S1, S2, S3, S4 *string }{ + S1: StringPtr("string3"), + S3: StringPtr("string4"), + }, + out: &struct{ S1, S2, S3, S4 *string }{ + S1: StringPtr("string3"), + S2: StringPtr("string2"), + S3: StringPtr("string4"), + S4: nil, + }, + }, + { + name: "Prepend pointer to strings", + dst: &struct{ S1, S2, S3, S4 *string }{ + S1: StringPtr("string1"), + S2: StringPtr("string2"), + }, + src: &struct{ S1, S2, S3, S4 *string }{ + S1: StringPtr("string3"), + S3: StringPtr("string4"), + }, + out: &struct{ S1, S2, S3, S4 *string }{ + S1: StringPtr("string1"), + S2: StringPtr("string2"), + S3: StringPtr("string4"), + S4: nil, + }, + order: Prepend, + }, + { + name: "Append slice", + dst: &struct{ S []string }{ + S: []string{"string1"}, + }, + src: &struct{ S []string }{ + S: []string{"string2"}, + }, + out: &struct{ S []string }{ + S: []string{"string1", "string2"}, + }, + }, + { + name: "Prepend slice", + dst: &struct{ S []string }{ + S: []string{"string1"}, + }, + src: &struct{ S []string }{ + S: []string{"string2"}, + }, + out: &struct{ S []string }{ + S: []string{"string2", "string1"}, + }, + order: Prepend, + }, + { + name: "Replace slice", + dst: &struct{ S []string }{ + S: []string{"string1"}, + }, + src: &struct{ S []string }{ + S: []string{"string2"}, + }, + out: &struct{ S []string }{ + S: []string{"string2"}, + }, + order: Replace, + }, + { + name: "Append empty slice", + dst: &struct{ S1, S2 []string }{ + S1: []string{"string1"}, + S2: []string{}, + }, + src: &struct{ S1, S2 []string }{ + S1: []string{}, + S2: []string{"string2"}, + }, + out: &struct{ S1, S2 []string }{ + S1: []string{"string1"}, + S2: []string{"string2"}, + }, + }, + { + name: "Prepend empty slice", + dst: &struct{ S1, S2 []string }{ + S1: []string{"string1"}, + S2: []string{}, + }, + src: &struct{ S1, S2 []string }{ + S1: []string{}, + S2: []string{"string2"}, + }, + out: &struct{ S1, S2 []string }{ + S1: []string{"string1"}, + S2: []string{"string2"}, + }, + order: Prepend, + }, + { + name: "Replace empty slice", + dst: &struct{ S1, S2 []string }{ + S1: []string{"string1"}, + S2: []string{}, + }, + src: &struct{ S1, S2 []string }{ + S1: []string{}, + S2: []string{"string2"}, + }, + out: &struct{ S1, S2 []string }{ + S1: []string{}, + S2: []string{"string2"}, + }, + order: Replace, + }, + { + name: "Append nil slice", + dst: &struct{ S1, S2, S3 []string }{ + S1: []string{"string1"}, + }, + src: &struct{ S1, S2, S3 []string }{ + S2: []string{"string2"}, + }, + out: &struct{ S1, S2, S3 []string }{ + S1: []string{"string1"}, + S2: []string{"string2"}, + S3: nil, + }, + }, + { + name: "Prepend nil slice", + dst: &struct{ S1, S2, S3 []string }{ + S1: []string{"string1"}, + }, + src: &struct{ S1, S2, S3 []string }{ + S2: []string{"string2"}, + }, + out: &struct{ S1, S2, S3 []string }{ + S1: []string{"string1"}, + S2: []string{"string2"}, + S3: nil, + }, + order: Prepend, + }, + { + name: "Replace nil slice", + dst: &struct{ S1, S2, S3 []string }{ + S1: []string{"string1"}, + }, + src: &struct{ S1, S2, S3 []string }{ + S2: []string{"string2"}, + }, + out: &struct{ S1, S2, S3 []string }{ + S1: []string{"string1"}, + S2: []string{"string2"}, + S3: nil, + }, + order: Replace, + }, + { + name: "Replace embedded slice", + dst: &struct{ S *struct{ S1 []string } }{ + S: &struct{ S1 []string }{ + S1: []string{"string1"}, + }, + }, + src: &struct{ S *struct{ S1 []string } }{ + S: &struct{ S1 []string }{ + S1: []string{"string2"}, + }, + }, + out: &struct{ S *struct{ S1 []string } }{ + S: &struct{ S1 []string }{ + S1: []string{"string2"}, + }, + }, + order: Replace, + }, + { + name: "Append slice of structs", + dst: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "foo"}, {F: "bar"}, + }, + }, + src: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "baz"}, + }, + }, + out: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "foo"}, {F: "bar"}, {F: "baz"}, + }, + }, + order: Append, + }, + { + name: "Prepend slice of structs", + dst: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "foo"}, {F: "bar"}, + }, + }, + src: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "baz"}, + }, + }, + out: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "baz"}, {F: "foo"}, {F: "bar"}, + }, + }, + order: Prepend, + }, + { + name: "Append map", + dst: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "", + "key1": "dst_value1", + "key2": "dst_value2", + }, + }, + src: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "src_value0", + "key1": "src_value1", + "key3": "src_value3", + }, + }, + out: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "src_value0", + "key1": "src_value1", + "key2": "dst_value2", + "key3": "src_value3", + }, + }, + order: Append, + }, + { + name: "Prepend map", + dst: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "", + "key1": "dst_value1", + "key2": "dst_value2", + }, + }, + src: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "src_value0", + "key1": "src_value1", + "key3": "src_value3", + }, + }, + out: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "", + "key1": "dst_value1", + "key2": "dst_value2", + "key3": "src_value3", + }, + }, + order: Prepend, + }, + { + name: "Replace map", + dst: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "", + "key1": "dst_value1", + "key2": "dst_value2", + }, + }, + src: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "src_value0", + "key1": "src_value1", + "key3": "src_value3", + }, + }, + out: &struct{ S map[string]string }{ + S: map[string]string{ + "key0": "src_value0", + "key1": "src_value1", + "key3": "src_value3", + }, + }, + order: Replace, + }, + { + name: "Append empty map", + dst: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{}, + }, + src: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{}, + S2: map[string]string{"key0": "src_value0"}, + }, + out: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{"key0": "src_value0"}, + }, + order: Append, + }, + { + name: "Prepend empty map", + dst: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{}, + }, + src: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{}, + S2: map[string]string{"key0": "src_value0"}, + }, + out: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{"key0": "src_value0"}, + }, + order: Prepend, + }, + { + name: "Replace empty map", + dst: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{}, + }, + src: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{}, + S2: map[string]string{"key0": "src_value0"}, + }, + out: &struct{ S1, S2 map[string]string }{ + S1: map[string]string{}, + S2: map[string]string{"key0": "src_value0"}, + }, + order: Replace, + }, + { + name: "Append nil map", + dst: &struct{ S1, S2, S3 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + }, + src: &struct{ S1, S2, S3 map[string]string }{ + S2: map[string]string{"key0": "src_value0"}, + }, + out: &struct{ S1, S2, S3 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{"key0": "src_value0"}, + }, + order: Append, + }, + { + name: "Prepend nil map", + dst: &struct{ S1, S2, S3 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + }, + src: &struct{ S1, S2, S3 map[string]string }{ + S2: map[string]string{"key0": "src_value0"}, + }, + out: &struct{ S1, S2, S3 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{"key0": "src_value0"}, + }, + order: Prepend, + }, + { + name: "Replace nil map", + dst: &struct{ S1, S2, S3 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + }, + src: &struct{ S1, S2, S3 map[string]string }{ + S2: map[string]string{"key0": "src_value0"}, + }, + out: &struct{ S1, S2, S3 map[string]string }{ + S1: map[string]string{"key0": "dst_value0"}, + S2: map[string]string{"key0": "src_value0"}, + S3: nil, + }, + order: Replace, + }, + { + name: "Replace slice of structs", + dst: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "foo"}, {F: "bar"}, + }, + }, + src: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "baz"}, + }, + }, + out: &struct{ S []struct{ F string } }{ + S: []struct{ F string }{ + {F: "baz"}, + }, + }, + order: Replace, + }, + { + name: "Append pointer", + dst: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string2", + }, + }, + out: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string1string2", + }, + }, + }, + { + name: "Prepend pointer", + dst: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string2", + }, + }, + out: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string2string1", + }, + }, + order: Prepend, + }, + { + name: "Append interface", + dst: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string2", + }, + }, + out: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1string2", + }, + }, + }, + { + name: "Prepend interface", + dst: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string2", + }, + }, + out: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string2string1", + }, + }, + order: Prepend, + }, + { + name: "Unexported field", + dst: &struct{ s string }{ + s: "string1", + }, + src: &struct{ s string }{ + s: "string2", + }, + out: &struct{ s string }{ + s: "string1", + }, + }, + { + name: "Unexported field", + dst: &struct{ i *int64 }{ + i: Int64Ptr(33), + }, + src: &struct{ i *int64 }{ + i: Int64Ptr(5), + }, + out: &struct{ i *int64 }{ + i: Int64Ptr(33), + }, + }, + { + name: "Empty struct", + dst: &struct{}{}, + src: &struct{}{}, + out: &struct{}{}, + }, + { + name: "Interface nil", + dst: &struct{ S interface{} }{ + S: nil, + }, + src: &struct{ S interface{} }{ + S: nil, + }, + out: &struct{ S interface{} }{ + S: nil, + }, + }, + { + name: "Pointer nil", + dst: &struct{ S *struct{} }{ + S: nil, + }, + src: &struct{ S *struct{} }{ + S: nil, + }, + out: &struct{ S *struct{} }{ + S: nil, + }, + }, + { + name: "Anonymous struct", + dst: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1", + I: Int64Ptr(55), + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string2", + I: Int64Ptr(-4), + }, + }, + }, + src: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string3", + I: Int64Ptr(66), + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string4", + I: Int64Ptr(-8), + }, + }, + }, + out: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1string3", + I: Int64Ptr(66), + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string2string4", + I: Int64Ptr(-8), + }, + }, + }, + }, + { + name: "BlueprintEmbed struct", + dst: &struct { + BlueprintEmbed EmbeddedStruct + Nested struct{ BlueprintEmbed EmbeddedStruct } + }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string1", + I: Int64Ptr(55), + }, + Nested: struct{ BlueprintEmbed EmbeddedStruct }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string2", + I: Int64Ptr(-4), + }, + }, + }, + src: &struct { + BlueprintEmbed EmbeddedStruct + Nested struct{ BlueprintEmbed EmbeddedStruct } + }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string3", + I: Int64Ptr(66), + }, + Nested: struct{ BlueprintEmbed EmbeddedStruct }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string4", + I: Int64Ptr(-8), + }, + }, + }, + out: &struct { + BlueprintEmbed EmbeddedStruct + Nested struct{ BlueprintEmbed EmbeddedStruct } + }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string1string3", + I: Int64Ptr(66), + }, + Nested: struct{ BlueprintEmbed EmbeddedStruct }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string2string4", + I: Int64Ptr(-8), + }, + }, + }, + }, + { + name: "Anonymous interface", + dst: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct { + S string + I *int64 + }{ + S: "string1", + I: Int64Ptr(-8), + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct { + S string + I *int64 + }{ + S: "string2", + I: Int64Ptr(55), + }, + }, + }, + src: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct { + S string + I *int64 + }{ + S: "string3", + I: Int64Ptr(6), + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct { + S string + I *int64 + }{ + S: "string4", + I: Int64Ptr(6), + }, + }, + }, + out: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct { + S string + I *int64 + }{ + S: "string1string3", + I: Int64Ptr(6), + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct { + S string + I *int64 + }{ + S: "string2string4", + I: Int64Ptr(6), + }, + }, + }, + }, + { + name: "Nil pointer to a struct", + dst: &struct { + Nested *struct { + S string + } + }{}, + src: &struct { + Nested *struct { + S string + } + }{ + Nested: &struct { + S string + }{ + S: "string", + }, + }, + out: &struct { + Nested *struct { + S string + } + }{ + Nested: &struct { + S string + }{ + S: "string", + }, + }, + }, + { + name: "Nil pointer to a struct in an interface", + dst: &struct { + Nested interface{} + }{ + Nested: (*struct{ S string })(nil), + }, + src: &struct { + Nested interface{} + }{ + Nested: &struct { + S string + }{ + S: "string", + }, + }, + out: &struct { + Nested interface{} + }{ + Nested: &struct { + S string + }{ + S: "string", + }, + }, + }, + { + name: "Interface src nil", + dst: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S interface{} }{ + S: nil, + }, + out: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + + // Errors + + { + name: "Non-pointer dst", + dst: struct{}{}, + src: &struct{}{}, + err: errors.New("expected pointer to struct, got struct {}"), + out: struct{}{}, + }, + { + name: "Non-pointer src", + dst: &struct{}{}, + src: struct{}{}, + err: errors.New("expected pointer to struct, got struct {}"), + out: &struct{}{}, + }, + { + name: "Non-struct dst", + dst: &[]string{"bad"}, + src: &struct{}{}, + err: errors.New("expected pointer to struct, got *[]string"), + out: &[]string{"bad"}, + }, + { + name: "Non-struct src", + dst: &struct{}{}, + src: &[]string{"bad"}, + err: errors.New("expected pointer to struct, got *[]string"), + out: &struct{}{}, + }, + { + name: "Mismatched types", + dst: &struct{ A string }{ + A: "string1", + }, + src: &struct{ B string }{ + B: "string2", + }, + out: &struct{ A string }{ + A: "string1", + }, + err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"), + }, + { + name: "Unsupported kind", + dst: &struct{ I int }{ + I: 1, + }, + src: &struct{ I int }{ + I: 2, + }, + out: &struct{ I int }{ + I: 1, + }, + err: extendPropertyErrorf("i", "unsupported kind int"), + }, + { + name: "Unsupported kind", + dst: &struct{ I int64 }{ + I: 1, + }, + src: &struct{ I int64 }{ + I: 2, + }, + out: &struct{ I int64 }{ + I: 1, + }, + err: extendPropertyErrorf("i", "unsupported kind int64"), + }, + { + name: "Interface nilitude mismatch", + dst: &struct{ S interface{} }{ + S: nil, + }, + src: &struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + out: &struct{ S interface{} }{ + S: nil, + }, + err: extendPropertyErrorf("s", "nilitude mismatch"), + }, + { + name: "Interface type mismatch", + dst: &struct{ S interface{} }{ + S: &struct{ A string }{ + A: "string1", + }, + }, + src: &struct{ S interface{} }{ + S: &struct{ B string }{ + B: "string2", + }, + }, + out: &struct{ S interface{} }{ + S: &struct{ A string }{ + A: "string1", + }, + }, + err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"), + }, + { + name: "Interface not a pointer", + dst: &struct{ S interface{} }{ + S: struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S interface{} }{ + S: struct{ S string }{ + S: "string2", + }, + }, + out: &struct{ S interface{} }{ + S: struct{ S string }{ + S: "string1", + }, + }, + err: extendPropertyErrorf("s", "interface not a pointer"), + }, + { + name: "Pointer not a struct", + dst: &struct{ S *[]string }{ + S: &[]string{"string1"}, + }, + src: &struct{ S *[]string }{ + S: &[]string{"string2"}, + }, + out: &struct{ S *[]string }{ + S: &[]string{"string1"}, + }, + err: extendPropertyErrorf("s", "pointer is a slice"), + }, + { + name: "Error in nested struct", + dst: &struct{ S interface{} }{ + S: &struct{ I int }{ + I: 1, + }, + }, + src: &struct{ S interface{} }{ + S: &struct{ I int }{ + I: 2, + }, + }, + out: &struct{ S interface{} }{ + S: &struct{ I int }{ + I: 1, + }, + }, + err: extendPropertyErrorf("s.i", "unsupported kind int"), + }, + + // Filters + + { + name: "Filter true", + dst: &struct{ S string }{ + S: "string1", + }, + src: &struct{ S string }{ + S: "string2", + }, + out: &struct{ S string }{ + S: "string1string2", + }, + filter: func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (bool, error) { + return true, nil + }, + }, + { + name: "Filter false", + dst: &struct{ S string }{ + S: "string1", + }, + src: &struct{ S string }{ + S: "string2", + }, + out: &struct{ S string }{ + S: "string1", + }, + filter: func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (bool, error) { + return false, nil + }, + }, + { + name: "Filter check args", + dst: &struct{ S string }{ + S: "string1", + }, + src: &struct{ S string }{ + S: "string2", + }, + out: &struct{ S string }{ + S: "string1string2", + }, + filter: func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (bool, error) { + return property == "s" && + dstField.Name == "S" && srcField.Name == "S" && + dstValue.(string) == "string1" && srcValue.(string) == "string2", nil + }, + }, + { + name: "Filter mutated", + dst: &struct { + S string `blueprint:"mutated"` + }{ + S: "string1", + }, + src: &struct { + S string `blueprint:"mutated"` + }{ + S: "string2", + }, + out: &struct { + S string `blueprint:"mutated"` + }{ + S: "string1", + }, + }, + { + name: "Filter mutated", + dst: &struct { + S *int64 `blueprint:"mutated"` + }{ + S: Int64Ptr(4), + }, + src: &struct { + S *int64 `blueprint:"mutated"` + }{ + S: Int64Ptr(5), + }, + out: &struct { + S *int64 `blueprint:"mutated"` + }{ + S: Int64Ptr(4), + }, + }, + { + name: "Filter error", + dst: &struct{ S string }{ + S: "string1", + }, + src: &struct{ S string }{ + S: "string2", + }, + out: &struct{ S string }{ + S: "string1", + }, + filter: func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (bool, error) { + return true, fmt.Errorf("filter error") + }, + err: extendPropertyErrorf("s", "filter error"), + }, + } +} + +func TestAppendProperties(t *testing.T) { + for _, testCase := range appendPropertiesTestCases() { + t.Run(testCase.name, func(t *testing.T) { + + got := testCase.dst + var err error + var testType string + + switch testCase.order { + case Append: + testType = "append" + err = AppendProperties(got, testCase.src, testCase.filter) + case Prepend: + testType = "prepend" + err = PrependProperties(got, testCase.src, testCase.filter) + case Replace: + testType = "replace" + err = ExtendProperties(got, testCase.src, testCase.filter, OrderReplace) + } + + check(t, testType, testCase.name, got, err, testCase.out, testCase.err) + }) + } +} + +func TestExtendProperties(t *testing.T) { + for _, testCase := range appendPropertiesTestCases() { + t.Run(testCase.name, func(t *testing.T) { + + got := testCase.dst + var err error + var testType string + + order := func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (Order, error) { + switch testCase.order { + case Append: + return Append, nil + case Prepend: + return Prepend, nil + case Replace: + return Replace, nil + } + return Append, errors.New("unknown order") + } + + switch testCase.order { + case Append: + testType = "prepend" + case Prepend: + testType = "append" + case Replace: + testType = "replace" + } + + err = ExtendProperties(got, testCase.src, testCase.filter, order) + + check(t, testType, testCase.name, got, err, testCase.out, testCase.err) + }) + } +} + +type appendMatchingPropertiesTestCase struct { + name string + dst []interface{} + src interface{} + out []interface{} + order Order // default is Append + filter ExtendPropertyFilterFunc + err error +} + +func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase { + return []appendMatchingPropertiesTestCase{ + { + name: "Append strings", + dst: []interface{}{&struct{ S string }{ + S: "string1", + }}, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{&struct{ S string }{ + S: "string1string2", + }}, + }, + { + name: "Prepend strings", + dst: []interface{}{&struct{ S string }{ + S: "string1", + }}, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{&struct{ S string }{ + S: "string2string1", + }}, + order: Prepend, + }, + { + name: "Append all", + dst: []interface{}{ + &struct{ S, A string }{ + S: "string1", + }, + &struct{ S, B string }{ + S: "string2", + }, + }, + src: &struct{ S string }{ + S: "string3", + }, + out: []interface{}{ + &struct{ S, A string }{ + S: "string1string3", + }, + &struct{ S, B string }{ + S: "string2string3", + }, + }, + }, + { + name: "Append some", + dst: []interface{}{ + &struct{ S, A string }{ + S: "string1", + }, + &struct{ B string }{}, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ S, A string }{ + S: "string1string2", + }, + &struct{ B string }{}, + }, + }, + { + name: "Append mismatched structs", + dst: []interface{}{&struct{ S, A string }{ + S: "string1", + }}, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{&struct{ S, A string }{ + S: "string1string2", + }}, + }, + { + name: "Append mismatched pointer structs", + dst: []interface{}{&struct{ S *struct{ S, A string } }{ + S: &struct{ S, A string }{ + S: "string1", + }, + }}, + src: &struct{ S *struct{ S string } }{ + S: &struct{ S string }{ + S: "string2", + }, + }, + out: []interface{}{&struct{ S *struct{ S, A string } }{ + S: &struct{ S, A string }{ + S: "string1string2", + }, + }}, + }, + { + name: "Append through mismatched types", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ S interface{} }{ + S: &struct{ S, A string }{ + S: "string1", + }, + }, + }, + src: &struct{ S struct{ S string } }{ + S: struct{ S string }{ + S: "string2", + }, + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ S interface{} }{ + S: &struct{ S, A string }{ + S: "string1string2", + }, + }, + }, + }, + { + name: "Append through mismatched types and nil", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ S interface{} }{ + S: (*struct{ S, A string })(nil), + }, + }, + src: &struct{ S struct{ S string } }{ + S: struct{ S string }{ + S: "string2", + }, + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ S interface{} }{ + S: &struct{ S, A string }{ + S: "string2", + }, + }, + }, + }, + { + name: "Append through multiple matches", + dst: []interface{}{ + &struct { + S struct{ S, A string } + }{ + S: struct{ S, A string }{ + S: "string1", + }, + }, + &struct { + S struct{ S, B string } + }{ + S: struct{ S, B string }{ + S: "string2", + }, + }, + }, + src: &struct{ S struct{ B string } }{ + S: struct{ B string }{ + B: "string3", + }, + }, + out: []interface{}{ + &struct { + S struct{ S, A string } + }{ + S: struct{ S, A string }{ + S: "string1", + }, + }, + &struct { + S struct{ S, B string } + }{ + S: struct{ S, B string }{ + S: "string2", + B: "string3", + }, + }, + }, + }, + { + name: "Append through embedded struct", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1", + }, + }, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1string2", + }, + }, + }, + }, + { + name: "Append through BlueprintEmbed struct", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ BlueprintEmbed EmbeddedStruct }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string1", + }, + }, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ BlueprintEmbed EmbeddedStruct }{ + BlueprintEmbed: EmbeddedStruct{ + S: "string1string2", + }, + }, + }, + }, + { + name: "Append through embedded pointer to struct", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ *EmbeddedStruct }{ + EmbeddedStruct: &EmbeddedStruct{ + S: "string1", + }, + }, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ *EmbeddedStruct }{ + EmbeddedStruct: &EmbeddedStruct{ + S: "string1string2", + }, + }, + }, + }, + { + name: "Append through BlueprintEmbed pointer to struct", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ BlueprintEmbed *EmbeddedStruct }{ + BlueprintEmbed: &EmbeddedStruct{ + S: "string1", + }, + }, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ BlueprintEmbed *EmbeddedStruct }{ + BlueprintEmbed: &EmbeddedStruct{ + S: "string1string2", + }, + }, + }, + }, + { + name: "Append through embedded nil pointer to struct", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ *EmbeddedStruct }{}, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ *EmbeddedStruct }{ + EmbeddedStruct: &EmbeddedStruct{ + S: "string2", + }, + }, + }, + }, + { + name: "Append through BlueprintEmbed nil pointer to struct", + dst: []interface{}{ + &struct{ B string }{}, + &struct{ BlueprintEmbed *EmbeddedStruct }{}, + }, + src: &struct{ S string }{ + S: "string2", + }, + out: []interface{}{ + &struct{ B string }{}, + &struct{ BlueprintEmbed *EmbeddedStruct }{ + BlueprintEmbed: &EmbeddedStruct{ + S: "string2", + }, + }, + }, + }, + + // Errors + + { + name: "Non-pointer dst", + dst: []interface{}{struct{}{}}, + src: &struct{}{}, + err: errors.New("expected pointer to struct, got struct {}"), + out: []interface{}{struct{}{}}, + }, + { + name: "Non-pointer src", + dst: []interface{}{&struct{}{}}, + src: struct{}{}, + err: errors.New("expected pointer to struct, got struct {}"), + out: []interface{}{&struct{}{}}, + }, + { + name: "Non-struct dst", + dst: []interface{}{&[]string{"bad"}}, + src: &struct{}{}, + err: errors.New("expected pointer to struct, got *[]string"), + out: []interface{}{&[]string{"bad"}}, + }, + { + name: "Non-struct src", + dst: []interface{}{&struct{}{}}, + src: &[]string{"bad"}, + err: errors.New("expected pointer to struct, got *[]string"), + out: []interface{}{&struct{}{}}, + }, + { + name: "Append none", + dst: []interface{}{ + &struct{ A string }{}, + &struct{ B string }{}, + }, + src: &struct{ S string }{ + S: "string1", + }, + out: []interface{}{ + &struct{ A string }{}, + &struct{ B string }{}, + }, + err: extendPropertyErrorf("s", "failed to find property to extend"), + }, + { + name: "Append mismatched kinds", + dst: []interface{}{ + &struct{ S string }{ + S: "string1", + }, + }, + src: &struct{ S []string }{ + S: []string{"string2"}, + }, + out: []interface{}{ + &struct{ S string }{ + S: "string1", + }, + }, + err: extendPropertyErrorf("s", "mismatched types string and []string"), + }, + { + name: "Append mismatched types", + dst: []interface{}{ + &struct{ S []int }{ + S: []int{1}, + }, + }, + src: &struct{ S []string }{ + S: []string{"string2"}, + }, + out: []interface{}{ + &struct{ S []int }{ + S: []int{1}, + }, + }, + err: extendPropertyErrorf("s", "mismatched types []int and []string"), + }, + } +} + +func TestAppendMatchingProperties(t *testing.T) { + for _, testCase := range appendMatchingPropertiesTestCases() { + t.Run(testCase.name, func(t *testing.T) { + + got := testCase.dst + var err error + var testType string + + switch testCase.order { + case Append: + testType = "append" + err = AppendMatchingProperties(got, testCase.src, testCase.filter) + case Prepend: + testType = "prepend" + err = PrependMatchingProperties(got, testCase.src, testCase.filter) + case Replace: + testType = "replace" + err = ExtendMatchingProperties(got, testCase.src, testCase.filter, OrderReplace) + } + + check(t, testType, testCase.name, got, err, testCase.out, testCase.err) + }) + } +} + +func TestExtendMatchingProperties(t *testing.T) { + for _, testCase := range appendMatchingPropertiesTestCases() { + t.Run(testCase.name, func(t *testing.T) { + + got := testCase.dst + var err error + var testType string + + order := func(property string, + dstField, srcField reflect.StructField, + dstValue, srcValue interface{}) (Order, error) { + switch testCase.order { + case Append: + return Append, nil + case Prepend: + return Prepend, nil + case Replace: + return Replace, nil + } + return Append, errors.New("unknown order") + } + + switch testCase.order { + case Append: + testType = "prepend matching" + case Prepend: + testType = "append matching" + case Replace: + testType = "replace matching" + } + + err = ExtendMatchingProperties(got, testCase.src, testCase.filter, order) + + check(t, testType, testCase.name, got, err, testCase.out, testCase.err) + }) + } +} + +func check(t *testing.T, testType, testString string, + got interface{}, err error, + expected interface{}, expectedErr error) { + + printedTestCase := false + e := func(s string, expected, got interface{}) { + if !printedTestCase { + t.Errorf("test case %s: %s", testType, testString) + printedTestCase = true + } + t.Errorf("incorrect %s", s) + t.Errorf(" expected: %s", p(expected)) + t.Errorf(" got: %s", p(got)) + } + + if err != nil { + if expectedErr != nil { + if err.Error() != expectedErr.Error() { + e("unexpected error", expectedErr.Error(), err.Error()) + } + } else { + e("unexpected error", nil, err.Error()) + } + } else { + if expectedErr != nil { + e("missing error", expectedErr, nil) + } + } + + if !reflect.DeepEqual(expected, got) { + e("output:", expected, got) + } +} + +func p(in interface{}) string { + if v, ok := in.([]interface{}); ok { + s := make([]string, len(v)) + for i := range v { + s[i] = fmt.Sprintf("%#v", v[i]) + } + return "[" + strings.Join(s, ", ") + "]" + } else { + return fmt.Sprintf("%#v", in) + } +} diff --git a/blueprint/proptools/filter.go b/blueprint/proptools/filter.go new file mode 100644 index 0000000..54a20d5 --- /dev/null +++ b/blueprint/proptools/filter.go @@ -0,0 +1,210 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" + "strconv" +) + +type FilterFieldPredicate func(field reflect.StructField, string string) (bool, reflect.StructField) + +type cantFitPanic struct { + field reflect.StructField + size int +} + +func (x cantFitPanic) Error() string { + return fmt.Sprintf("Can't fit field %s %s %s size %d into %d", + x.field.Name, x.field.Type.String(), strconv.Quote(string(x.field.Tag)), + fieldToTypeNameSize(x.field, true)+2, x.size) +} + +// All runtime created structs will have a name that starts with "struct {" and ends with "}" +const emptyStructTypeNameSize = len("struct {}") + +func filterPropertyStructFields(fields []reflect.StructField, prefix string, maxTypeNameSize int, + predicate FilterFieldPredicate) (filteredFieldsShards [][]reflect.StructField, filtered bool) { + + structNameSize := emptyStructTypeNameSize + + var filteredFields []reflect.StructField + + appendAndShardIfNameFull := func(field reflect.StructField) { + fieldTypeNameSize := fieldToTypeNameSize(field, true) + // Every field will have a space before it and either a semicolon or space after it. + fieldTypeNameSize += 2 + + if maxTypeNameSize > 0 && structNameSize+fieldTypeNameSize > maxTypeNameSize { + if len(filteredFields) == 0 { + if isStruct(field.Type) || isStructPtr(field.Type) { + // An error fitting the nested struct should have been caught when recursing + // into the nested struct. + panic(fmt.Errorf("Shouldn't happen: can't fit nested struct %q (%d) into %d", + field.Type.String(), len(field.Type.String()), maxTypeNameSize-structNameSize)) + } + panic(cantFitPanic{field, maxTypeNameSize - structNameSize}) + + } + filteredFieldsShards = append(filteredFieldsShards, filteredFields) + filteredFields = nil + structNameSize = emptyStructTypeNameSize + } + + filteredFields = append(filteredFields, field) + structNameSize += fieldTypeNameSize + } + + for _, field := range fields { + var keep bool + if keep, field = predicate(field, prefix); !keep { + filtered = true + continue + } + + subPrefix := field.Name + if prefix != "" { + subPrefix = prefix + "." + subPrefix + } + + ptrToStruct := false + if isStructPtr(field.Type) { + ptrToStruct = true + } + + // Recurse into structs + if ptrToStruct || isStruct(field.Type) { + subMaxTypeNameSize := maxTypeNameSize + if maxTypeNameSize > 0 { + // In the worst case where only this nested struct will fit in the outer struct, the + // outer struct will contribute struct{}, the name and tag of the field that contains + // the nested struct, and one space before and after the field. + subMaxTypeNameSize -= emptyStructTypeNameSize + fieldToTypeNameSize(field, false) + 2 + } + typ := field.Type + if ptrToStruct { + subMaxTypeNameSize -= len("*") + typ = typ.Elem() + } + nestedTypes, subFiltered := filterPropertyStruct(typ, subPrefix, subMaxTypeNameSize, predicate) + filtered = filtered || subFiltered + if nestedTypes == nil { + continue + } + + for _, nestedType := range nestedTypes { + if ptrToStruct { + nestedType = reflect.PtrTo(nestedType) + } + field.Type = nestedType + appendAndShardIfNameFull(field) + } + } else { + appendAndShardIfNameFull(field) + } + } + + if len(filteredFields) > 0 { + filteredFieldsShards = append(filteredFieldsShards, filteredFields) + } + + return filteredFieldsShards, filtered +} + +func fieldToTypeNameSize(field reflect.StructField, withType bool) int { + nameSize := len(field.Name) + nameSize += len(" ") + if withType { + nameSize += len(field.Type.String()) + } + if field.Tag != "" { + nameSize += len(" ") + nameSize += len(strconv.Quote(string(field.Tag))) + } + return nameSize +} + +// FilterPropertyStruct takes a reflect.Type that is either a struct or a pointer to a struct, and returns a +// reflect.Type that only contains the fields in the original type for which predicate returns true, and a bool +// that is true if the new struct type has fewer fields than the original type. If there are no fields in the +// original type for which predicate returns true it returns nil and true. +func FilterPropertyStruct(prop reflect.Type, predicate FilterFieldPredicate) (filteredProp reflect.Type, filtered bool) { + filteredFieldsShards, filtered := filterPropertyStruct(prop, "", -1, predicate) + switch len(filteredFieldsShards) { + case 0: + return nil, filtered + case 1: + return filteredFieldsShards[0], filtered + default: + panic("filterPropertyStruct should only return 1 struct if maxNameSize < 0") + } +} + +func filterPropertyStruct(prop reflect.Type, prefix string, maxNameSize int, + predicate FilterFieldPredicate) (filteredProp []reflect.Type, filtered bool) { + + var fields []reflect.StructField + + ptr := prop.Kind() == reflect.Ptr + if ptr { + prop = prop.Elem() + } + + for i := 0; i < prop.NumField(); i++ { + fields = append(fields, prop.Field(i)) + } + + filteredFieldsShards, filtered := filterPropertyStructFields(fields, prefix, maxNameSize, predicate) + + if len(filteredFieldsShards) == 0 { + return nil, true + } + + // If the predicate selected all fields in the structure then it is generally better to reuse the + // original type as it avoids the footprint of creating another type. Also, if the original type + // is a named type then it will reduce the size of any structs the caller may create that include + // fields of this type. However, the original type should only be reused if it does not exceed + // maxNameSize. That is, of course, more likely for an anonymous type than a named one but this + // treats them the same. + if !filtered && (maxNameSize < 0 || len(prop.String()) < maxNameSize) { + if ptr { + return []reflect.Type{reflect.PtrTo(prop)}, false + } + return []reflect.Type{prop}, false + } + + var ret []reflect.Type + for _, filteredFields := range filteredFieldsShards { + p := reflect.StructOf(filteredFields) + if ptr { + p = reflect.PtrTo(p) + } + ret = append(ret, p) + } + + return ret, true +} + +// FilterPropertyStructSharded takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a list +// of reflect.Type that only contains the fields in the original type for which predicate returns true, and a bool that +// is true if the new struct type has fewer fields than the original type. If there are no fields in the original type +// for which predicate returns true it returns nil and true. Each returned struct type will have a maximum of 10 top +// level fields in it to attempt to avoid hitting the 65535 byte type name length limit in reflect.StructOf +// (reflect.nameFrom: name too long), although the limit can still be reached with a single struct field with many +// fields in it. +func FilterPropertyStructSharded(prop reflect.Type, maxTypeNameSize int, predicate FilterFieldPredicate) (filteredProp []reflect.Type, filtered bool) { + return filterPropertyStruct(prop, "", maxTypeNameSize, predicate) +} diff --git a/blueprint/proptools/filter_test.go b/blueprint/proptools/filter_test.go new file mode 100644 index 0000000..1c27cb2 --- /dev/null +++ b/blueprint/proptools/filter_test.go @@ -0,0 +1,562 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "reflect" + "strings" + "testing" +) + +type Named struct { + A *string `keep:"true"` + B *string +} + +type NamedAllFiltered struct { + A *string +} + +type NamedNoneFiltered struct { + A *string `keep:"true"` +} + +func TestFilterPropertyStruct(t *testing.T) { + tests := []struct { + name string + in interface{} + out interface{} + filtered bool + }{ + // Property tests + { + name: "basic", + in: &struct { + A *string `keep:"true"` + B *string + }{}, + out: &struct { + A *string + }{}, + filtered: true, + }, + { + name: "all filtered", + in: &struct { + A *string + }{}, + out: nil, + filtered: true, + }, + { + name: "none filtered", + in: &struct { + A *string `keep:"true"` + }{}, + out: &struct { + A *string `keep:"true"` + }{}, + filtered: false, + }, + + // Sub-struct tests + { + name: "substruct", + in: &struct { + A struct { + A *string `keep:"true"` + B *string + } `keep:"true"` + }{}, + out: &struct { + A struct { + A *string + } + }{}, + filtered: true, + }, + { + name: "substruct all filtered", + in: &struct { + A struct { + A *string + } `keep:"true"` + }{}, + out: nil, + filtered: true, + }, + { + name: "substruct none filtered", + in: &struct { + A struct { + A *string `keep:"true"` + } `keep:"true"` + }{}, + out: &struct { + A struct { + A *string `keep:"true"` + } `keep:"true"` + }{}, + filtered: false, + }, + + // Named sub-struct tests + { + name: "named substruct", + in: &struct { + A Named `keep:"true"` + }{}, + out: &struct { + A struct { + A *string + } + }{}, + filtered: true, + }, + { + name: "substruct all filtered", + in: &struct { + A NamedAllFiltered `keep:"true"` + }{}, + out: nil, + filtered: true, + }, + { + name: "substruct none filtered", + in: &struct { + A NamedNoneFiltered `keep:"true"` + }{}, + out: &struct { + A NamedNoneFiltered `keep:"true"` + }{}, + filtered: false, + }, + + // Pointer to sub-struct tests + { + name: "pointer substruct", + in: &struct { + A *struct { + A *string `keep:"true"` + B *string + } `keep:"true"` + }{}, + out: &struct { + A *struct { + A *string + } + }{}, + filtered: true, + }, + { + name: "pointer substruct all filtered", + in: &struct { + A *struct { + A *string + } `keep:"true"` + }{}, + out: nil, + filtered: true, + }, + { + name: "pointer substruct none filtered", + in: &struct { + A *struct { + A *string `keep:"true"` + } `keep:"true"` + }{}, + out: &struct { + A *struct { + A *string `keep:"true"` + } `keep:"true"` + }{}, + filtered: false, + }, + + // Pointer to named sub-struct tests + { + name: "pointer named substruct", + in: &struct { + A *Named `keep:"true"` + }{}, + out: &struct { + A *struct { + A *string + } + }{}, + filtered: true, + }, + { + name: "pointer substruct all filtered", + in: &struct { + A *NamedAllFiltered `keep:"true"` + }{}, + out: nil, + filtered: true, + }, + { + name: "pointer substruct none filtered", + in: &struct { + A *NamedNoneFiltered `keep:"true"` + }{}, + out: &struct { + A *NamedNoneFiltered `keep:"true"` + }{}, + filtered: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + out, filtered := FilterPropertyStruct(reflect.TypeOf(test.in), + func(field reflect.StructField, prefix string) (bool, reflect.StructField) { + if HasTag(field, "keep", "true") { + field.Tag = "" + return true, field + } + return false, field + }) + if filtered != test.filtered { + t.Errorf("expected filtered %v, got %v", test.filtered, filtered) + } + expected := reflect.TypeOf(test.out) + if out != expected { + t.Errorf("expected type %v, got %v", expected, out) + } + }) + } +} + +func TestFilterPropertyStructSharded(t *testing.T) { + type KeepAllWithAReallyLongNameThatExceedsTheMaxNameSize struct { + A *string `keep:"true"` + B *string `keep:"true"` + C *string `keep:"true"` + } + + tests := []struct { + name string + maxNameSize int + in interface{} + out []interface{} + filtered bool + }{ + // Property tests + { + name: "basic", + maxNameSize: 20, + in: &struct { + A *string `keep:"true"` + B *string `keep:"true"` + C *string + }{}, + out: []interface{}{ + &struct { + A *string + }{}, + &struct { + B *string + }{}, + }, + filtered: true, + }, + { + name: "anonymous where all match but still needs sharding", + maxNameSize: 20, + in: &struct { + A *string `keep:"true"` + B *string `keep:"true"` + C *string `keep:"true"` + }{}, + out: []interface{}{ + &struct { + A *string + }{}, + &struct { + B *string + }{}, + &struct { + C *string + }{}, + }, + filtered: true, + }, + { + name: "named where all match but still needs sharding", + maxNameSize: 20, + in: &KeepAllWithAReallyLongNameThatExceedsTheMaxNameSize{}, + out: []interface{}{ + &struct { + A *string + }{}, + &struct { + B *string + }{}, + &struct { + C *string + }{}, + }, + filtered: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + out, filtered := filterPropertyStruct(reflect.TypeOf(test.in), "", test.maxNameSize, + func(field reflect.StructField, prefix string) (bool, reflect.StructField) { + if HasTag(field, "keep", "true") { + field.Tag = "" + return true, field + } + return false, field + }) + if filtered != test.filtered { + t.Errorf("expected filtered %v, got %v", test.filtered, filtered) + } + var expected []reflect.Type + for _, t := range test.out { + expected = append(expected, reflect.TypeOf(t)) + } + if !reflect.DeepEqual(out, expected) { + t.Errorf("expected type %v, got %v", expected, out) + } + }) + } +} + +func Test_fieldToTypeNameSize(t *testing.T) { + tests := []struct { + name string + field reflect.StructField + }{ + { + name: "string", + field: reflect.StructField{ + Name: "Foo", + Type: reflect.TypeOf(""), + }, + }, + { + name: "string pointer", + field: reflect.StructField{ + Name: "Foo", + Type: reflect.TypeOf(StringPtr("")), + }, + }, + { + name: "anonymous struct", + field: reflect.StructField{ + Name: "Foo", + Type: reflect.TypeOf(struct{ foo string }{}), + }, + }, { + name: "anonymous struct pointer", + field: reflect.StructField{ + Name: "Foo", + Type: reflect.TypeOf(&struct{ foo string }{}), + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + typeName := reflect.StructOf([]reflect.StructField{test.field}).String() + typeName = strings.TrimPrefix(typeName, "struct { ") + typeName = strings.TrimSuffix(typeName, " }") + if g, w := fieldToTypeNameSize(test.field, true), len(typeName); g != w { + t.Errorf("want fieldToTypeNameSize(..., true) = %v, got %v", w, g) + } + if g, w := fieldToTypeNameSize(test.field, false), len(typeName)-len(test.field.Type.String()); g != w { + t.Errorf("want fieldToTypeNameSize(..., false) = %v, got %v", w, g) + } + }) + } +} + +func Test_filterPropertyStructFields(t *testing.T) { + type args struct { + } + tests := []struct { + name string + maxTypeNameSize int + in interface{} + out []interface{} + }{ + { + name: "empty", + maxTypeNameSize: -1, + in: struct{}{}, + out: nil, + }, + { + name: "one", + maxTypeNameSize: -1, + in: struct { + A *string + }{}, + out: []interface{}{ + struct { + A *string + }{}, + }, + }, + { + name: "two", + maxTypeNameSize: 20, + in: struct { + A *string + B *string + }{}, + out: []interface{}{ + struct { + A *string + }{}, + struct { + B *string + }{}, + }, + }, + { + name: "nested", + maxTypeNameSize: 36, + in: struct { + AAAAA struct { + A string + } + BBBBB struct { + B string + } + }{}, + out: []interface{}{ + struct { + AAAAA struct { + A string + } + }{}, + struct { + BBBBB struct { + B string + } + }{}, + }, + }, + { + name: "nested pointer", + maxTypeNameSize: 37, + in: struct { + AAAAA *struct { + A string + } + BBBBB *struct { + B string + } + }{}, + out: []interface{}{ + struct { + AAAAA *struct { + A string + } + }{}, + struct { + BBBBB *struct { + B string + } + }{}, + }, + }, + { + name: "doubly nested", + maxTypeNameSize: 49, + in: struct { + AAAAA struct { + A struct { + A string + } + } + BBBBB struct { + B struct { + B string + } + } + }{}, + out: []interface{}{ + struct { + AAAAA struct { + A struct { + A string + } + } + }{}, + struct { + BBBBB struct { + B struct { + B string + } + } + }{}, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + inType := reflect.TypeOf(test.in) + var in []reflect.StructField + for i := 0; i < inType.NumField(); i++ { + in = append(in, inType.Field(i)) + } + + keep := func(field reflect.StructField, string string) (bool, reflect.StructField) { + return true, field + } + + // Test that maxTypeNameSize is the + if test.maxTypeNameSize > 0 { + correctPanic := false + func() { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(cantFitPanic); ok { + correctPanic = true + } else { + panic(r) + } + } + }() + + _, _ = filterPropertyStructFields(in, "", test.maxTypeNameSize-1, keep) + }() + + if !correctPanic { + t.Errorf("filterPropertyStructFields() with size-1 should produce cantFitPanic") + } + } + + filteredFieldsShards, _ := filterPropertyStructFields(in, "", test.maxTypeNameSize, keep) + + var out []interface{} + for _, filteredFields := range filteredFieldsShards { + typ := reflect.StructOf(filteredFields) + if test.maxTypeNameSize > 0 && len(typ.String()) > test.maxTypeNameSize { + t.Errorf("out %q expected size <= %d, got %d", + typ.String(), test.maxTypeNameSize, len(typ.String())) + } + out = append(out, reflect.Zero(typ).Interface()) + } + + if g, w := out, test.out; !reflect.DeepEqual(g, w) { + t.Errorf("filterPropertyStructFields() want %v, got %v", w, g) + } + }) + } +} diff --git a/blueprint/proptools/proptools.go b/blueprint/proptools/proptools.go new file mode 100644 index 0000000..6946d7e --- /dev/null +++ b/blueprint/proptools/proptools.go @@ -0,0 +1,135 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +// PropertyNameForField converts the name of a field in property struct to the property name that +// might appear in a Blueprints file. Since the property struct fields must always be exported +// to be accessed with reflection and the canonical Blueprints style is lowercased names, it +// lower cases the first rune in the field name unless the field name contains an uppercase rune +// after the first rune (which is always uppercase), and no lowercase runes. +func PropertyNameForField(fieldName string) string { + r, size := utf8.DecodeRuneInString(fieldName) + propertyName := string(unicode.ToLower(r)) + if size == len(fieldName) { + return propertyName + } + if strings.IndexFunc(fieldName[size:], unicode.IsLower) == -1 && + strings.IndexFunc(fieldName[size:], unicode.IsUpper) != -1 { + return fieldName + } + if len(fieldName) > size { + propertyName += fieldName[size:] + } + return propertyName +} + +// FieldNameForProperty converts the name of a property that might appear in a Blueprints file to +// the name of a field in property struct by uppercasing the first rune. +func FieldNameForProperty(propertyName string) string { + r, size := utf8.DecodeRuneInString(propertyName) + fieldName := string(unicode.ToUpper(r)) + if len(propertyName) > size { + fieldName += propertyName[size:] + } + return fieldName +} + +// BoolPtr returns a pointer to a new bool containing the given value. +func BoolPtr(b bool) *bool { + return &b +} + +// Int64Ptr returns a pointer to a new int64 containing the given value. +func Int64Ptr(i int64) *int64 { + b := int64(i) + return &(b) +} + +// StringPtr returns a pointer to a new string containing the given value. +func StringPtr(s string) *string { + return &s +} + +// BoolDefault takes a pointer to a bool and returns the value pointed to by the pointer if it is non-nil, +// or def if the pointer is nil. +func BoolDefault(b *bool, def bool) bool { + if b != nil { + return *b + } + return def +} + +// Bool takes a pointer to a bool and returns true iff the pointer is non-nil and points to a true +// value. +func Bool(b *bool) bool { + return BoolDefault(b, false) +} + +// String takes a pointer to a string and returns the value of the string if the pointer is non-nil, +// or def if the pointer is nil. +func StringDefault(s *string, def string) string { + if s != nil { + return *s + } + return def +} + +// String takes a pointer to a string and returns the value of the string if the pointer is non-nil, +// or an empty string. +func String(s *string) string { + return StringDefault(s, "") +} + +// IntDefault takes a pointer to an int64 and returns the value pointed to by the pointer cast to int +// if it is non-nil, or def if the pointer is nil. +func IntDefault(i *int64, def int) int { + if i != nil { + return int(*i) + } + return def +} + +// Int takes a pointer to an int64 and returns the value pointed to by the pointer cast to int +// if it is non-nil, or 0 if the pointer is nil. +func Int(i *int64) int { + return IntDefault(i, 0) +} + +func isStruct(t reflect.Type) bool { + return t.Kind() == reflect.Struct +} + +func isStructPtr(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +func isSlice(t reflect.Type) bool { + return t.Kind() == reflect.Slice +} + +func isSliceOfStruct(t reflect.Type) bool { + return isSlice(t) && isStruct(t.Elem()) +} + +func isMapOfStruct(t reflect.Type) bool { + return t.Kind() == reflect.Map && isStruct(t.Elem()) +} diff --git a/blueprint/proptools/proptools_test.go b/blueprint/proptools/proptools_test.go new file mode 100644 index 0000000..207ee1b --- /dev/null +++ b/blueprint/proptools/proptools_test.go @@ -0,0 +1,114 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import "testing" + +func TestPropertyNameForField(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "short", + input: "S", + want: "s", + }, + { + name: "long", + input: "String", + want: "string", + }, + { + name: "uppercase", + input: "STRING", + want: "STRING", + }, + { + name: "mixed", + input: "StRiNg", + want: "stRiNg", + }, + { + name: "underscore", + input: "Under_score", + want: "under_score", + }, + { + name: "uppercase underscore", + input: "UNDER_SCORE", + want: "UNDER_SCORE", + }, + { + name: "x86", + input: "X86", + want: "x86", + }, + { + name: "x86_64", + input: "X86_64", + want: "x86_64", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := PropertyNameForField(tt.input); got != tt.want { + t.Errorf("PropertyNameForField(%v) = %v, want %v", tt.input, got, tt.want) + } + }) + } +} + +func TestFieldNameForProperty(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "short lowercase", + input: "s", + want: "S", + }, + { + name: "short uppercase", + input: "S", + want: "S", + }, + { + name: "long lowercase", + input: "string", + want: "String", + }, + { + name: "long uppercase", + input: "STRING", + want: "STRING", + }, + { + name: "mixed", + input: "StRiNg", + want: "StRiNg", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := FieldNameForProperty(tt.input); got != tt.want { + t.Errorf("FieldNameForProperty(%v) = %v, want %v", tt.input, got, tt.want) + } + }) + } +} diff --git a/blueprint/proptools/tag.go b/blueprint/proptools/tag.go new file mode 100644 index 0000000..801fa3b --- /dev/null +++ b/blueprint/proptools/tag.go @@ -0,0 +1,74 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" + "strings" +) + +// HasTag returns true if a StructField has a tag in the form `name:"foo,value"`. +func HasTag(field reflect.StructField, name, value string) bool { + tag := field.Tag.Get(name) + for len(tag) > 0 { + idx := strings.Index(tag, ",") + + if idx < 0 { + return tag == value + } + if tag[:idx] == value { + return true + } + + tag = tag[idx+1:] + } + + return false +} + +// PropertyIndexesWithTag returns the indexes of all properties (in the form used by reflect.Value.FieldByIndex) that +// are tagged with the given key and value, including ones found in embedded structs or pointers to structs. +func PropertyIndexesWithTag(ps interface{}, key, value string) [][]int { + t := reflect.TypeOf(ps) + if !isStructPtr(t) { + panic(fmt.Errorf("type %s is not a pointer to a struct", t)) + } + t = t.Elem() + + return propertyIndexesWithTag(t, key, value) +} +func propertyIndexesWithTag(t reflect.Type, key, value string) [][]int { + var indexes [][]int + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + ft := field.Type + if isStruct(ft) || isStructPtr(ft) || isSliceOfStruct(ft) { + if ft.Kind() == reflect.Ptr || ft.Kind() == reflect.Slice || ft.Kind() == reflect.Map { + ft = ft.Elem() + } + subIndexes := propertyIndexesWithTag(ft, key, value) + for _, sub := range subIndexes { + sub = append([]int{i}, sub...) + indexes = append(indexes, sub) + } + } else if HasTag(field, key, value) { + indexes = append(indexes, field.Index) + } + } + + return indexes +} diff --git a/blueprint/proptools/tag_test.go b/blueprint/proptools/tag_test.go new file mode 100644 index 0000000..d466859 --- /dev/null +++ b/blueprint/proptools/tag_test.go @@ -0,0 +1,198 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "reflect" + "testing" +) + +type testType struct { + NoTag string + EmptyTag string `` + OtherTag string `foo:"bar"` + MatchingTag string `name:"value"` + ExtraValues string `name:"foo,value,bar"` + ExtraTags string `foo:"bar" name:"value"` +} + +func TestHasTag(t *testing.T) { + tests := []struct { + field string + want bool + }{ + { + field: "NoTag", + want: false, + }, + { + field: "EmptyTag", + want: false, + }, + { + field: "OtherTag", + want: false, + }, + { + field: "MatchingTag", + want: true, + }, + { + field: "ExtraValues", + want: true, + }, + { + field: "ExtraTags", + want: true, + }, + } + for _, test := range tests { + t.Run(test.field, func(t *testing.T) { + field, _ := reflect.TypeOf(testType{}).FieldByName(test.field) + if got := HasTag(field, "name", "value"); got != test.want { + t.Errorf(`HasTag(%q, "name", "value") = %v, want %v`, field.Tag, got, test.want) + } + }) + } +} + +func BenchmarkHasTag(b *testing.B) { + tests := []struct { + field string + }{ + { + field: "NoTag", + }, + { + field: "EmptyTag", + }, + { + field: "OtherTag", + }, + { + field: "MatchingTag", + }, + { + field: "ExtraValues", + }, + { + field: "ExtraTags", + }, + } + for _, test := range tests { + b.Run(test.field, func(b *testing.B) { + field, _ := reflect.TypeOf(testType{}).FieldByName(test.field) + for i := 0; i < b.N; i++ { + HasTag(field, "name", "value") + } + }) + } +} + +func TestPropertyIndexesWithTag(t *testing.T) { + tests := []struct { + name string + ps interface{} + want [][]int + }{ + { + name: "none", + ps: &struct { + Foo string + }{}, + want: nil, + }, + { + name: "one", + ps: &struct { + Foo string `name:"value"` + }{}, + want: [][]int{{0}}, + }, + { + name: "two", + ps: &struct { + Foo string `name:"value"` + Bar string `name:"value"` + }{}, + want: [][]int{{0}, {1}}, + }, + { + name: "some", + ps: &struct { + Foo string `name:"other"` + Bar string `name:"value"` + }{}, + want: [][]int{{1}}, + }, + { + name: "embedded", + ps: &struct { + Foo struct { + Bar string `name:"value"` + } + }{}, + want: [][]int{{0, 0}}, + }, + { + name: "embedded ptr", + ps: &struct { + Foo *struct { + Bar string `name:"value"` + } + }{}, + want: [][]int{{0, 0}}, + }, + { + name: "slice of struct", + ps: &struct { + Other int + Foo []struct { + Other int + Bar string `name:"value"` + } + }{}, + want: [][]int{{1, 1}}, + }, + { + name: "slice^2 of struct", + ps: &struct { + Other int + Foo []struct { + Other int + Bar []struct { + Other int + Baz string `name:"value"` + } + } + }{}, + want: [][]int{{1, 1, 1}}, + }, + { + name: "nil", + ps: (*struct { + Foo string `name:"value"` + })(nil), + want: [][]int{{0}}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := PropertyIndexesWithTag(test.ps, "name", "value"); !reflect.DeepEqual(got, test.want) { + t.Errorf("PropertyIndexesWithTag() = %v, want %v", got, test.want) + } + }) + } +} diff --git a/blueprint/proptools/typeequal.go b/blueprint/proptools/typeequal.go new file mode 100644 index 0000000..d9b3c18 --- /dev/null +++ b/blueprint/proptools/typeequal.go @@ -0,0 +1,109 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import "reflect" + +// TypeEqual takes two property structs, and returns true if they are of equal type, any embedded +// pointers to structs or interfaces having matching nilitude, and any interface{} values in any +// embedded structs, pointers to structs, or interfaces are also of equal type. +func TypeEqual(s1, s2 interface{}) bool { + return typeEqual(reflect.ValueOf(s1), reflect.ValueOf(s2)) +} + +func typeEqual(v1, v2 reflect.Value) bool { + if v1.Type() != v2.Type() { + return false + } + + if v1.Kind() == reflect.Interface { + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.IsNil() { + return true + } + v1 = v1.Elem() + v2 = v2.Elem() + if v1.Type() != v2.Type() { + return false + } + } + + if v1.Kind() == reflect.Ptr { + if v1.Type().Elem().Kind() != reflect.Struct { + return true + } + if v1.IsNil() && !v2.IsNil() { + return concreteType(v2) + } else if v2.IsNil() && !v1.IsNil() { + return concreteType(v1) + } else if v1.IsNil() && v2.IsNil() { + return true + } + + v1 = v1.Elem() + v2 = v2.Elem() + } + + if v1.Kind() != reflect.Struct { + return true + } + + for i := 0; i < v1.NumField(); i++ { + v1 := v1.Field(i) + v2 := v2.Field(i) + + switch kind := v1.Kind(); kind { + case reflect.Interface, reflect.Ptr, reflect.Struct: + if !typeEqual(v1, v2) { + return false + } + } + } + + return true +} + +// Returns true if v recursively contains no interfaces +func concreteType(v reflect.Value) bool { + if v.Kind() == reflect.Interface { + return false + } + + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return true + } + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return true + } + + for i := 0; i < v.NumField(); i++ { + v := v.Field(i) + + switch kind := v.Kind(); kind { + case reflect.Interface, reflect.Ptr, reflect.Struct: + if !concreteType(v) { + return false + } + } + } + + return true +} diff --git a/blueprint/proptools/typeequal_test.go b/blueprint/proptools/typeequal_test.go new file mode 100644 index 0000000..cd86cd3 --- /dev/null +++ b/blueprint/proptools/typeequal_test.go @@ -0,0 +1,150 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "testing" +) + +var typeEqualTestCases = []struct { + in1 interface{} + in2 interface{} + out bool +}{ + { + // Matching structs + in1: struct{ S1 string }{}, + in2: struct{ S1 string }{}, + out: true, + }, + { + // Mismatching structs + in1: struct{ S1 string }{}, + in2: struct{ S2 string }{}, + out: false, + }, + { + // Matching pointer to struct + in1: &struct{ S1 string }{}, + in2: &struct{ S1 string }{}, + out: true, + }, + { + // Mismatching pointer to struct + in1: &struct{ S1 string }{}, + in2: &struct{ S2 string }{}, + out: false, + }, + { + // Matching embedded structs + in1: struct{ S struct{ S1 string } }{}, + in2: struct{ S struct{ S1 string } }{}, + out: true, + }, + { + // Misatching embedded structs + in1: struct{ S struct{ S1 string } }{}, + in2: struct{ S struct{ S2 string } }{}, + out: false, + }, + { + // Matching embedded pointer to struct + in1: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}}, + in2: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}}, + out: true, + }, + { + // Mismatching embedded pointer to struct + in1: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}}, + in2: &struct{ S *struct{ S2 string } }{S: &struct{ S2 string }{}}, + out: false, + }, + { + // Matching embedded nil pointer to struct + in1: &struct{ S *struct{ S1 string } }{}, + in2: &struct{ S *struct{ S1 string } }{}, + out: true, + }, + { + // Mismatching embedded nil pointer to struct + in1: &struct{ S *struct{ S1 string } }{}, + in2: &struct{ S *struct{ S2 string } }{}, + out: false, + }, + { + // Mismatching nilitude embedded pointer to struct + in1: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}}, + in2: &struct{ S *struct{ S1 string } }{}, + out: true, + }, + { + // Matching embedded interface to pointer to struct + in1: &struct{ S interface{} }{S: &struct{ S1 string }{}}, + in2: &struct{ S interface{} }{S: &struct{ S1 string }{}}, + out: true, + }, + { + // Mismatching embedded interface to pointer to struct + in1: &struct{ S interface{} }{S: &struct{ S1 string }{}}, + in2: &struct{ S interface{} }{S: &struct{ S2 string }{}}, + out: false, + }, + { + // Matching embedded nil interface to pointer to struct + in1: &struct{ S interface{} }{}, + in2: &struct{ S interface{} }{}, + out: true, + }, + { + // Mismatching nilitude embedded interface to pointer to struct + in1: &struct{ S interface{} }{S: &struct{ S1 string }{}}, + in2: &struct{ S interface{} }{}, + out: false, + }, + { + // Matching pointer to non-struct + in1: struct{ S1 *string }{S1: StringPtr("test1")}, + in2: struct{ S1 *string }{S1: StringPtr("test2")}, + out: true, + }, + { + // Matching nilitude pointer to non-struct + in1: struct{ S1 *string }{S1: StringPtr("test1")}, + in2: struct{ S1 *string }{}, + out: true, + }, + { + // Mismatching pointer to non-struct + in1: struct{ S1 *string }{}, + in2: struct{ S2 *string }{}, + out: false, + }, +} + +func TestTypeEqualProperties(t *testing.T) { + for _, testCase := range typeEqualTestCases { + testString := fmt.Sprintf("%#v, %#v -> %t", testCase.in1, testCase.in2, testCase.out) + + got := TypeEqual(testCase.in1, testCase.in2) + + if got != testCase.out { + t.Errorf("test case: %s", testString) + t.Errorf("incorrect output") + t.Errorf(" expected: %t", testCase.out) + t.Errorf(" got: %t", got) + } + } +} diff --git a/blueprint/proptools/unpack.go b/blueprint/proptools/unpack.go new file mode 100644 index 0000000..28a68b5 --- /dev/null +++ b/blueprint/proptools/unpack.go @@ -0,0 +1,579 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" + "sort" + "strconv" + "strings" + "text/scanner" + + "github.com/google/blueprint/parser" +) + +const maxUnpackErrors = 10 + +var ( + // Hard-coded list of allowlisted property names of type map. This is to limit use of maps to + // where absolutely necessary. + validMapProperties = []string{} +) + +type UnpackError struct { + Err error + Pos scanner.Position +} + +func (e *UnpackError) Error() string { + return fmt.Sprintf("%s: %s", e.Pos, e.Err) +} + +// packedProperty helps to track properties usage (`used` will be true) +type packedProperty struct { + property *parser.Property + used bool +} + +// unpackContext keeps compound names and their values in a map. It is initialized from +// parsed properties. +type unpackContext struct { + propertyMap map[string]*packedProperty + validMapProperties map[string]bool + errs []error +} + +// UnpackProperties populates the list of runtime values ("property structs") from the parsed properties. +// If a property a.b.c has a value, a field with the matching name in each runtime value is initialized +// from it. See PropertyNameForField for field and property name matching. +// For instance, if the input contains +// { foo: "abc", bar: {x: 1},} +// and a runtime value being has been declared as +// var v struct { Foo string; Bar int } +// then v.Foo will be set to "abc" and v.Bar will be set to 1 +// (cf. unpack_test.go for further examples) +// +// The type of a receiving field has to match the property type, i.e., a bool/int/string field +// can be set from a property with bool/int/string value, a struct can be set from a map (only the +// matching fields are set), and an slice can be set from a list. +// If a field of a runtime value has been already set prior to the UnpackProperties, the new value +// is appended to it (see somewhat inappropriately named ExtendBasicType). +// The same property can initialize fields in multiple runtime values. It is an error if any property +// value was not used to initialize at least one field. +func UnpackProperties(properties []*parser.Property, objects ...interface{}) (map[string]*parser.Property, []error) { + return unpackProperties(properties, validMapProperties, objects...) +} + +func unpackProperties(properties []*parser.Property, validMapProps []string, objects ...interface{}) (map[string]*parser.Property, []error) { + var unpackContext unpackContext + unpackContext.propertyMap = make(map[string]*packedProperty) + if !unpackContext.buildPropertyMap("", properties) { + return nil, unpackContext.errs + } + unpackContext.validMapProperties = make(map[string]bool, len(validMapProps)) + for _, p := range validMapProps { + unpackContext.validMapProperties[p] = true + } + + for _, obj := range objects { + valueObject := reflect.ValueOf(obj) + if !isStructPtr(valueObject.Type()) { + panic(fmt.Errorf("properties must be *struct, got %s", + valueObject.Type())) + } + unpackContext.unpackToStruct("", valueObject.Elem()) + if len(unpackContext.errs) >= maxUnpackErrors { + return nil, unpackContext.errs + } + } + + // Gather property map, and collect any unused properties. + // Avoid reporting subproperties of unused properties. + result := make(map[string]*parser.Property) + var unusedNames []string + for name, v := range unpackContext.propertyMap { + if v.used { + result[name] = v.property + } else { + unusedNames = append(unusedNames, name) + } + } + if len(unusedNames) == 0 && len(unpackContext.errs) == 0 { + return result, nil + } + return nil, unpackContext.reportUnusedNames(unusedNames) +} + +func (ctx *unpackContext) reportUnusedNames(unusedNames []string) []error { + sort.Strings(unusedNames) + var lastReported string + for _, name := range unusedNames { + // if 'foo' has been reported, ignore 'foo\..*' and 'foo\[.*' + if lastReported != "" { + trimmed := strings.TrimPrefix(name, lastReported) + if trimmed != name && (trimmed[0] == '.' || trimmed[0] == '[') { + continue + } + } + ctx.errs = append(ctx.errs, &UnpackError{ + fmt.Errorf("unrecognized property %q", name), + ctx.propertyMap[name].property.ColonPos}) + lastReported = name + } + return ctx.errs +} + +func (ctx *unpackContext) buildPropertyMap(prefix string, properties []*parser.Property) bool { + nOldErrors := len(ctx.errs) + for _, property := range properties { + name := fieldPath(prefix, property.Name) + if first, present := ctx.propertyMap[name]; present { + ctx.addError( + &UnpackError{fmt.Errorf("property %q already defined", name), property.ColonPos}) + if ctx.addError( + &UnpackError{fmt.Errorf("<-- previous definition here"), first.property.ColonPos}) { + return false + } + continue + } + + ctx.propertyMap[name] = &packedProperty{property, false} + switch propValue := property.Value.Eval().(type) { + case *parser.Map: + // If this is a map and the values are not primitive types, we need to unroll it for further + // mapping. Keys are limited to string types. + ctx.buildPropertyMap(name, propValue.Properties) + if len(propValue.MapItems) == 0 { + continue + } + items := propValue.MapItems + keysType := items[0].Key.Type() + valsAreBasic := primitiveType(items[0].Value.Type()) + if keysType != parser.StringType { + ctx.addError(&UnpackError{Err: fmt.Errorf("complex key types are unsupported: %s", keysType)}) + return false + } else if valsAreBasic { + continue + } + itemProperties := make([]*parser.Property, len(items), len(items)) + for i, item := range items { + itemProperties[i] = &parser.Property{ + Name: fmt.Sprintf("%s{value:%d}", property.Name, i), + NamePos: property.NamePos, + ColonPos: property.ColonPos, + Value: item.Value, + } + } + if !ctx.buildPropertyMap(prefix, itemProperties) { + return false + } + case *parser.List: + // If it is a list, unroll it unless its elements are of primitive type + // (no further mapping will be needed in that case, so we avoid cluttering + // the map). + if len(propValue.Values) == 0 { + continue + } + if primitiveType(propValue.Values[0].Type()) { + continue + } + + itemProperties := make([]*parser.Property, len(propValue.Values), len(propValue.Values)) + for i, expr := range propValue.Values { + itemProperties[i] = &parser.Property{ + Name: property.Name + "[" + strconv.Itoa(i) + "]", + NamePos: property.NamePos, + ColonPos: property.ColonPos, + Value: expr, + } + } + if !ctx.buildPropertyMap(prefix, itemProperties) { + return false + } + } + } + + return len(ctx.errs) == nOldErrors +} + +// primitiveType returns whether typ is a primitive type +func primitiveType(typ parser.Type) bool { + return typ == parser.StringType || typ == parser.Int64Type || typ == parser.BoolType +} + +func fieldPath(prefix, fieldName string) string { + if prefix == "" { + return fieldName + } + return prefix + "." + fieldName +} + +func (ctx *unpackContext) addError(e error) bool { + ctx.errs = append(ctx.errs, e) + return len(ctx.errs) < maxUnpackErrors +} + +func (ctx *unpackContext) unpackToStruct(namePrefix string, structValue reflect.Value) { + structType := structValue.Type() + + for i := 0; i < structValue.NumField(); i++ { + fieldValue := structValue.Field(i) + field := structType.Field(i) + + // In Go 1.7, runtime-created structs are unexported, so it's not + // possible to create an exported anonymous field with a generated + // type. So workaround this by special-casing "BlueprintEmbed" to + // behave like an anonymous field for structure unpacking. + if field.Name == "BlueprintEmbed" { + field.Name = "" + field.Anonymous = true + } + + if field.PkgPath != "" { + // This is an unexported field, so just skip it. + continue + } + + propertyName := fieldPath(namePrefix, PropertyNameForField(field.Name)) + + if !fieldValue.CanSet() { + panic(fmt.Errorf("field %s is not settable", propertyName)) + } + + // Get the property value if it was specified. + packedProperty, propertyIsSet := ctx.propertyMap[propertyName] + + origFieldValue := fieldValue + + // To make testing easier we validate the struct field's type regardless + // of whether or not the property was specified in the parsed string. + // TODO(ccross): we don't validate types inside nil struct pointers + // Move type validation to a function that runs on each factory once + switch kind := fieldValue.Kind(); kind { + case reflect.Bool, reflect.String, reflect.Struct, reflect.Slice: + // Do nothing + case reflect.Map: + // Restrict names of map properties that _can_ be set in bp files + if _, ok := ctx.validMapProperties[propertyName]; !ok { + if !HasTag(field, "blueprint", "mutated") { + ctx.addError(&UnpackError{ + Err: fmt.Errorf("Uses of maps for properties must be allowlisted. %q is an unsupported use case", propertyName), + }) + } + } + case reflect.Interface: + if fieldValue.IsNil() { + panic(fmt.Errorf("field %s contains a nil interface", propertyName)) + } + fieldValue = fieldValue.Elem() + elemType := fieldValue.Type() + if elemType.Kind() != reflect.Ptr { + panic(fmt.Errorf("field %s contains a non-pointer interface", propertyName)) + } + fallthrough + case reflect.Ptr: + switch ptrKind := fieldValue.Type().Elem().Kind(); ptrKind { + case reflect.Struct: + if fieldValue.IsNil() && (propertyIsSet || field.Anonymous) { + // Instantiate nil struct pointers + // Set into origFieldValue in case it was an interface, in which case + // fieldValue points to the unsettable pointer inside the interface + fieldValue = reflect.New(fieldValue.Type().Elem()) + origFieldValue.Set(fieldValue) + } + fieldValue = fieldValue.Elem() + case reflect.Bool, reflect.Int64, reflect.String: + // Nothing + default: + panic(fmt.Errorf("field %s contains a pointer to %s", propertyName, ptrKind)) + } + + case reflect.Int, reflect.Uint: + if !HasTag(field, "blueprint", "mutated") { + panic(fmt.Errorf(`int field %s must be tagged blueprint:"mutated"`, propertyName)) + } + + default: + panic(fmt.Errorf("unsupported kind for field %s: %s", propertyName, kind)) + } + + if field.Anonymous && isStruct(fieldValue.Type()) { + ctx.unpackToStruct(namePrefix, fieldValue) + continue + } + + if !propertyIsSet { + // This property wasn't specified. + continue + } + + packedProperty.used = true + property := packedProperty.property + + if HasTag(field, "blueprint", "mutated") { + if !ctx.addError( + &UnpackError{ + fmt.Errorf("mutated field %s cannot be set in a Blueprint file", propertyName), + property.ColonPos, + }) { + return + } + continue + } + + if isStruct(fieldValue.Type()) { + if property.Value.Eval().Type() != parser.MapType { + ctx.addError(&UnpackError{ + fmt.Errorf("can't assign %s value to map property %q", + property.Value.Type(), property.Name), + property.Value.Pos(), + }) + continue + } + ctx.unpackToStruct(propertyName, fieldValue) + if len(ctx.errs) >= maxUnpackErrors { + return + } + } else if isSlice(fieldValue.Type()) { + if unpackedValue, ok := ctx.unpackToSlice(propertyName, property, fieldValue.Type()); ok { + ExtendBasicType(fieldValue, unpackedValue, Append) + } + if len(ctx.errs) >= maxUnpackErrors { + return + } + } else if fieldValue.Type().Kind() == reflect.Map { + if unpackedValue, ok := ctx.unpackToMap(propertyName, property, fieldValue.Type()); ok { + ExtendBasicType(fieldValue, unpackedValue, Append) + } + if len(ctx.errs) >= maxUnpackErrors { + return + } + + } else { + unpackedValue, err := propertyToValue(fieldValue.Type(), property) + if err != nil && !ctx.addError(err) { + return + } + ExtendBasicType(fieldValue, unpackedValue, Append) + } + } +} + +// unpackToMap unpacks given parser.property into a go map of type mapType +func (ctx *unpackContext) unpackToMap(mapName string, property *parser.Property, mapType reflect.Type) (reflect.Value, bool) { + propValueAsMap, ok := property.Value.Eval().(*parser.Map) + // Verify this property is a map + if !ok { + ctx.addError(&UnpackError{ + fmt.Errorf("can't assign %q value to map property %q", property.Value.Type(), property.Name), + property.Value.Pos(), + }) + return reflect.MakeMap(mapType), false + } + // And is not a struct + if len(propValueAsMap.Properties) > 0 { + ctx.addError(&UnpackError{ + fmt.Errorf("can't assign property to a map (%s) property %q", property.Value.Type(), property.Name), + property.Value.Pos(), + }) + return reflect.MakeMap(mapType), false + } + + items := propValueAsMap.MapItems + m := reflect.MakeMap(mapType) + if len(items) == 0 { + return m, true + } + keyConstructor := ctx.itemConstructor(items[0].Key.Type()) + keyType := mapType.Key() + valueConstructor := ctx.itemConstructor(items[0].Value.Type()) + valueType := mapType.Elem() + + itemProperty := &parser.Property{NamePos: property.NamePos, ColonPos: property.ColonPos} + for i, item := range items { + itemProperty.Name = fmt.Sprintf("%s{key:%d}", mapName, i) + itemProperty.Value = item.Key + if packedProperty, ok := ctx.propertyMap[itemProperty.Name]; ok { + packedProperty.used = true + } + keyValue, ok := itemValue(keyConstructor, itemProperty, keyType) + if !ok { + continue + } + itemProperty.Name = fmt.Sprintf("%s{value:%d}", mapName, i) + itemProperty.Value = item.Value + if packedProperty, ok := ctx.propertyMap[itemProperty.Name]; ok { + packedProperty.used = true + } + value, ok := itemValue(valueConstructor, itemProperty, valueType) + if ok { + m.SetMapIndex(keyValue, value) + } + } + + return m, true +} + +// unpackSlice creates a value of a given slice type from the property which should be a list +func (ctx *unpackContext) unpackToSlice( + sliceName string, property *parser.Property, sliceType reflect.Type) (reflect.Value, bool) { + propValueAsList, ok := property.Value.Eval().(*parser.List) + if !ok { + ctx.addError(&UnpackError{ + fmt.Errorf("can't assign %s value to list property %q", + property.Value.Type(), property.Name), + property.Value.Pos(), + }) + return reflect.MakeSlice(sliceType, 0, 0), false + } + exprs := propValueAsList.Values + value := reflect.MakeSlice(sliceType, 0, len(exprs)) + if len(exprs) == 0 { + return value, true + } + + itemConstructor := ctx.itemConstructor(exprs[0].Type()) + itemType := sliceType.Elem() + + itemProperty := &parser.Property{NamePos: property.NamePos, ColonPos: property.ColonPos} + for i, expr := range exprs { + itemProperty.Name = sliceName + "[" + strconv.Itoa(i) + "]" + itemProperty.Value = expr + if packedProperty, ok := ctx.propertyMap[itemProperty.Name]; ok { + packedProperty.used = true + } + if itemValue, ok := itemValue(itemConstructor, itemProperty, itemType); ok { + value = reflect.Append(value, itemValue) + } + } + return value, true +} + +// constructItem is a function to construct a reflect.Value from given parser.Property of reflect.Type +type constructItem func(*parser.Property, reflect.Type) (reflect.Value, bool) + +// itemValue creates a new item of type t with value determined by f +func itemValue(f constructItem, property *parser.Property, t reflect.Type) (reflect.Value, bool) { + isPtr := t.Kind() == reflect.Ptr + if isPtr { + t = t.Elem() + } + val, ok := f(property, t) + if !ok { + return val, ok + } + if isPtr { + ptrValue := reflect.New(val.Type()) + ptrValue.Elem().Set(val) + return ptrValue, true + } + return val, true +} + +// itemConstructor returns a function to construct an item of typ +func (ctx *unpackContext) itemConstructor(typ parser.Type) constructItem { + // The function to construct an item value depends on the type of list elements. + switch typ { + case parser.BoolType, parser.StringType, parser.Int64Type: + return func(property *parser.Property, t reflect.Type) (reflect.Value, bool) { + value, err := propertyToValue(t, property) + if err != nil { + ctx.addError(err) + return value, false + } + return value, true + } + case parser.ListType: + return func(property *parser.Property, t reflect.Type) (reflect.Value, bool) { + return ctx.unpackToSlice(property.Name, property, t) + } + case parser.MapType: + return func(property *parser.Property, t reflect.Type) (reflect.Value, bool) { + if t.Kind() == reflect.Map { + return ctx.unpackToMap(property.Name, property, t) + } else { + itemValue := reflect.New(t).Elem() + ctx.unpackToStruct(property.Name, itemValue) + return itemValue, true + } + } + case parser.NotEvaluatedType: + return func(property *parser.Property, t reflect.Type) (reflect.Value, bool) { + return reflect.New(t), false + } + default: + panic(fmt.Errorf("bizarre property expression type: %v", typ)) + } +} + +// propertyToValue creates a value of a given value type from the property. +func propertyToValue(typ reflect.Type, property *parser.Property) (reflect.Value, error) { + var value reflect.Value + var baseType reflect.Type + isPtr := typ.Kind() == reflect.Ptr + if isPtr { + baseType = typ.Elem() + } else { + baseType = typ + } + + switch kind := baseType.Kind(); kind { + case reflect.Bool: + b, ok := property.Value.Eval().(*parser.Bool) + if !ok { + return value, &UnpackError{ + fmt.Errorf("can't assign %s value to bool property %q", + property.Value.Type(), property.Name), + property.Value.Pos(), + } + } + value = reflect.ValueOf(b.Value) + + case reflect.Int64: + b, ok := property.Value.Eval().(*parser.Int64) + if !ok { + return value, &UnpackError{ + fmt.Errorf("can't assign %s value to int64 property %q", + property.Value.Type(), property.Name), + property.Value.Pos(), + } + } + value = reflect.ValueOf(b.Value) + + case reflect.String: + s, ok := property.Value.Eval().(*parser.String) + if !ok { + return value, &UnpackError{ + fmt.Errorf("can't assign %s value to string property %q", + property.Value.Type(), property.Name), + property.Value.Pos(), + } + } + value = reflect.ValueOf(s.Value) + + default: + return value, &UnpackError{ + fmt.Errorf("cannot assign %s value %s to %s property %s", property.Value.Type(), property.Value, kind, typ), + property.NamePos} + } + + if isPtr { + ptrValue := reflect.New(value.Type()) + ptrValue.Elem().Set(value) + return ptrValue, nil + } + return value, nil +} diff --git a/blueprint/proptools/unpack_test.go b/blueprint/proptools/unpack_test.go new file mode 100644 index 0000000..5c6e3d0 --- /dev/null +++ b/blueprint/proptools/unpack_test.go @@ -0,0 +1,1305 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "bytes" + "reflect" + + "testing" + + "github.com/google/blueprint/parser" +) + +var validUnpackTestCases = []struct { + name string + input string + output []interface{} + empty []interface{} + errs []error +}{ + { + name: "blank and unset", + input: ` + m { + s: "abc", + blank: "", + } + `, + output: []interface{}{ + &struct { + S *string + Blank *string + Unset *string + }{ + S: StringPtr("abc"), + Blank: StringPtr(""), + Unset: nil, + }, + }, + }, + + { + name: "string", + input: ` + m { + s: "abc", + } + `, + output: []interface{}{ + &struct { + S string + }{ + S: "abc", + }, + }, + }, + + { + name: "bool", + input: ` + m { + isGood: true, + } + `, + output: []interface{}{ + &struct { + IsGood bool + }{ + IsGood: true, + }, + }, + }, + + { + name: "boolptr", + input: ` + m { + isGood: true, + isBad: false, + } + `, + output: []interface{}{ + &struct { + IsGood *bool + IsBad *bool + IsUgly *bool + }{ + IsGood: BoolPtr(true), + IsBad: BoolPtr(false), + IsUgly: nil, + }, + }, + }, + + { + name: "slice", + input: ` + m { + stuff: ["asdf", "jkl;", "qwert", + "uiop", "bnm,"], + empty: [] + } + `, + output: []interface{}{ + &struct { + Stuff []string + Empty []string + Nil []string + NonString []struct{ S string } `blueprint:"mutated"` + }{ + Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"}, + Empty: []string{}, + Nil: nil, + NonString: nil, + }, + }, + }, + + { + name: "map", + input: ` + m { + stuff: { "asdf": "jkl;", "qwert": "uiop"}, + empty: {}, + nested: { + other_stuff: {}, + }, + } + `, + output: []interface{}{ + &struct { + Stuff map[string]string + Empty map[string]string + Nil map[string]string + NonString map[string]struct{ S string } `blueprint:"mutated"` + Nested struct { + Other_stuff map[string]string + } + }{ + Stuff: map[string]string{"asdf": "jkl;", "qwert": "uiop"}, + Empty: map[string]string{}, + Nil: nil, + NonString: nil, + Nested: struct{ Other_stuff map[string]string }{ + Other_stuff: map[string]string{}, + }, + }, + }, + }, + + { + name: "map with slice", + input: ` + m { + stuff: { "asdf": ["jkl;"], "qwert": []}, + empty: {}, + } + `, + output: []interface{}{ + &struct { + Stuff map[string][]string + Empty map[string][]string + Nil map[string][]string + NonString map[string]struct{ S string } `blueprint:"mutated"` + }{ + Stuff: map[string][]string{"asdf": []string{"jkl;"}, "qwert": []string{}}, + Empty: map[string][]string{}, + Nil: nil, + NonString: nil, + }, + }, + }, + + { + name: "map with struct", + input: ` + m { + stuff: { "asdf": {s:"a"}}, + empty: {}, + } + `, + output: []interface{}{ + &struct { + Stuff map[string]struct{ S string } + Empty map[string]struct{ S string } + Nil map[string]struct{ S string } + }{ + Stuff: map[string]struct{ S string }{"asdf": struct{ S string }{"a"}}, + Empty: map[string]struct{ S string }{}, + Nil: nil, + }, + }, + }, + + { + name: "double nested", + input: ` + m { + nested: { + nested: { + s: "abc", + }, + }, + } + `, + output: []interface{}{ + &struct { + Nested struct { + Nested struct { + S string + } + } + }{ + Nested: struct{ Nested struct{ S string } }{ + Nested: struct{ S string }{ + S: "abc", + }, + }, + }, + }, + }, + + { + name: "nested", + input: ` + m { + nested: { + s: "abc", + } + } + `, + output: []interface{}{ + &struct { + Nested struct { + S string + } + }{ + Nested: struct{ S string }{ + S: "abc", + }, + }, + }, + }, + + { + name: "nested interface", + input: ` + m { + nested: { + s: "def", + } + } + `, + output: []interface{}{ + &struct { + Nested interface{} + }{ + Nested: &struct{ S string }{ + S: "def", + }, + }, + }, + }, + + { + name: "mixed", + input: ` + m { + nested: { + foo: "abc", + }, + bar: false, + baz: ["def", "ghi"], + } + `, + output: []interface{}{ + &struct { + Nested struct { + Foo string + } + Bar bool + Baz []string + }{ + Nested: struct{ Foo string }{ + Foo: "abc", + }, + Bar: false, + Baz: []string{"def", "ghi"}, + }, + }, + }, + + { + name: "filter", + input: ` + m { + nested: { + foo: "abc", + }, + bar: false, + baz: ["def", "ghi"], + } + `, + output: []interface{}{ + &struct { + Nested struct { + Foo string `allowNested:"true"` + } `blueprint:"filter(allowNested:\"true\")"` + Bar bool + Baz []string + }{ + Nested: struct { + Foo string `allowNested:"true"` + }{ + Foo: "abc", + }, + Bar: false, + Baz: []string{"def", "ghi"}, + }, + }, + }, + + // List of maps + { + name: "list of structs", + input: ` + m { + mapslist: [ + { + foo: "abc", + bar: true, + }, + { + foo: "def", + bar: false, + } + ], + } + `, + output: []interface{}{ + &struct { + Mapslist []struct { + Foo string + Bar bool + } + }{ + Mapslist: []struct { + Foo string + Bar bool + }{ + {Foo: "abc", Bar: true}, + {Foo: "def", Bar: false}, + }, + }, + }, + }, + + // List of pointers to structs + { + name: "list of pointers to structs", + input: ` + m { + mapslist: [ + { + foo: "abc", + bar: true, + }, + { + foo: "def", + bar: false, + } + ], + } + `, + output: []interface{}{ + &struct { + Mapslist []*struct { + Foo string + Bar bool + } + }{ + Mapslist: []*struct { + Foo string + Bar bool + }{ + {Foo: "abc", Bar: true}, + {Foo: "def", Bar: false}, + }, + }, + }, + }, + + // List of lists + { + name: "list of lists", + input: ` + m { + listoflists: [ + ["abc",], + ["def",], + ], + } + `, + output: []interface{}{ + &struct { + Listoflists [][]string + }{ + Listoflists: [][]string{ + []string{"abc"}, + []string{"def"}, + }, + }, + }, + }, + + // Multilevel + { + name: "multilevel", + input: ` + m { + name: "mymodule", + flag: true, + settings: ["foo1", "foo2", "foo3",], + perarch: { + arm: "32", + arm64: "64", + }, + configvars: [ + { var: "var1", values: ["1.1", "1.2", ], }, + { var: "var2", values: ["2.1", ], }, + ], + } + `, + output: []interface{}{ + &struct { + Name string + Flag bool + Settings []string + Perarch *struct { + Arm string + Arm64 string + } + Configvars []struct { + Var string + Values []string + } + }{ + Name: "mymodule", + Flag: true, + Settings: []string{"foo1", "foo2", "foo3"}, + Perarch: &struct { + Arm string + Arm64 string + }{Arm: "32", Arm64: "64"}, + Configvars: []struct { + Var string + Values []string + }{ + {Var: "var1", Values: []string{"1.1", "1.2"}}, + {Var: "var2", Values: []string{"2.1"}}, + }, + }, + }, + }, + // Anonymous struct + { + name: "embedded struct", + input: ` + m { + s: "abc", + nested: { + s: "def", + }, + } + `, + output: []interface{}{ + &struct { + EmbeddedStruct + Nested struct { + EmbeddedStruct + } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "abc", + }, + Nested: struct { + EmbeddedStruct + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "def", + }, + }, + }, + }, + }, + + // Anonymous interface + { + name: "embedded interface", + input: ` + m { + s: "abc", + nested: { + s: "def", + }, + } + `, + output: []interface{}{ + &struct { + EmbeddedInterface + Nested struct { + EmbeddedInterface + } + }{ + EmbeddedInterface: &struct{ S string }{ + S: "abc", + }, + Nested: struct { + EmbeddedInterface + }{ + EmbeddedInterface: &struct{ S string }{ + S: "def", + }, + }, + }, + }, + }, + + // Anonymous struct with name collision + { + name: "embedded name collision", + input: ` + m { + s: "abc", + nested: { + s: "def", + }, + } + `, + output: []interface{}{ + &struct { + S string + EmbeddedStruct + Nested struct { + S string + EmbeddedStruct + } + }{ + S: "abc", + EmbeddedStruct: EmbeddedStruct{ + S: "abc", + }, + Nested: struct { + S string + EmbeddedStruct + }{ + S: "def", + EmbeddedStruct: EmbeddedStruct{ + S: "def", + }, + }, + }, + }, + }, + + // Anonymous interface with name collision + { + name: "embeded interface name collision", + input: ` + m { + s: "abc", + nested: { + s: "def", + }, + } + `, + output: []interface{}{ + &struct { + S string + EmbeddedInterface + Nested struct { + S string + EmbeddedInterface + } + }{ + S: "abc", + EmbeddedInterface: &struct{ S string }{ + S: "abc", + }, + Nested: struct { + S string + EmbeddedInterface + }{ + S: "def", + EmbeddedInterface: &struct{ S string }{ + S: "def", + }, + }, + }, + }, + }, + + // Variables + { + name: "variables", + input: ` + list = ["abc"] + string = "def" + list_with_variable = [string] + struct_value = { name: "foo" } + m { + s: string, + list: list, + list2: list_with_variable, + structattr: struct_value, + } + `, + output: []interface{}{ + &struct { + S string + List []string + List2 []string + Structattr struct { + Name string + } + }{ + S: "def", + List: []string{"abc"}, + List2: []string{"def"}, + Structattr: struct { + Name string + }{ + Name: "foo", + }, + }, + }, + }, + + // Multiple property structs + { + name: "multiple", + input: ` + m { + nested: { + s: "abc", + } + } + `, + output: []interface{}{ + &struct { + Nested struct { + S string + } + }{ + Nested: struct{ S string }{ + S: "abc", + }, + }, + &struct { + Nested struct { + S string + } + }{ + Nested: struct{ S string }{ + S: "abc", + }, + }, + &struct { + }{}, + }, + }, + + // Nil pointer to struct + { + name: "nil struct pointer", + input: ` + m { + nested: { + s: "abc", + } + } + `, + output: []interface{}{ + &struct { + Nested *struct { + S string + } + }{ + Nested: &struct{ S string }{ + S: "abc", + }, + }, + }, + empty: []interface{}{ + &struct { + Nested *struct { + S string + } + }{}, + }, + }, + + // Interface containing nil pointer to struct + { + name: "interface nil struct pointer", + input: ` + m { + nested: { + s: "abc", + } + } + `, + output: []interface{}{ + &struct { + Nested interface{} + }{ + Nested: &EmbeddedStruct{ + S: "abc", + }, + }, + }, + empty: []interface{}{ + &struct { + Nested interface{} + }{ + Nested: (*EmbeddedStruct)(nil), + }, + }, + }, + + // Factory set properties + { + name: "factory properties", + input: ` + m { + string: "abc", + string_ptr: "abc", + bool: false, + bool_ptr: false, + list: ["a", "b", "c"], + } + `, + output: []interface{}{ + &struct { + String string + String_ptr *string + Bool bool + Bool_ptr *bool + List []string + }{ + String: "012abc", + String_ptr: StringPtr("abc"), + Bool: true, + Bool_ptr: BoolPtr(false), + List: []string{"0", "1", "2", "a", "b", "c"}, + }, + }, + empty: []interface{}{ + &struct { + String string + String_ptr *string + Bool bool + Bool_ptr *bool + List []string + }{ + String: "012", + String_ptr: StringPtr("012"), + Bool: true, + Bool_ptr: BoolPtr(true), + List: []string{"0", "1", "2"}, + }, + }, + }, + // Captitalized property + { + input: ` + m { + CAPITALIZED: "foo", + } + `, + output: []interface{}{ + &struct { + CAPITALIZED string + }{ + CAPITALIZED: "foo", + }, + }, + }, +} + +func TestUnpackProperties(t *testing.T) { + for _, testCase := range validUnpackTestCases { + t.Run(testCase.name, func(t *testing.T) { + r := bytes.NewBufferString(testCase.input) + file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) + if len(errs) != 0 { + t.Errorf("test case: %s", testCase.input) + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + for _, def := range file.Defs { + module, ok := def.(*parser.Module) + if !ok { + continue + } + + var output []interface{} + if len(testCase.empty) > 0 { + for _, p := range testCase.empty { + output = append(output, CloneProperties(reflect.ValueOf(p)).Interface()) + } + } else { + for _, p := range testCase.output { + output = append(output, CloneEmptyProperties(reflect.ValueOf(p)).Interface()) + } + } + + _, errs = unpackProperties(module.Properties, []string{"stuff", "empty", "nil", "nested.other_stuff"}, output...) + if len(errs) != 0 && len(testCase.errs) == 0 { + t.Errorf("test case: %s", testCase.input) + t.Errorf("unexpected unpack errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } else if !reflect.DeepEqual(errs, testCase.errs) { + t.Errorf("test case: %s", testCase.input) + t.Errorf("incorrect errors:") + t.Errorf(" expected: %+v", testCase.errs) + t.Errorf(" got: %+v", errs) + } + + if len(output) != len(testCase.output) { + t.Fatalf("incorrect number of property structs, expected %d got %d", + len(testCase.output), len(output)) + } + + for i := range output { + got := reflect.ValueOf(output[i]).Interface() + if !reflect.DeepEqual(got, testCase.output[i]) { + t.Errorf("test case: %s", testCase.input) + t.Errorf("incorrect output:") + t.Errorf(" expected: %+v", testCase.output[i]) + t.Errorf(" got: %+v", got) + } + } + } + }) + } +} + +func TestUnpackErrors(t *testing.T) { + testCases := []struct { + name string + input string + output []interface{} + errors []string + }{ + { + name: "missing", + input: ` + m { + missing: true, + } + `, + output: []interface{}{}, + errors: []string{`:3:13: unrecognized property "missing"`}, + }, + { + name: "missing nested", + input: ` + m { + nested: { + missing: true, + }, + } + `, + output: []interface{}{ + &struct { + Nested struct{} + }{}, + }, + errors: []string{`:4:14: unrecognized property "nested.missing"`}, + }, + { + name: "mutated", + input: ` + m { + mutated: true, + } + `, + output: []interface{}{ + &struct { + Mutated bool `blueprint:"mutated"` + }{}, + }, + errors: []string{`:3:13: mutated field mutated cannot be set in a Blueprint file`}, + }, + { + name: "nested mutated", + input: ` + m { + nested: { + mutated: true, + }, + } + `, + output: []interface{}{ + &struct { + Nested struct { + Mutated bool `blueprint:"mutated"` + } + }{}, + }, + errors: []string{`:4:14: mutated field nested.mutated cannot be set in a Blueprint file`}, + }, + { + name: "duplicate", + input: ` + m { + exists: true, + exists: true, + } + `, + output: []interface{}{ + &struct { + Exists bool + }{}, + }, + errors: []string{ + `:4:12: property "exists" already defined`, + `:3:12: <-- previous definition here`, + }, + }, + { + name: "nested duplicate", + input: ` + m { + nested: { + exists: true, + exists: true, + }, + } + `, + output: []interface{}{ + &struct { + Nested struct { + Exists bool + } + }{}, + }, + errors: []string{ + `:5:13: property "nested.exists" already defined`, + `:4:13: <-- previous definition here`, + }, + }, + { + name: "wrong type", + input: ` + m { + int: "foo", + } + `, + output: []interface{}{ + &struct { + Int *int64 + }{}, + }, + errors: []string{ + `:3:11: can't assign string value to int64 property "int"`, + }, + }, + { + name: "wrong type for map", + input: ` + m { + map: "foo", + } + `, + output: []interface{}{ + &struct { + Map struct { + S string + } + }{}, + }, + errors: []string{ + `:3:11: can't assign string value to map property "map"`, + }, + }, + { + name: "wrong type for list", + input: ` + m { + list: "foo", + } + `, + output: []interface{}{ + &struct { + List []string + }{}, + }, + errors: []string{ + `:3:12: can't assign string value to list property "list"`, + }, + }, + { + name: "wrong type for list of maps", + input: ` + m { + map_list: "foo", + } + `, + output: []interface{}{ + &struct { + Map_list []struct { + S string + } + }{}, + }, + errors: []string{ + `:3:16: can't assign string value to list property "map_list"`, + }, + }, + { + name: "invalid use of maps", + input: ` + m { + map: {"foo": "bar"}, + } + `, + output: []interface{}{ + &struct { + Map map[string]string + }{}, + }, + errors: []string{ + `: Uses of maps for properties must be allowlisted. "map" is an unsupported use case`, + }, + }, + { + name: "invalid use of maps, not used in bp file", + input: ` + m { + } + `, + output: []interface{}{ + &struct { + Map map[string]string + }{}, + }, + errors: []string{ + `: Uses of maps for properties must be allowlisted. "map" is an unsupported use case`, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + r := bytes.NewBufferString(testCase.input) + file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) + if len(errs) != 0 { + t.Errorf("test case: %s", testCase.input) + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + for _, def := range file.Defs { + module, ok := def.(*parser.Module) + if !ok { + continue + } + + var output []interface{} + for _, p := range testCase.output { + output = append(output, CloneEmptyProperties(reflect.ValueOf(p)).Interface()) + } + + _, errs = UnpackProperties(module.Properties, output...) + + printErrors := false + for _, expectedErr := range testCase.errors { + foundError := false + for _, err := range errs { + if err.Error() == expectedErr { + foundError = true + } + } + if !foundError { + t.Errorf("expected error %s", expectedErr) + printErrors = true + } + } + if printErrors { + t.Errorf("got errors:") + for _, err := range errs { + t.Errorf(" %s", err.Error()) + } + } + } + }) + } +} + +func BenchmarkUnpackProperties(b *testing.B) { + run := func(b *testing.B, props []interface{}, input string) { + b.ReportAllocs() + b.StopTimer() + r := bytes.NewBufferString(input) + file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) + if len(errs) != 0 { + b.Errorf("test case: %s", input) + b.Errorf("unexpected parse errors:") + for _, err := range errs { + b.Errorf(" %s", err) + } + b.FailNow() + } + + for i := 0; i < b.N; i++ { + for _, def := range file.Defs { + module, ok := def.(*parser.Module) + if !ok { + continue + } + + var output []interface{} + for _, p := range props { + output = append(output, CloneProperties(reflect.ValueOf(p)).Interface()) + } + + b.StartTimer() + _, errs = UnpackProperties(module.Properties, output...) + b.StopTimer() + if len(errs) > 0 { + b.Errorf("unexpected unpack errors:") + for _, err := range errs { + b.Errorf(" %s", err) + } + } + } + } + } + + b.Run("basic", func(b *testing.B) { + props := []interface{}{ + &struct { + Nested struct { + S string + } + }{}, + } + bp := ` + m { + nested: { + s: "abc", + }, + } + ` + run(b, props, bp) + }) + + b.Run("interface", func(b *testing.B) { + props := []interface{}{ + &struct { + Nested interface{} + }{ + Nested: (*struct { + S string + })(nil), + }, + } + bp := ` + m { + nested: { + s: "abc", + }, + } + ` + run(b, props, bp) + }) + + b.Run("many", func(b *testing.B) { + props := []interface{}{ + &struct { + A *string + B *string + C *string + D *string + E *string + F *string + G *string + H *string + I *string + J *string + }{}, + } + bp := ` + m { + a: "a", + b: "b", + c: "c", + d: "d", + e: "e", + f: "f", + g: "g", + h: "h", + i: "i", + j: "j", + } + ` + run(b, props, bp) + }) + + b.Run("deep", func(b *testing.B) { + props := []interface{}{ + &struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + Nested struct { + S string + } + } + } + } + } + } + } + } + } + } + }{}, + } + bp := ` + m { + nested: { nested: { nested: { nested: { nested: { + nested: { nested: { nested: { nested: { nested: { + s: "abc", + }, }, }, }, }, + }, }, }, }, }, + } + ` + run(b, props, bp) + }) + + b.Run("mix", func(b *testing.B) { + props := []interface{}{ + &struct { + Name string + Flag bool + Settings []string + Perarch *struct { + Arm string + Arm64 string + } + Configvars []struct { + Name string + Values []string + } + }{}, + } + bp := ` + m { + name: "mymodule", + flag: true, + settings: ["foo1", "foo2", "foo3",], + perarch: { + arm: "32", + arm64: "64", + }, + configvars: [ + { name: "var1", values: ["var1:1", "var1:2", ], }, + { name: "var2", values: ["var2:1", "var2:2", ], }, + ], + } + ` + run(b, props, bp) + }) +} diff --git a/blueprint/provider.go b/blueprint/provider.go new file mode 100644 index 0000000..b83e1d4 --- /dev/null +++ b/blueprint/provider.go @@ -0,0 +1,216 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "reflect" +) + +// This file implements Providers, modelled after Bazel +// (https://docs.bazel.build/versions/master/skylark/rules.html#providers). +// Each provider can be associated with a mutator, in which case the value for the provider for a +// module can only be set during the mutator call for the module, and the value can only be +// retrieved after the mutator call for the module. For providers not associated with a mutator, the +// value can for the provider for a module can only be set during GenerateBuildActions for the +// module, and the value can only be retrieved after GenerateBuildActions for the module. +// +// Providers are globally registered during init() and given a unique ID. The value of a provider +// for a module is stored in an []interface{} indexed by the ID. If the value of a provider has +// not been set, the value in the []interface{} will be nil. +// +// If the storage used by the provider value arrays becomes too large: +// sizeof([]interface) * number of providers * number of modules that have a provider value set +// then the storage can be replaced with something like a bitwise trie. +// +// The purpose of providers is to provide a serializable checkpoint between modules to enable +// Blueprint to skip parts of the analysis phase when inputs haven't changed. To that end, +// values passed to providers should be treated as immutable by callers to both the getters and +// setters. Go doesn't provide any way to enforce immutability on arbitrary types, so it may be +// necessary for the getters and setters to make deep copies of the values, likely extending +// proptools.CloneProperties to do so. + +type provider struct { + id int + typ reflect.Type + zero interface{} + mutator string +} + +type ProviderKey *provider + +var providerRegistry []ProviderKey + +// NewProvider returns a ProviderKey for the type of the given example value. The example value +// is otherwise unused. +// +// The returned ProviderKey can be used to set a value of the ProviderKey's type for a module +// inside GenerateBuildActions for the module, and to get the value from GenerateBuildActions from +// any module later in the build graph. +// +// Once Go has generics the exampleValue parameter will not be necessary: +// NewProvider(type T)() ProviderKey(T) +func NewProvider(exampleValue interface{}) ProviderKey { + return NewMutatorProvider(exampleValue, "") +} + +// NewMutatorProvider returns a ProviderKey for the type of the given example value. The example +// value is otherwise unused. +// +// The returned ProviderKey can be used to set a value of the ProviderKey's type for a module inside +// the given mutator for the module, and to get the value from GenerateBuildActions from any +// module later in the build graph in the same mutator, or any module in a later mutator or during +// GenerateBuildActions. +// +// Once Go has generics the exampleValue parameter will not be necessary: +// NewMutatorProvider(type T)(mutator string) ProviderKey(T) +func NewMutatorProvider(exampleValue interface{}, mutator string) ProviderKey { + checkCalledFromInit() + + typ := reflect.TypeOf(exampleValue) + zero := reflect.Zero(typ).Interface() + + provider := &provider{ + id: len(providerRegistry), + typ: typ, + zero: zero, + mutator: mutator, + } + + providerRegistry = append(providerRegistry, provider) + + return provider +} + +// initProviders fills c.providerMutators with the *mutatorInfo associated with each provider ID, +// if any. +func (c *Context) initProviders() { + c.providerMutators = make([]*mutatorInfo, len(providerRegistry)) + for _, provider := range providerRegistry { + for _, mutator := range c.mutatorInfo { + if mutator.name == provider.mutator { + c.providerMutators[provider.id] = mutator + } + } + } +} + +// setProvider sets the value for a provider on a moduleInfo. Verifies that it is called during the +// appropriate mutator or GenerateBuildActions pass for the provider, and that the value is of the +// appropriate type. The value should not be modified after being passed to setProvider. +// +// Once Go has generics the value parameter can be typed: +// setProvider(type T)(m *moduleInfo, provider ProviderKey(T), value T) +func (c *Context) setProvider(m *moduleInfo, provider ProviderKey, value interface{}) { + if provider.mutator == "" { + if !m.startedGenerateBuildActions { + panic(fmt.Sprintf("Can't set value of provider %s before GenerateBuildActions started", + provider.typ)) + } else if m.finishedGenerateBuildActions { + panic(fmt.Sprintf("Can't set value of provider %s after GenerateBuildActions finished", + provider.typ)) + } + } else { + expectedMutator := c.providerMutators[provider.id] + if expectedMutator == nil { + panic(fmt.Sprintf("Can't set value of provider %s associated with unregistered mutator %s", + provider.typ, provider.mutator)) + } else if c.mutatorFinishedForModule(expectedMutator, m) { + panic(fmt.Sprintf("Can't set value of provider %s after mutator %s finished", + provider.typ, provider.mutator)) + } else if !c.mutatorStartedForModule(expectedMutator, m) { + panic(fmt.Sprintf("Can't set value of provider %s before mutator %s started", + provider.typ, provider.mutator)) + } + } + + if typ := reflect.TypeOf(value); typ != provider.typ { + panic(fmt.Sprintf("Value for provider has incorrect type, wanted %s, got %s", + provider.typ, typ)) + } + + if m.providers == nil { + m.providers = make([]interface{}, len(providerRegistry)) + } + + if m.providers[provider.id] != nil { + panic(fmt.Sprintf("Value of provider %s is already set", provider.typ)) + } + + m.providers[provider.id] = value +} + +// provider returns the value, if any, for a given provider for a module. Verifies that it is +// called after the appropriate mutator or GenerateBuildActions pass for the provider on the module. +// If the value for the provider was not set it returns the zero value of the type of the provider, +// which means the return value can always be type-asserted to the type of the provider. The return +// value should always be considered read-only. +// +// Once Go has generics the return value can be typed and the type assert by callers can be dropped: +// provider(type T)(m *moduleInfo, provider ProviderKey(T)) T +func (c *Context) provider(m *moduleInfo, provider ProviderKey) (interface{}, bool) { + if provider.mutator == "" { + if !m.finishedGenerateBuildActions { + panic(fmt.Sprintf("Can't get value of provider %s before GenerateBuildActions finished", + provider.typ)) + } + } else { + expectedMutator := c.providerMutators[provider.id] + if expectedMutator != nil && !c.mutatorFinishedForModule(expectedMutator, m) { + panic(fmt.Sprintf("Can't get value of provider %s before mutator %s finished", + provider.typ, provider.mutator)) + } + } + + if len(m.providers) > provider.id { + if p := m.providers[provider.id]; p != nil { + return p, true + } + } + + return provider.zero, false +} + +func (c *Context) mutatorFinishedForModule(mutator *mutatorInfo, m *moduleInfo) bool { + if c.finishedMutators[mutator] { + // mutator pass finished for all modules + return true + } + + if c.startedMutator == mutator { + // mutator pass started, check if it is finished for this module + return m.finishedMutator == mutator + } + + // mutator pass hasn't started + return false +} + +func (c *Context) mutatorStartedForModule(mutator *mutatorInfo, m *moduleInfo) bool { + if c.finishedMutators[mutator] { + // mutator pass finished for all modules + return true + } + + if c.startedMutator == mutator { + // mutator pass is currently running + if m.startedMutator == mutator { + // mutator has started for this module + return true + } + } + + return false +} diff --git a/blueprint/provider_test.go b/blueprint/provider_test.go new file mode 100644 index 0000000..942dd31 --- /dev/null +++ b/blueprint/provider_test.go @@ -0,0 +1,420 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "reflect" + "strings" + "testing" +) + +type providerTestModule struct { + SimpleName + properties struct { + Deps []string + } + + mutatorProviderValues []string + generateBuildActionsProviderValues []string +} + +func newProviderTestModule() (Module, []interface{}) { + m := &providerTestModule{} + return m, []interface{}{&m.properties, &m.SimpleName.Properties} +} + +type providerTestMutatorInfo struct { + Values []string +} + +type providerTestGenerateBuildActionsInfo struct { + Value string +} + +type providerTestUnsetInfo string + +var providerTestMutatorInfoProvider = NewMutatorProvider(&providerTestMutatorInfo{}, "provider_mutator") +var providerTestGenerateBuildActionsInfoProvider = NewProvider(&providerTestGenerateBuildActionsInfo{}) +var providerTestUnsetInfoProvider = NewMutatorProvider((providerTestUnsetInfo)(""), "provider_mutator") +var providerTestUnusedMutatorProvider = NewMutatorProvider(&struct{ unused string }{}, "nonexistent_mutator") + +func (p *providerTestModule) GenerateBuildActions(ctx ModuleContext) { + unset := ctx.Provider(providerTestUnsetInfoProvider).(providerTestUnsetInfo) + if unset != "" { + panic(fmt.Sprintf("expected zero value for providerTestGenerateBuildActionsInfoProvider before it was set, got %q", + unset)) + } + + _ = ctx.Provider(providerTestUnusedMutatorProvider) + + ctx.SetProvider(providerTestGenerateBuildActionsInfoProvider, &providerTestGenerateBuildActionsInfo{ + Value: ctx.ModuleName(), + }) + + mp := ctx.Provider(providerTestMutatorInfoProvider).(*providerTestMutatorInfo) + if mp != nil { + p.mutatorProviderValues = mp.Values + } + + ctx.VisitDirectDeps(func(module Module) { + gbap := ctx.OtherModuleProvider(module, providerTestGenerateBuildActionsInfoProvider).(*providerTestGenerateBuildActionsInfo) + if gbap != nil { + p.generateBuildActionsProviderValues = append(p.generateBuildActionsProviderValues, gbap.Value) + } + }) +} + +func providerTestDepsMutator(ctx BottomUpMutatorContext) { + if p, ok := ctx.Module().(*providerTestModule); ok { + ctx.AddDependency(ctx.Module(), nil, p.properties.Deps...) + } +} + +func providerTestMutator(ctx BottomUpMutatorContext) { + values := []string{strings.ToLower(ctx.ModuleName())} + + ctx.VisitDirectDeps(func(module Module) { + mp := ctx.OtherModuleProvider(module, providerTestMutatorInfoProvider).(*providerTestMutatorInfo) + if mp != nil { + values = append(values, mp.Values...) + } + }) + + ctx.SetProvider(providerTestMutatorInfoProvider, &providerTestMutatorInfo{ + Values: values, + }) +} + +func providerTestAfterMutator(ctx BottomUpMutatorContext) { + _ = ctx.Provider(providerTestMutatorInfoProvider) +} + +func TestProviders(t *testing.T) { + ctx := NewContext() + ctx.RegisterModuleType("provider_module", newProviderTestModule) + ctx.RegisterBottomUpMutator("provider_deps_mutator", providerTestDepsMutator) + ctx.RegisterBottomUpMutator("provider_mutator", providerTestMutator) + ctx.RegisterBottomUpMutator("provider_after_mutator", providerTestAfterMutator) + + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + provider_module { + name: "A", + deps: ["B"], + } + + provider_module { + name: "B", + deps: ["C", "D"], + } + + provider_module { + name: "C", + deps: ["D"], + } + + provider_module { + name: "D", + } + `), + }) + + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) == 0 { + _, errs = ctx.ResolveDependencies(nil) + } + if len(errs) == 0 { + _, errs = ctx.PrepareBuildActions(nil) + } + if len(errs) > 0 { + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + aModule := ctx.moduleGroupFromName("A", nil).moduleByVariantName("").logicModule.(*providerTestModule) + if g, w := aModule.generateBuildActionsProviderValues, []string{"B"}; !reflect.DeepEqual(g, w) { + t.Errorf("expected A.generateBuildActionsProviderValues %q, got %q", w, g) + } + if g, w := aModule.mutatorProviderValues, []string{"a", "b", "c", "d", "d"}; !reflect.DeepEqual(g, w) { + t.Errorf("expected A.mutatorProviderValues %q, got %q", w, g) + } + + bModule := ctx.moduleGroupFromName("B", nil).moduleByVariantName("").logicModule.(*providerTestModule) + if g, w := bModule.generateBuildActionsProviderValues, []string{"C", "D"}; !reflect.DeepEqual(g, w) { + t.Errorf("expected B.generateBuildActionsProviderValues %q, got %q", w, g) + } + if g, w := bModule.mutatorProviderValues, []string{"b", "c", "d", "d"}; !reflect.DeepEqual(g, w) { + t.Errorf("expected B.mutatorProviderValues %q, got %q", w, g) + } +} + +type invalidProviderUsageMutatorInfo string +type invalidProviderUsageGenerateBuildActionsInfo string + +var invalidProviderUsageMutatorInfoProvider = NewMutatorProvider(invalidProviderUsageMutatorInfo(""), "mutator_under_test") +var invalidProviderUsageGenerateBuildActionsInfoProvider = NewProvider(invalidProviderUsageGenerateBuildActionsInfo("")) + +type invalidProviderUsageTestModule struct { + parent *invalidProviderUsageTestModule + + SimpleName + properties struct { + Deps []string + + Early_mutator_set_of_mutator_provider bool + Late_mutator_set_of_mutator_provider bool + Late_build_actions_set_of_mutator_provider bool + Early_mutator_set_of_build_actions_provider bool + + Early_mutator_get_of_mutator_provider bool + Early_module_get_of_mutator_provider bool + Early_mutator_get_of_build_actions_provider bool + Early_module_get_of_build_actions_provider bool + + Duplicate_set bool + } +} + +func invalidProviderUsageDepsMutator(ctx BottomUpMutatorContext) { + if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok { + ctx.AddDependency(ctx.Module(), nil, i.properties.Deps...) + } +} + +func invalidProviderUsageParentMutator(ctx TopDownMutatorContext) { + if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok { + ctx.VisitDirectDeps(func(module Module) { + module.(*invalidProviderUsageTestModule).parent = i + }) + } +} + +func invalidProviderUsageBeforeMutator(ctx BottomUpMutatorContext) { + if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok { + if i.properties.Early_mutator_set_of_mutator_provider { + // A mutator attempting to set the value of a provider associated with a later mutator. + ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo("")) + } + if i.properties.Early_mutator_get_of_mutator_provider { + // A mutator attempting to get the value of a provider associated with a later mutator. + _ = ctx.Provider(invalidProviderUsageMutatorInfoProvider) + } + } +} + +func invalidProviderUsageMutatorUnderTest(ctx TopDownMutatorContext) { + if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok { + if i.properties.Early_mutator_set_of_build_actions_provider { + // A mutator attempting to set the value of a non-mutator provider. + ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo("")) + } + if i.properties.Early_mutator_get_of_build_actions_provider { + // A mutator attempting to get the value of a non-mutator provider. + _ = ctx.Provider(invalidProviderUsageGenerateBuildActionsInfoProvider) + } + if i.properties.Early_module_get_of_mutator_provider { + // A mutator attempting to get the value of a provider associated with this mutator on + // a module for which this mutator hasn't run. This is a top down mutator so + // dependencies haven't run yet. + ctx.VisitDirectDeps(func(module Module) { + _ = ctx.OtherModuleProvider(module, invalidProviderUsageMutatorInfoProvider) + }) + } + } +} + +func invalidProviderUsageAfterMutator(ctx BottomUpMutatorContext) { + if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok { + if i.properties.Late_mutator_set_of_mutator_provider { + // A mutator trying to set the value of a provider associated with an earlier mutator. + ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo("")) + } + if i.properties.Late_mutator_set_of_mutator_provider { + // A mutator trying to set the value of a provider associated with an earlier mutator. + ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo("")) + } + } +} + +func (i *invalidProviderUsageTestModule) GenerateBuildActions(ctx ModuleContext) { + if i.properties.Late_build_actions_set_of_mutator_provider { + // A GenerateBuildActions trying to set the value of a provider associated with a mutator. + ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo("")) + } + if i.properties.Early_module_get_of_build_actions_provider { + // A GenerateBuildActions trying to get the value of a provider on a module for which + // GenerateBuildActions hasn't run. + _ = ctx.OtherModuleProvider(i.parent, invalidProviderUsageGenerateBuildActionsInfoProvider) + } + if i.properties.Duplicate_set { + ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo("")) + ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo("")) + } +} + +func TestInvalidProvidersUsage(t *testing.T) { + run := func(t *testing.T, module string, prop string, panicMsg string) { + t.Helper() + ctx := NewContext() + ctx.RegisterModuleType("invalid_provider_usage_test_module", func() (Module, []interface{}) { + m := &invalidProviderUsageTestModule{} + return m, []interface{}{&m.properties, &m.SimpleName.Properties} + }) + ctx.RegisterBottomUpMutator("deps", invalidProviderUsageDepsMutator) + ctx.RegisterBottomUpMutator("before", invalidProviderUsageBeforeMutator) + ctx.RegisterTopDownMutator("mutator_under_test", invalidProviderUsageMutatorUnderTest) + ctx.RegisterBottomUpMutator("after", invalidProviderUsageAfterMutator) + ctx.RegisterTopDownMutator("parent", invalidProviderUsageParentMutator) + + // Don't invalidate the parent pointer and before GenerateBuildActions. + ctx.skipCloneModulesAfterMutators = true + + var parentBP, moduleUnderTestBP, childBP string + + prop += ": true," + + switch module { + case "parent": + parentBP = prop + case "module_under_test": + moduleUnderTestBP = prop + case "child": + childBP = prop + } + + bp := fmt.Sprintf(` + invalid_provider_usage_test_module { + name: "parent", + deps: ["module_under_test"], + %s + } + + invalid_provider_usage_test_module { + name: "module_under_test", + deps: ["child"], + %s + } + + invalid_provider_usage_test_module { + name: "child", + %s + } + + `, + parentBP, + moduleUnderTestBP, + childBP) + + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(bp), + }) + + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + + if len(errs) == 0 { + _, errs = ctx.ResolveDependencies(nil) + } + + if len(errs) == 0 { + _, errs = ctx.PrepareBuildActions(nil) + } + + if len(errs) == 0 { + t.Fatal("expected an error") + } + + if len(errs) > 1 { + t.Errorf("expected a single error, got %d:", len(errs)) + for i, err := range errs { + t.Errorf("%d: %s", i, err) + } + t.FailNow() + } + + if panicErr, ok := errs[0].(panicError); ok { + if panicErr.panic != panicMsg { + t.Fatalf("expected panic %q, got %q", panicMsg, panicErr.panic) + } + } else { + t.Fatalf("expected a panicError, got %T: %s", errs[0], errs[0].Error()) + } + + } + + tests := []struct { + prop string + module string + + panicMsg string + skip string + }{ + { + prop: "early_mutator_set_of_mutator_provider", + module: "module_under_test", + panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test started", + }, + { + prop: "late_mutator_set_of_mutator_provider", + module: "module_under_test", + panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo after mutator mutator_under_test finished", + }, + { + prop: "late_build_actions_set_of_mutator_provider", + module: "module_under_test", + panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo after mutator mutator_under_test finished", + }, + { + prop: "early_mutator_set_of_build_actions_provider", + module: "module_under_test", + panicMsg: "Can't set value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions started", + }, + + { + prop: "early_mutator_get_of_mutator_provider", + module: "module_under_test", + panicMsg: "Can't get value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test finished", + }, + { + prop: "early_module_get_of_mutator_provider", + module: "module_under_test", + panicMsg: "Can't get value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test finished", + }, + { + prop: "early_mutator_get_of_build_actions_provider", + module: "module_under_test", + panicMsg: "Can't get value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions finished", + }, + { + prop: "early_module_get_of_build_actions_provider", + module: "module_under_test", + panicMsg: "Can't get value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions finished", + }, + { + prop: "duplicate_set", + module: "module_under_test", + panicMsg: "Value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo is already set", + }, + } + + for _, tt := range tests { + t.Run(tt.prop, func(t *testing.T) { + run(t, tt.module, tt.prop, tt.panicMsg) + }) + } +} diff --git a/blueprint/scope.go b/blueprint/scope.go new file mode 100644 index 0000000..3f39eb7 --- /dev/null +++ b/blueprint/scope.go @@ -0,0 +1,422 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +// A Variable represents a global Ninja variable definition that will be written +// to the output .ninja file. A variable may contain references to other global +// Ninja variables, but circular variable references are not allowed. +type Variable interface { + packageContext() *packageContext + name() string // "foo" + fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" + memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired + value(config interface{}) (ninjaString, error) + String() string +} + +// A Pool represents a Ninja pool that will be written to the output .ninja +// file. +type Pool interface { + packageContext() *packageContext + name() string // "foo" + fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" + memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired + def(config interface{}) (*poolDef, error) + String() string +} + +// A Rule represents a Ninja build rule that will be written to the output +// .ninja file. +type Rule interface { + packageContext() *packageContext + name() string // "foo" + fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" + memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired + def(config interface{}) (*ruleDef, error) + scope() *basicScope + isArg(argName string) bool + String() string +} + +type basicScope struct { + parent *basicScope + variables map[string]Variable + pools map[string]Pool + rules map[string]Rule + imports map[string]*basicScope +} + +func newScope(parent *basicScope) *basicScope { + return &basicScope{ + parent: parent, + variables: make(map[string]Variable), + pools: make(map[string]Pool), + rules: make(map[string]Rule), + imports: make(map[string]*basicScope), + } +} + +func makeRuleScope(parent *basicScope, argNames map[string]bool) *basicScope { + scope := newScope(parent) + for argName := range argNames { + _, err := scope.LookupVariable(argName) + if err != nil { + arg := &argVariable{argName} + err = scope.AddVariable(arg) + if err != nil { + // This should not happen. We should have already checked that + // the name is valid and that the scope doesn't have a variable + // with this name. + panic(err) + } + } + } + + // We treat built-in variables like arguments for the purpose of this scope. + for _, builtin := range builtinRuleArgs { + arg := &argVariable{builtin} + err := scope.AddVariable(arg) + if err != nil { + panic(err) + } + } + + return scope +} + +func (s *basicScope) LookupVariable(name string) (Variable, error) { + dotIndex := strings.IndexRune(name, '.') + if dotIndex >= 0 { + // The variable name looks like "pkg.var" + if dotIndex+1 == len(name) { + return nil, fmt.Errorf("variable name %q ends with a '.'", name) + } + if strings.ContainsRune(name[dotIndex+1:], '.') { + return nil, fmt.Errorf("variable name %q contains multiple '.' "+ + "characters", name) + } + + pkgName := name[:dotIndex] + varName := name[dotIndex+1:] + + first, _ := utf8.DecodeRuneInString(varName) + if !unicode.IsUpper(first) { + return nil, fmt.Errorf("cannot refer to unexported name %q", name) + } + + importedScope, err := s.lookupImportedScope(pkgName) + if err != nil { + return nil, err + } + + v, ok := importedScope.variables[varName] + if !ok { + return nil, fmt.Errorf("package %q does not contain variable %q", + pkgName, varName) + } + + return v, nil + } else { + // The variable name has no package part; just "var" + for ; s != nil; s = s.parent { + v, ok := s.variables[name] + if ok { + return v, nil + } + } + return nil, fmt.Errorf("undefined variable %q", name) + } +} + +func (s *basicScope) IsRuleVisible(rule Rule) bool { + _, isBuiltin := rule.(*builtinRule) + if isBuiltin { + return true + } + + name := rule.name() + + for s != nil { + if s.rules[name] == rule { + return true + } + + for _, import_ := range s.imports { + if import_.rules[name] == rule { + return true + } + } + + s = s.parent + } + + return false +} + +func (s *basicScope) IsPoolVisible(pool Pool) bool { + _, isBuiltin := pool.(*builtinPool) + if isBuiltin { + return true + } + + name := pool.name() + + for s != nil { + if s.pools[name] == pool { + return true + } + + for _, import_ := range s.imports { + if import_.pools[name] == pool { + return true + } + } + + s = s.parent + } + + return false +} + +func (s *basicScope) lookupImportedScope(pkgName string) (*basicScope, error) { + for ; s != nil; s = s.parent { + importedScope, ok := s.imports[pkgName] + if ok { + return importedScope, nil + } + } + return nil, fmt.Errorf("unknown imported package %q (missing call to "+ + "blueprint.Import()?)", pkgName) +} + +func (s *basicScope) AddImport(name string, importedScope *basicScope) error { + _, present := s.imports[name] + if present { + return fmt.Errorf("import %q is already defined in this scope", name) + } + s.imports[name] = importedScope + return nil +} + +func (s *basicScope) AddVariable(v Variable) error { + name := v.name() + _, present := s.variables[name] + if present { + return fmt.Errorf("variable %q is already defined in this scope", name) + } + s.variables[name] = v + return nil +} + +func (s *basicScope) AddPool(p Pool) error { + name := p.name() + _, present := s.pools[name] + if present { + return fmt.Errorf("pool %q is already defined in this scope", name) + } + s.pools[name] = p + return nil +} + +func (s *basicScope) AddRule(r Rule) error { + name := r.name() + _, present := s.rules[name] + if present { + return fmt.Errorf("rule %q is already defined in this scope", name) + } + s.rules[name] = r + return nil +} + +type localScope struct { + namePrefix string + scope *basicScope +} + +func newLocalScope(parent *basicScope, namePrefix string) *localScope { + return &localScope{ + namePrefix: namePrefix, + scope: newScope(parent), + } +} + +// ReparentTo sets the localScope's parent scope to the scope of the given +// package context. This allows a ModuleContext and SingletonContext to call +// a function defined in a different Go package and have that function retain +// access to all of the package-scoped variables of its own package. +func (s *localScope) ReparentTo(pctx PackageContext) { + s.scope.parent = pctx.getScope() +} + +func (s *localScope) LookupVariable(name string) (Variable, error) { + return s.scope.LookupVariable(name) +} + +func (s *localScope) IsRuleVisible(rule Rule) bool { + return s.scope.IsRuleVisible(rule) +} + +func (s *localScope) IsPoolVisible(pool Pool) bool { + return s.scope.IsPoolVisible(pool) +} + +func (s *localScope) AddLocalVariable(name, value string) (*localVariable, + error) { + + err := validateNinjaName(name) + if err != nil { + return nil, err + } + + if strings.ContainsRune(name, '.') { + return nil, fmt.Errorf("local variable name %q contains '.'", name) + } + + ninjaValue, err := parseNinjaString(s.scope, value) + if err != nil { + return nil, err + } + + v := &localVariable{ + fullName_: s.namePrefix + name, + name_: name, + value_: ninjaValue, + } + + err = s.scope.AddVariable(v) + if err != nil { + return nil, err + } + + return v, nil +} + +func (s *localScope) AddLocalRule(name string, params *RuleParams, + argNames ...string) (*localRule, error) { + + err := validateNinjaName(name) + if err != nil { + return nil, err + } + + err = validateArgNames(argNames) + if err != nil { + return nil, fmt.Errorf("invalid argument name: %s", err) + } + + argNamesSet := make(map[string]bool) + for _, argName := range argNames { + argNamesSet[argName] = true + } + + ruleScope := makeRuleScope(s.scope, argNamesSet) + + def, err := parseRuleParams(ruleScope, params) + if err != nil { + return nil, err + } + + r := &localRule{ + fullName_: s.namePrefix + name, + name_: name, + def_: def, + argNames: argNamesSet, + scope_: ruleScope, + } + + err = s.scope.AddRule(r) + if err != nil { + return nil, err + } + + return r, nil +} + +type localVariable struct { + fullName_ string + name_ string + value_ ninjaString +} + +func (l *localVariable) packageContext() *packageContext { + return nil +} + +func (l *localVariable) name() string { + return l.name_ +} + +func (l *localVariable) fullName(pkgNames map[*packageContext]string) string { + return l.fullName_ +} + +func (l *localVariable) memoizeFullName(pkgNames map[*packageContext]string) { + // Nothing to do, full name is known at initialization. +} + +func (l *localVariable) value(interface{}) (ninjaString, error) { + return l.value_, nil +} + +func (l *localVariable) String() string { + return ":" + l.fullName_ +} + +type localRule struct { + fullName_ string + name_ string + def_ *ruleDef + argNames map[string]bool + scope_ *basicScope +} + +func (l *localRule) packageContext() *packageContext { + return nil +} + +func (l *localRule) name() string { + return l.name_ +} + +func (l *localRule) fullName(pkgNames map[*packageContext]string) string { + return l.fullName_ +} + +func (l *localRule) memoizeFullName(pkgNames map[*packageContext]string) { + // Nothing to do, full name is known at initialization. +} + +func (l *localRule) def(interface{}) (*ruleDef, error) { + return l.def_, nil +} + +func (r *localRule) scope() *basicScope { + return r.scope_ +} + +func (r *localRule) isArg(argName string) bool { + return r.argNames[argName] +} + +func (r *localRule) String() string { + return ":" + r.fullName_ +} diff --git a/blueprint/singleton_ctx.go b/blueprint/singleton_ctx.go new file mode 100644 index 0000000..455f6fc --- /dev/null +++ b/blueprint/singleton_ctx.go @@ -0,0 +1,371 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + + "github.com/google/blueprint/pathtools" +) + +type Singleton interface { + GenerateBuildActions(SingletonContext) +} + +type SingletonContext interface { + // Config returns the config object that was passed to Context.PrepareBuildActions. + Config() interface{} + + // Name returns the name of the current singleton passed to Context.RegisterSingletonType + Name() string + + // ModuleName returns the name of the given Module. See BaseModuleContext.ModuleName for more information. + ModuleName(module Module) string + + // ModuleDir returns the directory of the given Module. See BaseModuleContext.ModuleDir for more information. + ModuleDir(module Module) string + + // ModuleSubDir returns the unique subdirectory name of the given Module. See ModuleContext.ModuleSubDir for + // more information. + ModuleSubDir(module Module) string + + // ModuleType returns the type of the given Module. See BaseModuleContext.ModuleType for more information. + ModuleType(module Module) string + + // BlueprintFile returns the path of the Blueprint file that defined the given module. + BlueprintFile(module Module) string + + // ModuleProvider returns the value, if any, for the provider for a module. If the value for the + // provider was not set it returns the zero value of the type of the provider, which means the + // return value can always be type-asserted to the type of the provider. The return value should + // always be considered read-only. It panics if called before the appropriate mutator or + // GenerateBuildActions pass for the provider on the module. + ModuleProvider(module Module, provider ProviderKey) interface{} + + // ModuleHasProvider returns true if the provider for the given module has been set. + ModuleHasProvider(m Module, provider ProviderKey) bool + + // ModuleErrorf reports an error at the line number of the module type in the module definition. + ModuleErrorf(module Module, format string, args ...interface{}) + + // Errorf reports an error at the specified position of the module definition file. + Errorf(format string, args ...interface{}) + + // Failed returns true if any errors have been reported. In most cases the singleton can continue with generating + // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error + // has prevented the singleton from creating necessary data it can return early when Failed returns true. + Failed() bool + + // Variable creates a new ninja variable scoped to the singleton. It can be referenced by calls to Rule and Build + // in the same singleton. + Variable(pctx PackageContext, name, value string) + + // Rule creates a new ninja rule scoped to the singleton. It can be referenced by calls to Build in the same + // singleton. + Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule + + // Build creates a new ninja build statement. + Build(pctx PackageContext, params BuildParams) + + // RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja. + RequireNinjaVersion(major, minor, micro int) + + // SetOutDir sets the value of the top-level "builddir" Ninja variable + // that controls where Ninja stores its build log files. This value can be + // set at most one time for a single build, later calls are ignored. + SetOutDir(pctx PackageContext, value string) + + // AddSubninja adds a ninja file to include with subninja. This should likely + // only ever be used inside bootstrap to handle glob rules. + AddSubninja(file string) + + // Eval takes a string with embedded ninja variables, and returns a string + // with all of the variables recursively expanded. Any variables references + // are expanded in the scope of the PackageContext. + Eval(pctx PackageContext, ninjaStr string) (string, error) + + // VisitAllModules calls visit for each defined variant of each module in an unspecified order. + VisitAllModules(visit func(Module)) + + // VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns + // true calls visit. + VisitAllModulesIf(pred func(Module) bool, visit func(Module)) + + // VisitDirectDeps calls visit for each direct dependency of the Module. If there are + // multiple direct dependencies on the same module visit will be called multiple times on + // that module and OtherModuleDependencyTag will return a different tag for each. + // + // The Module passed to the visit function should not be retained outside of the visit + // function, it may be invalidated by future mutators. + VisitDirectDeps(module Module, visit func(Module)) + + // VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred + // returns true calls visit. If there are multiple direct dependencies on the same module + // pred and visit will be called multiple times on that module and OtherModuleDependencyTag + // will return a different tag for each. + // + // The Module passed to the visit function should not be retained outside of the visit + // function, it may be invalidated by future mutators. + VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) + + // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first + // order. visit will only be called once for any given module, even if there are multiple paths through the + // dependency tree to the module or multiple direct dependencies with different tags. + VisitDepsDepthFirst(module Module, visit func(Module)) + + // VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing + // the dependency tree in depth first order. visit will only be called once for any given module, even if there are + // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags. + VisitDepsDepthFirstIf(module Module, pred func(Module) bool, + visit func(Module)) + + // VisitAllModuleVariants calls visit for each variant of the given module. + VisitAllModuleVariants(module Module, visit func(Module)) + + // PrimaryModule returns the first variant of the given module. This can be used to perform + // // singleton actions that are only done once for all variants of a module. + PrimaryModule(module Module) Module + + // FinalModule returns the last variant of the given module. This can be used to perform + // singleton actions that are only done once for all variants of a module. + FinalModule(module Module) Module + + // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The + // primary builder will be rerun whenever the specified files are modified. + AddNinjaFileDeps(deps ...string) + + // GlobWithDeps returns a list of files and directories that match the + // specified pattern but do not match any of the patterns in excludes. + // Any directories will have a '/' suffix. It also adds efficient + // dependencies to rerun the primary builder whenever a file matching + // the pattern as added or removed, without rerunning if a file that + // does not match the pattern is added to a searched directory. + GlobWithDeps(pattern string, excludes []string) ([]string, error) + + // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows + // the singleton to be used in build system tests that run against a mock filesystem. + Fs() pathtools.FileSystem +} + +var _ SingletonContext = (*singletonContext)(nil) + +type singletonContext struct { + name string + context *Context + config interface{} + scope *localScope + globals *liveTracker + + ninjaFileDeps []string + errs []error + + actionDefs localBuildActions +} + +func (s *singletonContext) Config() interface{} { + return s.config +} + +func (s *singletonContext) Name() string { + return s.name +} + +func (s *singletonContext) ModuleName(logicModule Module) string { + return s.context.ModuleName(logicModule) +} + +func (s *singletonContext) ModuleDir(logicModule Module) string { + return s.context.ModuleDir(logicModule) +} + +func (s *singletonContext) ModuleSubDir(logicModule Module) string { + return s.context.ModuleSubDir(logicModule) +} + +func (s *singletonContext) ModuleType(logicModule Module) string { + return s.context.ModuleType(logicModule) +} + +func (s *singletonContext) ModuleProvider(logicModule Module, provider ProviderKey) interface{} { + return s.context.ModuleProvider(logicModule, provider) +} + +// ModuleHasProvider returns true if the provider for the given module has been set. +func (s *singletonContext) ModuleHasProvider(logicModule Module, provider ProviderKey) bool { + return s.context.ModuleHasProvider(logicModule, provider) +} + +func (s *singletonContext) BlueprintFile(logicModule Module) string { + return s.context.BlueprintFile(logicModule) +} + +func (s *singletonContext) error(err error) { + if err != nil { + s.errs = append(s.errs, err) + } +} + +func (s *singletonContext) ModuleErrorf(logicModule Module, format string, + args ...interface{}) { + + s.error(s.context.ModuleErrorf(logicModule, format, args...)) +} + +func (s *singletonContext) Errorf(format string, args ...interface{}) { + // TODO: Make this not result in the error being printed as "internal error" + s.error(fmt.Errorf(format, args...)) +} + +func (s *singletonContext) Failed() bool { + return len(s.errs) > 0 +} + +func (s *singletonContext) Variable(pctx PackageContext, name, value string) { + s.scope.ReparentTo(pctx) + + v, err := s.scope.AddLocalVariable(name, value) + if err != nil { + panic(err) + } + + s.actionDefs.variables = append(s.actionDefs.variables, v) +} + +func (s *singletonContext) Rule(pctx PackageContext, name string, + params RuleParams, argNames ...string) Rule { + + s.scope.ReparentTo(pctx) + + r, err := s.scope.AddLocalRule(name, ¶ms, argNames...) + if err != nil { + panic(err) + } + + s.actionDefs.rules = append(s.actionDefs.rules, r) + + return r +} + +func (s *singletonContext) Build(pctx PackageContext, params BuildParams) { + s.scope.ReparentTo(pctx) + + def, err := parseBuildParams(s.scope, ¶ms) + if err != nil { + panic(err) + } + + s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def) +} + +func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) { + s.scope.ReparentTo(pctx) + + ninjaStr, err := parseNinjaString(s.scope, str) + if err != nil { + return "", err + } + + err = s.globals.addNinjaStringDeps(ninjaStr) + if err != nil { + return "", err + } + + return ninjaStr.Eval(s.globals.variables) +} + +func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) { + s.context.requireNinjaVersion(major, minor, micro) +} + +func (s *singletonContext) SetOutDir(pctx PackageContext, value string) { + s.scope.ReparentTo(pctx) + + ninjaValue, err := parseNinjaString(s.scope, value) + if err != nil { + panic(err) + } + + s.context.setOutDir(ninjaValue) +} + +func (s *singletonContext) AddSubninja(file string) { + s.context.subninjas = append(s.context.subninjas, file) +} + +func (s *singletonContext) VisitAllModules(visit func(Module)) { + var visitingModule Module + defer func() { + if r := recover(); r != nil { + panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s", + funcName(visit), s.context.moduleInfo[visitingModule])) + } + }() + + s.context.VisitAllModules(func(m Module) { + visitingModule = m + visit(m) + }) +} + +func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool, + visit func(Module)) { + + s.context.VisitAllModulesIf(pred, visit) +} + +func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) { + s.context.VisitDirectDeps(module, visit) +} + +func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) { + s.context.VisitDirectDepsIf(module, pred, visit) +} + +func (s *singletonContext) VisitDepsDepthFirst(module Module, + visit func(Module)) { + + s.context.VisitDepsDepthFirst(module, visit) +} + +func (s *singletonContext) VisitDepsDepthFirstIf(module Module, + pred func(Module) bool, visit func(Module)) { + + s.context.VisitDepsDepthFirstIf(module, pred, visit) +} + +func (s *singletonContext) PrimaryModule(module Module) Module { + return s.context.PrimaryModule(module) +} + +func (s *singletonContext) FinalModule(module Module) Module { + return s.context.FinalModule(module) +} + +func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) { + s.context.VisitAllModuleVariants(module, visit) +} + +func (s *singletonContext) AddNinjaFileDeps(deps ...string) { + s.ninjaFileDeps = append(s.ninjaFileDeps, deps...) +} + +func (s *singletonContext) GlobWithDeps(pattern string, + excludes []string) ([]string, error) { + return s.context.glob(pattern, excludes) +} + +func (s *singletonContext) Fs() pathtools.FileSystem { + return s.context.fs +} diff --git a/blueprint/splice_modules_test.go b/blueprint/splice_modules_test.go new file mode 100644 index 0000000..473999a --- /dev/null +++ b/blueprint/splice_modules_test.go @@ -0,0 +1,144 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "reflect" + "testing" +) + +var ( + testModuleA = &moduleInfo{variant: variant{name: "testModuleA"}} + testModuleB = &moduleInfo{variant: variant{name: "testModuleB"}} + testModuleC = &moduleInfo{variant: variant{name: "testModuleC"}} + testModuleD = &moduleInfo{variant: variant{name: "testModuleD"}} + testModuleE = &moduleInfo{variant: variant{name: "testModuleE"}} + testModuleF = &moduleInfo{variant: variant{name: "testModuleF"}} +) + +var spliceModulesTestCases = []struct { + in modulesOrAliases + at int + with modulesOrAliases + out modulesOrAliases + outAt int + reallocate bool +}{ + { + // Insert at the beginning + in: modulesOrAliases{testModuleA, testModuleB, testModuleC}, + at: 0, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleD, testModuleE, testModuleB, testModuleC}, + outAt: 1, + reallocate: true, + }, + { + // Insert in the middle + in: modulesOrAliases{testModuleA, testModuleB, testModuleC}, + at: 1, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleA, testModuleD, testModuleE, testModuleC}, + outAt: 2, + reallocate: true, + }, + { + // Insert at the end + in: modulesOrAliases{testModuleA, testModuleB, testModuleC}, + at: 2, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleA, testModuleB, testModuleD, testModuleE}, + outAt: 3, + reallocate: true, + }, + { + // Insert over a single element + in: modulesOrAliases{testModuleA}, + at: 0, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleD, testModuleE}, + outAt: 1, + reallocate: true, + }, + { + // Insert at the beginning without reallocating + in: modulesOrAliases{testModuleA, testModuleB, testModuleC, nil}[0:3], + at: 0, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleD, testModuleE, testModuleB, testModuleC}, + outAt: 1, + reallocate: false, + }, + { + // Insert in the middle without reallocating + in: modulesOrAliases{testModuleA, testModuleB, testModuleC, nil}[0:3], + at: 1, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleA, testModuleD, testModuleE, testModuleC}, + outAt: 2, + reallocate: false, + }, + { + // Insert at the end without reallocating + in: modulesOrAliases{testModuleA, testModuleB, testModuleC, nil}[0:3], + at: 2, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleA, testModuleB, testModuleD, testModuleE}, + outAt: 3, + reallocate: false, + }, + { + // Insert over a single element without reallocating + in: modulesOrAliases{testModuleA, nil}[0:1], + at: 0, + with: modulesOrAliases{testModuleD, testModuleE}, + out: modulesOrAliases{testModuleD, testModuleE}, + outAt: 1, + reallocate: false, + }, +} + +func TestSpliceModules(t *testing.T) { + for _, testCase := range spliceModulesTestCases { + in := make(modulesOrAliases, len(testCase.in), cap(testCase.in)) + copy(in, testCase.in) + origIn := in + got, gotAt := spliceModules(in, testCase.at, testCase.with) + if !reflect.DeepEqual(got, testCase.out) { + t.Errorf("test case: %v, %v -> %v", testCase.in, testCase.at, testCase.with) + t.Errorf("incorrect output:") + t.Errorf(" expected: %v", testCase.out) + t.Errorf(" got: %v", got) + } + if gotAt != testCase.outAt { + t.Errorf("test case: %v, %v -> %v", testCase.in, testCase.at, testCase.with) + t.Errorf("incorrect index:") + t.Errorf(" expected: %d", testCase.outAt) + t.Errorf(" got: %d", gotAt) + } + if sameArray(origIn, got) != !testCase.reallocate { + t.Errorf("test case: %v, %v -> %v", testCase.in, testCase.at, testCase.with) + not := "" + if !testCase.reallocate { + not = " not" + } + t.Errorf(" expected to%s reallocate", not) + } + } +} + +func sameArray(a, b modulesOrAliases) bool { + return &a[0:cap(a)][cap(a)-1] == &b[0:cap(b)][cap(b)-1] +} diff --git a/blueprint/tests/.out-dir b/blueprint/tests/.out-dir new file mode 100644 index 0000000..e69de29 diff --git a/blueprint/tests/bootstrap.bash b/blueprint/tests/bootstrap.bash new file mode 100755 index 0000000..6aececa --- /dev/null +++ b/blueprint/tests/bootstrap.bash @@ -0,0 +1,7 @@ +#!/bin/bash + +export BOOTSTRAP="${BASH_SOURCE[0]}" +export SRCDIR=".." +export WRAPPER="../blueprint.bash" + +../bootstrap.bash "$@" diff --git a/blueprint/tests/test.sh b/blueprint/tests/test.sh new file mode 100755 index 0000000..b0e3de2 --- /dev/null +++ b/blueprint/tests/test.sh @@ -0,0 +1,33 @@ +#!/bin/bash -ex + +# Go to srcdir +cd $(dirname ${BASH_SOURCE[0]})/.. + +rm -rf out.test +mkdir out.test +cd out.test +../tests/bootstrap.bash +./blueprint.bash + +if [[ -d .bootstrap/blueprint/test ]]; then + echo "Tests should not be enabled here" >&2 + exit 1 +fi + +sleep 2 +../tests/bootstrap.bash -t +./blueprint.bash + +if [[ ! -d .bootstrap/blueprint/test ]]; then + echo "Tests should be enabled here" >&2 + exit 1 +fi + +sleep 2 +../tests/bootstrap.bash +./blueprint.bash + +if [[ -d .bootstrap/blueprint/test ]]; then + echo "Tests should not be enabled here (2)" >&2 + exit 1 +fi diff --git a/blueprint/tests/test_tree/Blueprints b/blueprint/tests/test_tree/Blueprints new file mode 100644 index 0000000..aeb68d9 --- /dev/null +++ b/blueprint/tests/test_tree/Blueprints @@ -0,0 +1,3 @@ +// Root Blueprints file for test_tree + +subdirs = ["*"] diff --git a/blueprint/tests/test_tree/a/Blueprints b/blueprint/tests/test_tree/a/Blueprints new file mode 100644 index 0000000..836d6ba --- /dev/null +++ b/blueprint/tests/test_tree/a/Blueprints @@ -0,0 +1 @@ +// a/Blueprints diff --git a/blueprint/tests/test_tree_tests.sh b/blueprint/tests/test_tree_tests.sh new file mode 100755 index 0000000..03d918f --- /dev/null +++ b/blueprint/tests/test_tree_tests.sh @@ -0,0 +1,116 @@ +#!/bin/bash -ex + +function mtime() { + stat -c %Y $1 +} + +# Go to top of blueprint tree +cd $(dirname ${BASH_SOURCE[0]})/.. +TOP=${PWD} + +export TEMPDIR=$(mktemp -d -t blueprint.test.XXX) + +function cleanup() { + cd "${TOP}" + echo "cleaning up ${TEMPDIR}" + rm -rf "${TEMPDIR}" +} +trap cleanup EXIT + +export OUTDIR="${TEMPDIR}/out" +mkdir "${OUTDIR}" + +export SRCDIR="${TEMPDIR}/src" +cp -r tests/test_tree "${SRCDIR}" +cp -r "${TOP}" "${SRCDIR}/blueprint" + +cd "${OUTDIR}" +export BLUEPRINTDIR=${SRCDIR}/blueprint +#setup +${SRCDIR}/blueprint/bootstrap.bash $@ + +#confirm no build.ninja file is rebuilt when no change happens +./blueprint.bash + +OLDTIME_BOOTSTRAP=$(mtime .bootstrap/build.ninja) +OLDTIME=$(mtime build.ninja) + +sleep 2 +./blueprint.bash + +if [ ${OLDTIME} != $(mtime build.ninja) ]; then + echo "unnecessary build.ninja regeneration for null build" >&2 + exit 1 +fi + +if [ ${OLDTIME_BOOTSTRAP} != $(mtime .bootstrap/build.ninja) ]; then + echo "unnecessary .bootstrap/build.ninja regeneration for null build" >&2 + exit 1 +fi + +#confirm no build.ninja file is rebuilt when a new directory is created +mkdir ${SRCDIR}/newglob + +sleep 2 +./blueprint.bash + +if [ ${OLDTIME} != $(mtime build.ninja) ]; then + echo "unnecessary build.ninja regeneration for new empty directory" >&2 + exit 1 +fi +if [ ${OLDTIME_BOOTSTRAP} != $(mtime .bootstrap/build.ninja) ]; then + echo "unnecessary .bootstrap/build.ninja regeneration for new empty directory" >&2 + exit 1 +fi + +#confirm that build.ninja is rebuilt when a new Blueprints file is added +touch ${SRCDIR}/newglob/Blueprints + +sleep 2 +./blueprint.bash + +if [ ${OLDTIME} = $(mtime build.ninja) ]; then + echo "Failed to rebuild build.ninja for glob addition" >&2 + exit 1 +fi +if [ ${OLDTIME_BOOTSTRAP} = $(mtime .bootstrap/build.ninja) ]; then + echo "Failed to rebuild .bootstrap/build.ninja for glob addition" >&2 + exit 1 +fi + +#confirm that build.ninja is rebuilt when a glob match is removed +OLDTIME=$(mtime build.ninja) +OLDTIME_BOOTSTRAP=$(mtime .bootstrap/build.ninja) +rm ${SRCDIR}/newglob/Blueprints + +sleep 2 +./blueprint.bash + +if [ ${OLDTIME} = $(mtime build.ninja) ]; then + echo "Failed to rebuild build.ninja for glob removal" >&2 + exit 1 +fi +if [ ${OLDTIME_BOOTSTRAP} = $(mtime .bootstrap/build.ninja) ]; then + echo "Failed to rebuild .bootstrap/build.ninja for glob removal" >&2 + exit 1 +fi + +#confirm that build.ninja is not rebuilt when a glob match is removed +OLDTIME=$(mtime build.ninja) +OLDTIME_BOOTSTRAP=$(mtime .bootstrap/build.ninja) +rmdir ${SRCDIR}/newglob + +sleep 2 +./blueprint.bash + +if [ ${OLDTIME} != $(mtime build.ninja) ]; then + echo "unnecessary build.ninja regeneration for removal of empty directory" >&2 + exit 1 +fi + +if [ ${OLDTIME_BOOTSTRAP} != $(mtime .bootstrap/build.ninja) ]; then + echo "unnecessary .bootstrap/build.ninja regeneration for removal of empty directory" >&2 + exit 1 +fi + +echo tests passed diff --git a/blueprint/visit_test.go b/blueprint/visit_test.go new file mode 100644 index 0000000..798e289 --- /dev/null +++ b/blueprint/visit_test.go @@ -0,0 +1,169 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blueprint + +import ( + "fmt" + "testing" +) + +type visitModule struct { + SimpleName + properties struct { + Visit []string + VisitDepsDepthFirst string `blueprint:"mutated"` + VisitDepsDepthFirstIf string `blueprint:"mutated"` + VisitDirectDeps string `blueprint:"mutated"` + VisitDirectDepsIf string `blueprint:"mutated"` + } +} + +func newVisitModule() (Module, []interface{}) { + m := &visitModule{} + return m, []interface{}{&m.properties, &m.SimpleName.Properties} +} + +func (f *visitModule) GenerateBuildActions(ModuleContext) { +} + +type visitTag struct { + BaseDependencyTag +} + +var visitTagDep visitTag + +func visitDepsMutator(ctx BottomUpMutatorContext) { + if m, ok := ctx.Module().(*visitModule); ok { + ctx.AddDependency(ctx.Module(), visitTagDep, m.properties.Visit...) + } +} + +func visitMutator(ctx TopDownMutatorContext) { + if m, ok := ctx.Module().(*visitModule); ok { + ctx.VisitDepsDepthFirst(func(dep Module) { + if ctx.OtherModuleDependencyTag(dep) != visitTagDep { + panic(fmt.Errorf("unexpected dependency tag on %q", ctx.OtherModuleName(dep))) + } + m.properties.VisitDepsDepthFirst = m.properties.VisitDepsDepthFirst + ctx.OtherModuleName(dep) + }) + ctx.VisitDepsDepthFirstIf(func(dep Module) bool { + return ctx.OtherModuleName(dep) != "B" + }, func(dep Module) { + m.properties.VisitDepsDepthFirstIf = m.properties.VisitDepsDepthFirstIf + ctx.OtherModuleName(dep) + }) + ctx.VisitDirectDeps(func(dep Module) { + m.properties.VisitDirectDeps = m.properties.VisitDirectDeps + ctx.OtherModuleName(dep) + }) + ctx.VisitDirectDepsIf(func(dep Module) bool { + return ctx.OtherModuleName(dep) != "B" + }, func(dep Module) { + m.properties.VisitDirectDepsIf = m.properties.VisitDirectDepsIf + ctx.OtherModuleName(dep) + }) + } +} + +// A +// | +// B +// |\ +// C \ +// \| +// D +// | +// E +// / \ +// \ / +// F +func setupVisitTest(t *testing.T) *Context { + ctx := NewContext() + ctx.RegisterModuleType("visit_module", newVisitModule) + ctx.RegisterBottomUpMutator("visit_deps", visitDepsMutator) + ctx.RegisterTopDownMutator("visit", visitMutator) + + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(` + visit_module { + name: "A", + visit: ["B"], + } + + visit_module { + name: "B", + visit: ["C", "D"], + } + + visit_module { + name: "C", + visit: ["D"], + } + + visit_module { + name: "D", + visit: ["E"], + } + + visit_module { + name: "E", + visit: ["F", "F"], + } + + visit_module { + name: "F", + } + `), + }) + + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + t.Errorf("unexpected parse errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + t.Errorf("unexpected dep errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + + return ctx +} + +func TestVisit(t *testing.T) { + ctx := setupVisitTest(t) + + topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule().logicModule.(*visitModule) + assertString(t, topModule.properties.VisitDepsDepthFirst, "FEDCB") + assertString(t, topModule.properties.VisitDepsDepthFirstIf, "FEDC") + assertString(t, topModule.properties.VisitDirectDeps, "B") + assertString(t, topModule.properties.VisitDirectDepsIf, "") + + eModule := ctx.moduleGroupFromName("E", nil).modules.firstModule().logicModule.(*visitModule) + assertString(t, eModule.properties.VisitDepsDepthFirst, "F") + assertString(t, eModule.properties.VisitDepsDepthFirstIf, "F") + assertString(t, eModule.properties.VisitDirectDeps, "FF") + assertString(t, eModule.properties.VisitDirectDepsIf, "FF") +} + +func assertString(t *testing.T, got, expected string) { + if got != expected { + t.Errorf("expected %q got %q", expected, got) + } +} diff --git a/buildspec.mk.default b/buildspec.mk.default new file mode 120000 index 0000000..48c9d01 --- /dev/null +++ b/buildspec.mk.default @@ -0,0 +1 @@ +make/buildspec.mk.default \ No newline at end of file diff --git a/core b/core new file mode 120000 index 0000000..7fb1138 --- /dev/null +++ b/core @@ -0,0 +1 @@ +make/core \ No newline at end of file diff --git a/envsetup.sh b/envsetup.sh new file mode 120000 index 0000000..479006f --- /dev/null +++ b/envsetup.sh @@ -0,0 +1 @@ +make/envsetup.sh \ No newline at end of file diff --git a/make/.gitignore b/make/.gitignore new file mode 100644 index 0000000..f1f4a52 --- /dev/null +++ b/make/.gitignore @@ -0,0 +1,5 @@ +*.pyc +*.swp +blueprint/ +kati/ +soong/ diff --git a/make/Changes.md b/make/Changes.md new file mode 100644 index 0000000..cabbed6 --- /dev/null +++ b/make/Changes.md @@ -0,0 +1,755 @@ +# Build System Changes for Android.mk Writers + +## Genrule starts disallowing directory inputs + +To better specify the inputs to the build, we are restricting use of directories +as inputs to genrules. + +To fix existing uses, change inputs to specify the inputs and update the command +accordingly. For example: + +``` +genrule: { + name: "foo", + srcs: ["bar"], + cmd: "cp $(location bar)/*.xml $(gendir)", + ... +} +``` + +would become + +``` +genrule: { + name: "foo", + srcs: ["bar/*.xml"], + cmd: "cp $(in) $(gendir)", + ... +} +``` + +`BUILD_BROKEN_INPUT_DIR_MODULES` can be used to allowlist specific directories +with genrules that have input directories. + +## Dexpreopt starts enforcing `` checks (for Java modules) + +In order to construct correct class loader context for dexpreopt, build system +needs to know about the shared library dependencies of Java modules listed in +the `` tags in the manifest. Since the build system does not have +access to the manifest contents, that information must be present in the build +files. In simple cases Soong is able to infer it from its knowledge of Java SDK +libraries and the `libs` property in Android.bp, but in more complex cases it is +necessary to add the missing information in Android.bp/Android.mk manually. + +To specify a list of libraries for a given modules, use: + +* Android.bp properties: `uses_libs`, `optional_uses_libs` +* Android.mk variables: `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_USES_LIBRARIES` + +If a library is in `libs`, it usually should *not* be added to the above +properties, and Soong should be able to infer the `` tag. But +sometimes a library also needs additional information in its +Android.bp/Android.mk file (e.g. when it is a `java_library` rather than a +`java_sdk_library`, or when the library name is different from its module name, +or when the module is defined in Android.mk rather than Android.bp). In such +cases it is possible to tell the build system that the library provides a +`` with a given name (however, this is discouraged and will be +deprecated in the future, and it is recommended to fix the underlying problem): + +* Android.bp property: `provides_uses_lib` +* Android.mk variable: `LOCAL_PROVIDES_USES_LIBRARY` + +It is possible to disable the check on a per-module basis. When doing that it is +also recommended to disable dexpreopt, as disabling a failed check will result +in incorrect class loader context recorded in the .odex file, which will cause +class loader context mismatch and dexopt at first boot. + +* Android.bp property: `enforce_uses_lib` +* Android.mk variable: `LOCAL_ENFORCE_USES_LIBRARIES` + +Finally, it is possible to globally disable the check: + +* For a given product: `PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true` +* On the command line: `RELAX_USES_LIBRARY_CHECK=true` + +The environment variable overrides the product variable, so it is possible to +disable the check for a product, but quickly re-enable it for a local build. + +## `LOCAL_REQUIRED_MODULES` requires listed modules to exist {#BUILD_BROKEN_MISSING_REQUIRED_MODULES} + +Modules listed in `LOCAL_REQUIRED_MODULES`, `LOCAL_HOST_REQUIRED_MODULES` and +`LOCAL_TARGET_REQUIRED_MODULES` need to exist unless `ALLOW_MISSING_DEPENDENCIES` +is set. + +To temporarily relax missing required modules check, use: + +`BUILD_BROKEN_MISSING_REQUIRED_MODULES := true` + +## Changes in system properties settings + +### Product variables + +System properties for each of the partition is supposed to be set via following +product config variables. + +For system partition, + +* `PRODUCT_SYSTEM_PROPERTIES` +* `PRODUCT_SYSTEM_DEFAULT_PROPERTIES` is highly discouraged. Will be deprecated. + +For vendor partition, + +* `PRODUCT_VENDOR_PROPERTIES` +* `PRODUCT_PROPERTY_OVERRIDES` is highly discouraged. Will be deprecated. +* `PRODUCT_DEFAULT_PROPERTY_OVERRIDES` is also discouraged. Will be deprecated. + +For odm partition, + +* `PRODUCT_ODM_PROPERTIES` + +For system_ext partition, + +* `PRODUCT_SYSTEM_EXT_PROPERTIES` + +For product partition, + +* `PRODUCT_PRODUCT_PROPERTIES` + +### Duplication is not allowed within a partition + +For each partition, having multiple sysprop assignments for the same name is +prohibited. For example, the following will now trigger an error: + +`PRODUCT_VENDOR_PROPERTIES += foo=true foo=false` + +Having duplication across partitions are still allowed. So, the following is +not an error: + +`PRODUCT_VENDOR_PROPERTIES += foo=true` +`PRODUCT_SYSTEM_PROPERTIES += foo=false` + +In that case, the final value is determined at runtime. The precedence is + +* product +* odm +* vendor +* system_ext +* system + +So, `foo` becomes `true` because vendor has higher priority than system. + +To temporarily turn the build-time restriction off, use + +`BUILD_BROKEN_DUP_SYSPROP := true` + +### Optional assignments + +System properties can now be set as optional using the new syntax: + +`name ?= value` + +Then the system property named `name` gets the value `value` only when there +is no other non-optional assignments having the same name. For example, the +following is allowed and `foo` gets `true` + +`PRODUCT_VENDOR_PROPERTIES += foo=true foo?=false` + +Note that the order between the optional and the non-optional assignments +doesn't matter. The following gives the same result as above. + +`PRODUCT_VENDOR_PROPERTIES += foo?=false foo=true` + +Optional assignments can be duplicated and in that case their order matters. +Specifically, the last one eclipses others. + +`PRODUCT_VENDOR_PROPERTIES += foo?=apple foo?=banana foo?=mango` + +With above, `foo` becomes `mango` since its the last one. + +Note that this behavior is different from the previous behavior of preferring +the first one. To go back to the original behavior for compatability reason, +use: + +`BUILD_BROKEN_DUP_SYSPROP := true` + +## ELF prebuilts in `PRODUCT_COPY_FILES` {#BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES} + +ELF prebuilts in `PRODUCT_COPY_FILES` that are installed into these paths are an +error: + +* `/bin/*` +* `/lib/*` +* `/lib64/*` + +Define prebuilt modules and add them to `PRODUCT_PACKAGES` instead. +To temporarily relax this check and restore the behavior prior to this change, +set `BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES := true` in `BoardConfig.mk`. + +## COPY_HEADERS usage now produces warnings {#copy_headers} + +We've considered `BUILD_COPY_HEADERS`/`LOCAL_COPY_HEADERS` to be deprecated for +a long time, and the places where it's been able to be used have shrinked over +the last several releases. Equivalent functionality is not available in Soong. + +See the [build/soong/docs/best_practices.md#headers] for more information about +how best to handle headers in Android. + +## `m4` is not available on `$PATH` + +There is a prebuilt of it available in prebuilts/build-tools, and a make +variable `M4` that contains the path. + +Beyond the direct usage, whenever you use bison or flex directly, they call m4 +behind the scene, so you must set the M4 environment variable (and depend upon +it for incremental build correctness): + +``` +$(intermediates)/foo.c: .KATI_IMPLICIT_OUTPUTS := $(intermediates)/foo.h +$(intermediates)/foo.c: $(LOCAL_PATH)/foo.y $(M4) $(BISON) $(BISON_DATA) + M4=$(M4) $(BISON) ... +``` + +## Rules executed within limited environment + +With `ALLOW_NINJA_ENV=false` (soon to be the default), ninja, and all the +rules/actions executed within it will only have access to a limited number of +environment variables. Ninja does not track when environment variables change +in order to trigger rebuilds, so changing behavior based on arbitrary variables +is not safe with incremental builds. + +Kati and Soong can safely use environment variables, so the expectation is that +you'd embed any environment variables that you need to use within the command +line generated by those tools. See the [export section](#export_keyword) below +for examples. + +For a temporary workaround, you can set `ALLOW_NINJA_ENV=true` in your +environment to restore the previous behavior, or set +`BUILD_BROKEN_NINJA_USES_ENV_VAR := ...` in your `BoardConfig.mk` +to allow specific variables to be passed through until you've fixed the rules. + +## LOCAL_C_INCLUDES outside the source/output trees are an error {#BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS} + +Include directories are expected to be within the source tree (or in the output +directory, generated during the build). This has been checked in some form +since Oreo, but now has better checks. + +There's now a `BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS` variable, that when set, will +turn these errors into warnings temporarily. I don't expect this to last more +than a release, since they're fairly easy to clean up. + +Neither of these cases are supported by Soong, and will produce errors when +converting your module. + +### Absolute paths + +This has been checked since Oreo. The common reason to hit this is because a +makefile is calculating a path, and ran abspath/realpath/etc. This is a problem +because it makes your build non-reproducible. It's very unlikely that your +source path is the same on every machine. + +### Using `../` to leave the source/output directories + +This is the new check that has been added. In every case I've found, this has +been a mistake in the Android.mk -- assuming that `LOCAL_C_INCLUDES` (which is +relative to the top of the source tree) acts like `LOCAL_SRC_FILES` (which is +relative to `LOCAL_PATH`). + +Since this usually isn't a valid path, you can almost always just remove the +offending line. + + +## `BOARD_HAL_STATIC_LIBRARIES` and `LOCAL_HAL_STATIC_LIBRARIES` are obsolete {#BOARD_HAL_STATIC_LIBRARIES} + +Define proper HIDL / Stable AIDL HAL instead. + +* For libhealthd, use health HAL. See instructions for implementing + health HAL: + + * [hardware/interfaces/health/2.1/README.md] for health 2.1 HAL (recommended) + * [hardware/interfaces/health/1.0/README.md] for health 1.0 HAL + +* For libdumpstate, use at least Dumpstate HAL 1.0. + +## PRODUCT_STATIC_BOOT_CONTROL_HAL is obsolete {#PRODUCT_STATIC_BOOT_CONTROL_HAL} + +`PRODUCT_STATIC_BOOT_CONTROL_HAL` was the workaround to allow sideloading with +statically linked boot control HAL, before shared library HALs were supported +under recovery. Android Q has added such support (HALs will be loaded in +passthrough mode), and the workarounds are being removed. Targets should build +and install the recovery variant of boot control HAL modules into recovery +image, similar to the ones installed for normal boot. See the change to +crosshatch for example of this: + +* [device/google/crosshatch/bootctrl/Android.bp] for `bootctrl.sdm845` building + rules +* [device/google/crosshatch/device.mk] for installing `bootctrl.sdm845.recovery` + and `android.hardware.boot@1.0-impl.recovery` into recovery image + +[device/google/crosshatch/bootctrl/Android.bp]: https://android.googlesource.com/device/google/crosshatch/+/master/bootctrl/Android.bp +[device/google/crosshatch/device.mk]: https://android.googlesource.com/device/google/crosshatch/+/master/device.mk + +## Deprecation of `BUILD_*` module types + +See [build/make/Deprecation.md](Deprecation.md) for the current status. + +## `PRODUCT_HOST_PACKAGES` split from `PRODUCT_PACKAGES` {#PRODUCT_HOST_PACKAGES} + +Previously, adding a module to `PRODUCT_PACKAGES` that supported both the host +and the target (`host_supported` in Android.bp; two modules with the same name +in Android.mk) would cause both to be built and installed. In many cases you +only want either the host or target versions to be built/installed by default, +and would be over-building with both. So `PRODUCT_PACKAGES` will be changing to +just affect target modules, while `PRODUCT_HOST_PACKAGES` is being added for +host modules. + +Functional differences between `PRODUCT_PACKAGES` and `PRODUCT_HOST_PACKAGES`: + +* `PRODUCT_HOST_PACKAGES` does not have `_ENG`/`_DEBUG` variants, as that's a + property of the target, not the host. +* `PRODUCT_HOST_PACKAGES` does not support `LOCAL_MODULE_OVERRIDES`. +* `PRODUCT_HOST_PACKAGES` requires listed modules to exist, and be host + modules. (Unless `ALLOW_MISSING_DEPENDENCIES` is set) + +This is still an active migration, so currently it still uses +`PRODUCT_PACKAGES` to make installation decisions, but verifies that if we used +`PRODUCT_HOST_PACKAGES`, it would trigger installation for all of the same host +packages. This check ignores shared libraries, as those are not normally +necessary in `PRODUCT_*PACKAGES`, and tended to be over-built (especially the +32-bit variants). + +Future changes will switch installation decisions to `PRODUCT_HOST_PACKAGES` +for host modules, error when there's a host-only module in `PRODUCT_PACKAGES`, +and do some further cleanup where `LOCAL_REQUIRED_MODULES` are still merged +between host and target modules with the same name. + +## `*.c.arm` / `*.cpp.arm` deprecation {#file_arm} + +In Android.mk files, you used to be able to change LOCAL_ARM_MODE for each +source file by appending `.arm` to the end of the filename in +`LOCAL_SRC_FILES`. + +Soong does not support this uncommonly used behavior, instead expecting those +files to be split out into a separate static library that chooses `arm` over +`thumb` for the entire library. This must now also be done in Android.mk files. + +## Windows cross-compiles no longer supported in Android.mk + +Modules that build for Windows (our only `HOST_CROSS` OS currently) must now be +defined in `Android.bp` files. + +## `LOCAL_MODULE_TAGS := eng debug` are obsolete {#LOCAL_MODULE_TAGS} + +`LOCAL_MODULE_TAGS` value `eng` and `debug` are now obsolete. They allowed +modules to specify that they should always be installed on `-eng`, or `-eng` +and `-userdebug` builds. This conflicted with the ability for products to +specify which modules should be installed, effectively making it impossible to +build a stripped down product configuration that did not include those modules. + +For the equivalent functionality, specify the modules in `PRODUCT_PACKAGES_ENG` +or `PRODUCT_PACKAGES_DEBUG` in the appropriate product makefiles. + +Core android packages like `su` got added to the list in +`build/make/target/product/base_system.mk`, but for device-specific modules +there are often better base product makefiles to use instead. + +## `USER` deprecation {#USER} + +`USER` will soon be `nobody` in many cases due to the addition of a sandbox +around the Android build. Most of the time you shouldn't need to know the +identity of the user running the build, but if you do, it's available in the +make variable `BUILD_USERNAME` for now. + +Similarly, the `hostname` tool will also be returning a more consistent value +of `android-build`. The real value is available as `BUILD_HOSTNAME`. + +## `BUILD_NUMBER` removal from Android.mk {#BUILD_NUMBER} + +`BUILD_NUMBER` should not be used directly in Android.mk files, as it would +trigger them to be re-read every time the `BUILD_NUMBER` changes (which it does +on every build server build). If possible, just remove the use so that your +builds are more reproducible. If you do need it, use `BUILD_NUMBER_FROM_FILE`: + +``` make +$(LOCAL_BUILT_MODULE): + mytool --build_number $(BUILD_NUMBER_FROM_FILE) -o $@ +``` + +That will expand out to a subshell that will read the current `BUILD_NUMBER` +whenever it's run. It will not re-run your command if the build number has +changed, so incremental builds will have the build number from the last time +the particular output was rebuilt. + +## `DIST_DIR`, `dist_goal`, and `dist-for-goals` {#dist} + +`DIST_DIR` and `dist_goal` are no longer available when reading Android.mk +files (or other build tasks). Always use `dist-for-goals` instead, which takes +a PHONY goal, and a list of files to copy to `$DIST_DIR`. Whenever `dist` is +specified, and the goal would be built (either explicitly on the command line, +or as a dependency of something on the command line), that file will be copied +into `$DIST_DIR`. For example, + +``` make +$(call dist-for-goals,foo,bar/baz) +``` + +will copy `bar/baz` into `$DIST_DIR/baz` when `m foo dist` is run. + +#### Renames during copy + +Instead of specifying just a file, a destination name can be specified, +including subdirectories: + +``` make +$(call dist-for-goals,foo,bar/baz:logs/foo.log) +``` + +will copy `bar/baz` into `$DIST_DIR/logs/foo.log` when `m foo dist` is run. + +## `.PHONY` rule enforcement {#phony_targets} + +There are several new warnings/errors meant to ensure the proper use of +`.PHONY` targets in order to improve the speed and reliability of incremental +builds. + +`.PHONY`-marked targets are often used as shortcuts to provide "friendly" names +for real files to be built, but any target marked with `.PHONY` is also always +considered dirty, needing to be rebuilt every build. This isn't a problem for +aliases or one-off user-requested operations, but if real builds steps depend +on a `.PHONY` target, it can get quite expensive for what should be a tiny +build. + +``` make +...mk:42: warning: PHONY target "out/.../foo" looks like a real file (contains a "/") +``` + +Between this warning and the next, we're requiring that `.PHONY` targets do not +have "/" in them, and real file targets do have a "/". This makes it more +obvious when reading makefiles what is happening, and will help the build +system differentiate these in the future too. + +``` make +...mk:42: warning: writing to readonly directory: "kernel-modules" +``` + +This warning will show up for one of two reasons: + +1. The target isn't intended to be a real file, and should be marked with + `.PHONY`. This would be the case for this example. +2. The target is a real file, but it's outside the output directories. All + outputs from the build system should be within the output directory, + otherwise `m clean` is unable to clean the build, and future builds may not + work properly. + +``` make +...mk:42: warning: real file "out/.../foo" depends on PHONY target "buildbins" +``` + +If the first target isn't intended to be a real file, then it should be marked +with `.PHONY`, which will satisfy this warning. This isn't the case for this +example, as we require `.PHONY` targets not to have '/' in them. + +If the second (PHONY) target is a real file, it may unnecessarily be marked +with `.PHONY`. + +### `.PHONY` and calling other build systems + +One common pattern (mostly outside AOSP) that we've seen hit these warning is +when building with external build systems (firmware, bootloader, kernel, etc). +Those are often marked as `.PHONY` because the Android build system doesn't +have enough dependencies to know when to run the other build system again +during an incremental build. + +We recommend to build these outside of Android, and deliver prebuilts into the +Android tree instead of decreasing the speed and reliability of the incremental +Android build. + +In cases where that's not desired, to preserve the speed of Android +incrementals, over-specifying dependencies is likely a better option than +marking it with `.PHONY`: + +``` make +out/target/.../zImage: $(sort $(shell find -L $(KERNEL_SRCDIR))) + ... +``` + +For reliability, many of these other build systems do not guarantee the same +level of incremental build assurances as the Android Build is attempting to do +-- without custom checks, Make doesn't rebuild objects when CFLAGS change, etc. +In order to fix this, our recommendation is to do clean builds for each of +these external build systems every time anything they rely on changes. For +relatively smaller builds (like the kernel), this may be reasonable as long as +you're not trying to actively debug the kernel. + +## `export` and `unexport` deprecation {#export_keyword} + +The `export` and `unexport` keywords are obsolete, and will throw errors when +used. + +Device specific configuration should not be able to affect common core build +steps -- we're looking at triggering build steps to be invalidated if the set +of environment variables they can access changes. If device specific +configuration is allowed to change those, switching devices with the same +output directory could become significantly more expensive than it already can +be. + +If used during Android.mk files, and later tasks, it is increasingly likely +that they are being used incorrectly. Attempting to change the environment for +a single build step, and instead setting it for hundreds of thousands. + +It is not recommended to just move the environment variable setting outside of +the build (in vendorsetup.sh, or some other configuration script or wrapper). +We expect to limit the environment variables that the build respects in the +future, others will be cleared. (There will be methods to get custom variables +into the build, just not to every build step) + +Instead, write the export commands into the rule command lines themselves: + +``` make +$(intermediates)/generated_output.img: + rm -rf $@ + export MY_ENV_A="$(MY_A)"; make ... +``` + +If you want to set many environment variables, and/or use them many times, +write them out to a script and source the script: + +``` make +envsh := $(intermediates)/env.sh +$(envsh): + rm -rf $@ + echo 'export MY_ENV_A="$(MY_A)"' >$@ + echo 'export MY_ENV_B="$(MY_B)"' >>$@ + +$(intermediates)/generated_output.img: PRIVATE_ENV := $(envsh) +$(intermediates)/generated_output.img: $(envsh) a/b/c/package.sh + rm -rf $@ + source $(PRIVATE_ENV); make ... + source $(PRIVATE_ENV); a/b/c/package.sh ... +``` + +## Implicit make rules are obsolete {#implicit_rules} + +Implicit rules look something like the following: + +``` make +$(TARGET_OUT_SHARED_LIBRARIES)/%_vendor.so: $(TARGET_OUT_SHARED_LIBRARIES)/%.so + ... + +%.o : %.foo + ... +``` + +These can have wide ranging effects across unrelated modules, so they're now obsolete. Instead, use static pattern rules, which are similar, but explicitly match the specified outputs: + +``` make +libs := $(foreach lib,libfoo libbar,$(TARGET_OUT_SHARED_LIBRARIES)/$(lib)_vendor.so) +$(libs): %_vendor.so: %.so + ... + +files := $(wildcard $(LOCAL_PATH)/*.foo) +gen := $(patsubst $(LOCAL_PATH)/%.foo,$(intermediates)/%.o,$(files)) +$(gen): %.o : %.foo + ... +``` + +## Removing '/' from Valid Module Names {#name_slash} + +The build system uses module names in path names in many places. Having an +extra '/' or '../' being inserted can cause problems -- and not just build +breaks, but stranger invalid behavior. + +In every case we've seen, the fix is relatively simple: move the directory into +`LOCAL_MODULE_RELATIVE_PATH` (or `LOCAL_MODULE_PATH` if you're still using it). +If this causes multiple modules to be named the same, use unique module names +and `LOCAL_MODULE_STEM` to change the installed file name: + +``` make +include $(CLEAR_VARS) +LOCAL_MODULE := ver1/code.bin +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/firmware +... +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := ver2/code.bin +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/firmware +... +include $(BUILD_PREBUILT) +``` + +Can be rewritten as: + +``` +include $(CLEAR_VARS) +LOCAL_MODULE := ver1_code.bin +LOCAL_MODULE_STEM := code.bin +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware/ver1 +... +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := ver2_code.bin +LOCAL_MODULE_STEM := code.bin +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware/ver2 +... +include $(BUILD_PREBUILT) +``` + +You just need to make sure that any other references (`PRODUCT_PACKAGES`, +`LOCAL_REQUIRED_MODULES`, etc) are converted to the new names. + +## Valid Module Names {#name} + +We've adopted lexical requirements very similar to [Bazel's +requirements](https://docs.bazel.build/versions/master/build-ref.html#name) for +target names. Valid characters are `a-z`, `A-Z`, `0-9`, and the special +characters `_.+-=,@~`. This currently applies to `LOCAL_PACKAGE_NAME`, +`LOCAL_MODULE`, and `LOCAL_MODULE_SUFFIX`, and `LOCAL_MODULE_STEM*`. + +Many other characters already caused problems if you used them, so we don't +expect this to have a large effect. + +## PATH Tools {#PATH_Tools} + +The build has started restricting the external host tools usable inside the +build. This will help ensure that build results are reproducible across +different machines, and catch mistakes before they become larger issues. + +To start with, this includes replacing the $PATH with our own directory of +tools, mirroring that of the host PATH. The only difference so far is the +removal of the host GCC tools. Anything that is not explicitly in the +configuration as allowed will continue functioning, but will generate a log +message. This is expected to become more restrictive over time. + +The configuration is located in build/soong/ui/build/paths/config.go, and +contains all the common tools in use in many builds. Anything not in that list +will currently print a warning in the `$OUT_DIR/soong.log` file, including the +command and arguments used, and the process tree in order to help locate the +usage. + +In order to fix any issues brought up by these checks, the best way to fix them +is to use tools checked into the tree -- either as prebuilts, or building them +as host tools during the build. + +As a temporary measure, you can set `TEMPORARY_DISABLE_PATH_RESTRICTIONS=true` +in your environment to temporarily turn off the error checks and allow any tool +to be used (with logging). Beware that GCC didn't work well with the interposer +used for logging, so this may not help in all cases. + +## Deprecating / obsoleting envsetup.sh variables in Makefiles + +It is not required to source envsetup.sh before running a build. Many scripts, +including a majority of our automated build systems, do not do so. Make will +transparently make every environment variable available as a make variable. +This means that relying on environment variables only set up in envsetup.sh will +produce different output for local users and scripted users. + +Many of these variables also include absolute path names, which we'd like to +keep out of the generated files, so that you don't need to do a full rebuild if +you move the source tree. + +To fix this, we're marking the variables that are set in envsetup.sh as +deprecated in the makefiles. This will trigger a warning every time one is read +(or written) inside Kati. Once all the warnings have been removed for a +particular variable, we'll switch it to obsolete, and any references will become +errors. + +### envsetup.sh variables with make equivalents + +| instead of | use | +|--------------------------------------------------------------|----------------------| +| OUT {#OUT} | PRODUCT_OUT | +| ANDROID_HOST_OUT {#ANDROID_HOST_OUT} | HOST_OUT | +| ANDROID_PRODUCT_OUT {#ANDROID_PRODUCT_OUT} | PRODUCT_OUT | +| ANDROID_HOST_OUT_TESTCASES {#ANDROID_HOST_OUT_TESTCASES} | HOST_OUT_TESTCASES | +| ANDROID_TARGET_OUT_TESTCASES {#ANDROID_TARGET_OUT_TESTCASES} | TARGET_OUT_TESTCASES | + +All of the make variables may be relative paths from the current directory, or +absolute paths if the output directory was specified as an absolute path. If you +need an absolute variable, convert it to absolute during a rule, so that it's +not expanded into the generated ninja file: + +``` make +$(PRODUCT_OUT)/gen.img: my/src/path/gen.sh + export PRODUCT_OUT=$$(cd $(PRODUCT_OUT); pwd); cd my/src/path; ./gen.sh -o $${PRODUCT_OUT}/gen.img +``` + +### ANDROID_BUILD_TOP {#ANDROID_BUILD_TOP} + +In Android.mk files, you can always assume that the current directory is the +root of the source tree, so this can just be replaced with '.' (which is what +$TOP is hardcoded to), or removed entirely. If you need an absolute path, see +the instructions above. + +### Stop using PATH directly {#PATH} + +This isn't only set by envsetup.sh, but it is modified by it. Due to that it's +rather easy for this to change between different shells, and it's not ideal to +reread the makefiles every time this changes. + +In most cases, you shouldn't need to touch PATH at all. When you need to have a +rule reference a particular binary that's part of the source tree or outputs, +it's preferrable to just use the path to the file itself (since you should +already be adding that as a dependency). + +Depending on the rule, passing the file path itself may not be feasible due to +layers of unchangable scripts/binaries. In that case, be sure to add the +dependency, but modify the PATH within the rule itself: + +``` make +$(TARGET): myscript my/path/binary + PATH=my/path:$$PATH myscript -o $@ +``` + +### Stop using PYTHONPATH directly {#PYTHONPATH} + +Like PATH, this isn't only set by envsetup.sh, but it is modified by it. Due to +that it's rather easy for this to change between different shells, and it's not +ideal to reread the makefiles every time. + +The best solution here is to start switching to Soong's python building support, +which packages the python interpreter, libraries, and script all into one file +that no longer needs PYTHONPATH. See fontchain_lint for examples of this: + +* [external/fonttools/Lib/fontTools/Android.bp] for python_library_host +* [frameworks/base/Android.bp] for python_binary_host +* [frameworks/base/data/fonts/Android.mk] to execute the python binary + +If you still need to use PYTHONPATH, do so within the rule itself, just like +path: + +``` make +$(TARGET): myscript.py $(sort $(shell find my/python/lib -name '*.py')) + PYTHONPATH=my/python/lib:$$PYTHONPATH myscript.py -o $@ +``` +### Stop using PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE directly {#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE} + +Specify Framework Compatibility Matrix Version in device manifest by adding a `target-level` +attribute to the root element ``. If `PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE` +is 26 or 27, you can add `"target-level"="1"` to your device manifest instead. + +### Stop using USE_CLANG_PLATFORM_BUILD {#USE_CLANG_PLATFORM_BUILD} + +Clang is the default and only supported Android compiler, so there is no reason +for this option to exist. + +### Other envsetup.sh variables {#other_envsetup_variables} + +* ANDROID_TOOLCHAIN +* ANDROID_TOOLCHAIN_2ND_ARCH +* ANDROID_DEV_SCRIPTS +* ANDROID_EMULATOR_PREBUILTS +* ANDROID_PRE_BUILD_PATHS + +These are all exported from envsetup.sh, but don't have clear equivalents within +the makefile system. If you need one of them, you'll have to set up your own +version. + + +[build/soong/Changes.md]: https://android.googlesource.com/platform/build/soong/+/master/Changes.md +[build/soong/docs/best_practices.md#headers]: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers +[external/fonttools/Lib/fontTools/Android.bp]: https://android.googlesource.com/platform/external/fonttools/+/master/Lib/fontTools/Android.bp +[frameworks/base/Android.bp]: https://android.googlesource.com/platform/frameworks/base/+/master/Android.bp +[frameworks/base/data/fonts/Android.mk]: https://android.googlesource.com/platform/frameworks/base/+/master/data/fonts/Android.mk +[hardware/interfaces/health/1.0/README.md]: https://android.googlesource.com/platform/hardware/interfaces/+/master/health/1.0/README.md +[hardware/interfaces/health/2.1/README.md]: https://android.googlesource.com/platform/hardware/interfaces/+/master/health/2.1/README.md diff --git a/make/CleanSpec.mk b/make/CleanSpec.mk new file mode 100644 index 0000000..957da92 --- /dev/null +++ b/make/CleanSpec.mk @@ -0,0 +1,778 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmediaplayerservice_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmedia_jni_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_omx_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/android-info.txt) +$(call add-clean-step, find $(PRODUCT_OUT) -name "*.apk" | xargs rm) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/*/LINKED) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/*.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/*.so) +$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/iself) +$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/lsd) +$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/apriori) +$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/isprelinked) +$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/soslim) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/*.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/*.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/YouTube*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_omx_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/librtp_jni_intermediates) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/android-info.txt) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/JAVA_LIBRARIES/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libbcinfo_intermediates) + +# ICS MR2!!!!!!!!!!!! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libbcinfo_intermediates) + +# WAIT, I MEAN JELLY BEAN!!!!!!!!!!!! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Changing where ro.carrier value is instantiated for system/build.prop +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Now we switched to build against Mac OS X SDK 10.6 +$(call add-clean-step, rm -rf $(OUT_DIR)/host/darwin-x86/obj) + +$(call add-clean-step, rm -f $(OUT_DIR)/versions_checked.mk) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) + +# JB MR2!!!!!!! AND *NO*, THIS WILL NOT BE K-WHATEVER. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Start of "K" development! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# GCC 4.7 +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) + +# Wait, back to some JB development! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# And on to KLP... +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# KLP now based off API 18. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# Clean up around the /system/app -> /system/priv-app migration +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) + +# Clean up old location of generated Java files from aidl +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src) + +# Clean up ApplicationsProvider which is being removed. +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ApplicationsProvider_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/ApplicationsProvider.apk) + +# Clean up Moto OMA DM client which isn't ready yet. +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin.dev_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin.diagmon_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.pluginhelper_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.service.api_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/DMService_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SprintDM_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DMService.apk) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/SprintDM.apk) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/omadm) + +# GCC 4.8 +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) + +# KLP I mean KitKat now API 19. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# 4.4.1 +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# 4.4.2 +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# "L" and beyond. +# Make libart the default runtime +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Rename persist.sys.dalvik.vm.lib to allow new default +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# KKWT development +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# L development +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# L development +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# Add ro.product.cpu.abilist{32,64} to build.prop. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Adding dalvik.vm.dex2oat-flags to eng builds +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Switching the x86 emulator over to a 64 bit primary zygote. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) + +# Rename persist.sys.dalvik.vm.lib.1 to allow new default +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Switching PRODUCT_RUNTIMES default for some devices +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Switching to 32-bit-by-default host multilib build +$(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES)) + +# KKWT has become API 20 +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# ims-common.jar added to BOOTCLASSPATH +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/init.environ.rc_intermediates) + +# Change ro.zygote for core_64_bit.mk from zygote32_64 to zygote64_32 +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# Adding dalvik.vm.dex2oat-Xms, dalvik.vm.dex2oat-Xmx +# dalvik.vm.image-dex2oat-Xms, and dalvik.vm.image-dex2oat-Xmx +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system) + +# Switch host builds to Clang by default +$(call add-clean-step, rm -rf $(OUT_DIR)/host) + +# Adding dalvik.vm.dex2oat-filter +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# API 21? +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# API 21! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# API 22! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# Move to libc++ as the default STL. +$(call add-clean-step, rm -rf $(OUT_DIR)) + +# dex2oat instruction-set changes +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# Make GNU++11 the default standard version. This requires a cleanspec because +# char16_t/char32_t will be real types now instead of typedefs, which means +# an ABI change since the names will mangle differently. +$(call add-clean-step, rm -rf $(OUT_DIR)) + +# 5.1! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# Remove ro.product.locale.language/country and add ro.product.locale +# instead. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# On to MNC +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# Adding dalvik.vm.usejit +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# Rename dalvik.vm.usejit to debug.dalvik.vm.usejit +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# Revert rename dalvik.vm.usejit to debug.dalvik.vm.usejit +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# Change from interpret-only to verify-at-runtime. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) + +# New York, New York! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# 23 is becoming alive!!! +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) + +# Change PLATFORM_VERSION from NYC to N +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) + +# $(PRODUCT_OUT)/recovery/root/sdcard goes from symlink to folder. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sdcard) + +# Add BOARD_USES_SYSTEM_OTHER_ODEX +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/*) + +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/previous_overlays.txt) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/current_packages.txt) + +$(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES)/include) + +$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) +$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/previous_gen_java_config.mk) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/current_gen_java_config.mk) + +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*/package-res.apk) +$(call add-clean-step, rm -rf $(TARGET_OUT_INTERMEDIATES)/APPS/*/package-res.apk) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) + +$(call add-clean-step, rm -rf $(HOST_OUT_TESTCASES)) +$(call add-clean-step, rm -rf $(TARGET_OUT_TESTCASES)) + +$(call add-clean-step, rm -rf $(TARGET_OUT_ETC)/init) + +# Libraries are moved from {system|vendor}/lib to ./lib/framework, ./lib/vndk, etc. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/lib*) + +# Revert that move +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/lib*) + +# Sanitized libraries now live in a different location. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/lib*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/vendor/lib*) + +# Soong module variant change, remove obsolete intermediates +$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates) + +# Version checking moving to Soong +$(call add-clean-step, rm -rf $(OUT_DIR)/versions_checked.mk) + +# Vendor tests were being installed into /vendor/bin accidentally +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/nativetest*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/nativetest*) + +# Jack is no longer the default compiler, remove the intermediates +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*/classes*.jack) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*/jack*) + +# Move adbd from $(PRODUCT_OUT)/root/sbin to $(PRODUCT_OUT)/system/bin +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/adbd) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/sbin/adbd) + +# Soong linux -> linux_glibc rename +$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -name 'linux_x86*' | xargs rm -rf) +$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -name 'linux_common*' | xargs rm -rf) + +# Remove old aidl/logtags files that may be in the generated source directory +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*_intermediates/src) +$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/*/*_intermediates/java-source-list) +$(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/*/*_intermediates/src) +$(call add-clean-step, rm -f $(OUT_DIR)/host/common/obj/*/*_intermediates/java-source-list) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*/flat-res) + +# Remove old VNDK directories without version +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk-sp) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/vndk) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/vndk-sp) + +# Remove old dex output directories +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/with-local/) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/no-local/) +$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/with-local/) +$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/no-local/) + +# Remove legacy VINTF metadata files +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/manifest.xml) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/manifest.xml) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/manifest.xml) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/compatibility_matrix.xml) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/compatibility_matrix.xml) + +# Remove DisplayCutoutEmulation overlays +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutoutEmulationWide) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutoutEmulationNarrow) + +# Remove obsolete intermedates src files +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/src/RenderScript.stamp*) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/java-source-list) +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/java-source-list) +$(call add-clean-step, rm -rf $(OUT_DOCS)/*-timestamp) + +$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/APPS/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/JAVA_LIBRARIES/*_intermediates/src) +$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/APPS/*_intermediates/java-source-list) +$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/JAVA_LIBRARIES/*_intermediates/java-source-list) + +# Remove stale init.noenforce.rc +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/gsi/init.noenforce.rc) + +# Clean up Launcher3 which has been replaced with Launcher3QuickStep +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/Launcher3) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Launcher3) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Launcher3_intermediates) + +# Remove old merged AndroidManifest.xml location +$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/AndroidManifest.xml) + +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "vr_hwc*" -print0 | xargs -0 rm -f) + +$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/system/vold) + +# Remove product-services related files / images +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "*product-services*" -print0 | xargs -0 rm -rf) +$(call add-clean-step, find $(PRODUCT_OUT) -type d -name "*product-services*" -print0 | xargs -0 rm -rf) +$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "*product-services*" -print0 | xargs -0 rm -rf) + +# Remove obsolete recovery etc files +$(call add-clean-step, rm -rf $(TARGET_RECOVERY_ROOT_OUT)/etc) + +# Remove *_OUT_INTERMEDIATE_LIBRARIES +$(call add-clean-step, rm -rf $(addsuffix /lib,\ +$(HOST_OUT_INTERMEDIATES) $(2ND_HOST_OUT_INTERMEDIATES) \ +$(HOST_CROSS_OUT_INTERMEDIATES) $(2ND_HOST_CROSS_OUT_INTERMEDIATES) \ +$(TARGET_OUT_INTERMEDIATES) $(2ND_TARGET_OUT_INTERMEDIATES))) + +# Remove strip.sh intermediates to save space +$(call add-clean-step, find $(OUT_DIR) \( -name "*.so.debug" -o -name "*.so.dynsyms" -o -name "*.so.funcsyms" -o -name "*.so.keep_symbols" -o -name "*.so.mini_debuginfo.xz" \) -print0 | xargs -0 rm -f) + +# Clean up old ninja files +$(call add-clean-step, rm -f $(OUT_DIR)/build-*-dist*.ninja) + +$(call add-clean-step, rm -f $(HOST_OUT)/*ts/host-libprotobuf-java-*.jar) + +$(call add-clean-step, find $(OUT_DIR)/target/product/mainline_arm64/system -type f -name "*.*dex" -print0 | xargs -0 rm -f) + +# Clean up aidegen +$(call add-clean-step, rm -f $(HOST_OUT)/bin/aidegen) + +# Remove perfprofd +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/perfprofd) + +# Remove incorrectly created directories in the source tree +$(call add-clean-step, find system/app system/priv-app system/framework system_other -depth -type d -print0 | xargs -0 rmdir) +$(call add-clean-step, rm -f .d) + +# Remove obsolete apps +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) + +# Remove corrupt generated rule due to using toybox's sed +$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/system/core/init/generated_stub_builtin_function_map) + +# Clean up core JNI libraries moved to runtime apex +$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libjavacore.so) +$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libopenjdk.so) +$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libexpat.so) + +# Merge product_services into product +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services) + +# Clean up old location of hiddenapi files +$(call add-clean-step, rm -f $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi*) + +# Clean up previous default location of RROs +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay) + +# Remove ART artifacts installed only by modules `art-runtime` and +# `art-tools` in /system on target. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm32) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm64) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dex2oat) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dex2oatd) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexdiag) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexdump) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexlist) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexoptanalyzer) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexoptanalyzerd) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/oatdump) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/profman) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/profmand) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libadbconnection.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libadbconnectiond.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-compiler.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd-compiler.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-dexlayout.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd-dexlayout.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-disassembler.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartbase.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartbased.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfiled.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile_external.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile_support.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdt_fd_forward.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdt_socket.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libjdwp.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libnpt.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkd.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvm.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmd.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmti.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmtid.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libprofile.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libprofiled.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libtombstoned_client.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libvixl.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libvixld.so) + +# Clean up old location of dexpreopted boot jars +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/dex_bootjars) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/dex_bootjars_input) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libnpt.so) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*) + +# Clean up old testcase files +$(call add-clean-step, rm -rf $(TARGET_OUT_TESTCASES)/*) +$(call add-clean-step, rm -rf $(HOST_OUT_TESTCASES)/*) +$(call add-clean-step, rm -rf $(HOST_CROSS_OUT_TESTCASES)/*) +$(call add-clean-step, rm -rf $(TARGET_OUT_DATA)/*) +$(call add-clean-step, rm -rf $(HOST_OUT)/vts/*) +$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar) + +# Clean up old location of system_other.avbpubkey +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/security/avb/) + +# Clean up bufferhub files +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/hw/android.frameworks.bufferhub@1.0-service) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/android.frameworks.bufferhub@1.0-service.rc) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/super.img) + +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "generated_*_image_info.txt" -print0 | xargs -0 rm -f) + +# Clean up libicuuc.so and libicui18n.so +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libicu*) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl) + +# Clean up adb_debug.propr +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/adb_debug.prop) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libjavacrypto.so) + +# Clean up old verity tools. +$(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/BootSignature.jar) +$(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/VeritySigner.jar) +$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/build_verity_metadata.py) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libc_malloc*) + +# Clean up old location of soft OMX plugins +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libstagefright_soft*) + +# Move odm build.prop to /odm/etc/. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/odm/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/odm/build.prop) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex) + +# Remove libcameraservice and libcamera_client from base_system +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libcameraservice.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libcamera_client.so) + +# Move product and system_ext to root for emulators +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext) + +# link_type and jni_link_type files are no longer needed +$(call add-clean-step, find $(OUT_DIR) -type f -name "*link_type" -print0 | xargs -0 rm -f) + +# import_includes and export_includes files are no longer needed +$(call add-clean-step, find $(OUT_DIR) -type f -name "import_includes" -o -name "export_includes" -print0 | xargs -0 rm -f) + +# Recreate product and system_ext partitions for emulator +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*product*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*system_ext*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext) + +# Move GSI-specific files from /system to /system/system_ext +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/init.gsi.rc) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/config/) + +# Move fuzz targets from /data/fuzz/* to /data/fuzz//* for device, and +# /fuzz/* to /fuzz//* on host. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/fuzz/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/data/fuzz/*) +$(call add-clean-step, rm -rf $(HOST_OUT)/fuzz/*) +$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/host/*/fuzz/*) + +# Change file layout of system_other +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex) + +# Migrate preopt files to system_other for some devices +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/*/*app/*/oat) + +# Migrate preopt files from system_other for some devices +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other) + +# Remove Android Core Library artifacts from the system partition, now +# that they live in the ART APEX (b/142944799). +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar) + +# Remove symlinks for VNDK apexes +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*) + +# Switch to symlinks for VNDK libs +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*) + +# Remove Android Core Library artifacts from the system partition +# again, as the original change removing them was reverted. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar) + +# Remove cas@1.1 from the vendor partition +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.cas@1.1*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.cas@1.1*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/android.hardware.cas@1.1*) + +# Remove com.android.cellbroadcast apex for Go devices +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.cellbroadcast.apex) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex/com.android.cellbroadcast) + +# Remove CellBroadcastLegacyApp for Go devices +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CellBroadcastLegacyApp) + +# Remove MediaProvider after moving into APEX +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/MediaProvider) + +# The core image variant has been renamed to "" +$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name "android_*_core*" -print0 | xargs -0 rm -rf) + +# Remove 'media' command +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/media) + +# Remove CtsShim apks from system partition, since the have been moved inside +# the cts shim apex. Also remove the cts shim apex prebuilt since it has been +# removed in flattened apexs configurations. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CtsShimPrivPrebuilt) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CtsShimPrebuilt) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.apex.cts.shim.apex) + +# Remove vendor and recovery variants, the directory name has changed. +$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name "android_*_recovery*" -print0 | xargs -0 rm -rf) +$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name "android_*_vendor*" -print0 | xargs -0 rm -rf) + +# Remove PermissionController after moving into APEX +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/*PermissionController) + +# Clean up VTS-Core and VTS10 related artifacts. +$(call add-clean-step, rm -rf $(HOST_OUT)/vts-core/*) +$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-core-tradefed.jar) +$(call add-clean-step, rm -rf $(HOST_OUT)/vts10/*) +$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts10-tradefed.jar) +# Clean up VTS again as VTS-Core will be renamed to VTS +$(call add-clean-step, rm -rf $(HOST_OUT)/vts/*) +$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/prop.default) + +# Workaround for Soong not being able to rebuild the host binary if its +# JNI dependencies change: b/170389375 +$(call add-clean-step, rm -rf $(OUT_DIR)/soong/host/*/lib*/libconscrypt_openjdk_jni.so) +# vendor-ramdisk renamed to vendor_ramdisk +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor-ramdisk) + +# Common R directory has been removed. +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R) + +# Most of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES +$(call add-clean-step, rm -rf $(SOONG_HOST_OUT)) + +# More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES +$(call add-clean-step, rm -rf $(SOONG_HOST_OUT)) + +# More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES +$(call add-clean-step, rm -rf $(SOONG_HOST_OUT)) + +# Last of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES +# Don't use SOONG_HOST_OUT, it is now an alias for HOST_OUT. +$(call add-clean-step, rm -rf $(OUT_DIR)/soong/host) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/make/Deprecation.md b/make/Deprecation.md new file mode 100644 index 0000000..74b54fa --- /dev/null +++ b/make/Deprecation.md @@ -0,0 +1,69 @@ +# Deprecation of Make + +We've made significant progress converting AOSP from Make to Soong (Android.mk +to Android.bp), and we're ready to start turning off pieces of Make. If you +have any problems converting, please contact us via: + +* The [android-building@googlegroups.com] group. +* Our [public bug tracker](https://issuetracker.google.com/issues/new?component=381517). +* Or privately through your existing contacts at Google. + +## Status + +[build/make/core/deprecation.mk] is the source of truth, but for easy browsing: + +| Module type | State | +| -------------------------------- | --------- | +| `BUILD_AUX_EXECUTABLE` | Obsolete | +| `BUILD_AUX_STATIC_LIBRARY` | Obsolete | +| `BUILD_COPY_HEADERS` | Error | +| `BUILD_HOST_EXECUTABLE` | Error | +| `BUILD_HOST_FUZZ_TEST` | Obsolete | +| `BUILD_HOST_NATIVE_TEST` | Obsolete | +| `BUILD_HOST_SHARED_LIBRARY` | Error | +| `BUILD_HOST_SHARED_TEST_LIBRARY` | Obsolete | +| `BUILD_HOST_STATIC_LIBRARY` | Error | +| `BUILD_HOST_STATIC_TEST_LIBRARY` | Obsolete | +| `BUILD_HOST_TEST_CONFIG` | Obsolete | +| `BUILD_NATIVE_BENCHMARK` | Obsolete | +| `BUILD_SHARED_TEST_LIBRARY` | Obsolete | +| `BUILD_STATIC_TEST_LIBRARY` | Obsolete | +| `BUILD_TARGET_TEST_CONFIG` | Obsolete | +| `BUILD_*` | Available | + +## Module Type Deprecation Process + +We'll be turning off `BUILD_*` module types as all of the users are removed +from AOSP (and Google's internal trees). The process will go something like +this, using `BUILD_PACKAGE` as an example: + +* Prerequisite: all common users of `BUILD_PACKAGE` have been removed (some + device-specific ones may remain). +* `BUILD_PACKAGE` will be moved from `AVAILABLE_BUILD_MODULE_TYPES` to + `DEFAULT_WARNING_BUILD_MODULE_TYPES` in [build/make/core/deprecation.mk]. This + will make referring to `BUILD_PACKAGE` a warning. +* Any devices that still have warnings will have + `BUILD_BROKEN_USES_BUILD_PACKAGE := true` added to their `BoardConfig.mk`. +* `BUILD_PACKAGE` will be switched from `DEFAULT_WARNING_BUILD_MODULE_TYPES` to + `DEFAULT_ERROR_BUILD_MODULE_TYPES`, which will turn referring to + `BUILD_PACKAGE` into an error unless the device has overridden it. +* At some later point, after all devices in AOSP no longer set + `BUILD_BROKEN_USES_BUILD_PACKAGE`, `BUILD_PACKAGE` will be moved from + `DEFAULT_ERROR_BUILD_MODULE_TYPES` to `OBSOLETE_BUILD_MODULE_TYPES` and the + code will be removed. It will no longer be possible to use `BUILD_PACKAGE`. + +In most cases, we expect module types to stay in the default warning state for +about two weeks before becoming an error by default. Then it will spend some +amount of time in the default error state before moving to obsolete -- we'll +try and keep that around for a while, but other development may cause those to +break, and the fix may to be to obsolete them. There is no expectation that the +`BUILD_BROKEN_USES_BUILD_*` workarounds will work in a future release, it's a +short-term workaround. + +Just to be clear, the above process will happen on the AOSP master branch. So +if you're following Android releases, none of the deprecation steps will be in +Android Q, and the 2020 release will have jumped directly to the end for many +module types. + +[android-building@googlegroups.com]: https://groups.google.com/forum/#!forum/android-building +[build/make/core/deprecation.mk]: /core/deprecation.mk diff --git a/make/METADATA b/make/METADATA new file mode 100644 index 0000000..44781a7 --- /dev/null +++ b/make/METADATA @@ -0,0 +1,8 @@ +third_party { + license_note: "would be NOTICE save for GPL in:\n" + " core/LINUX_KERNEL_COPYING\n" + " tools/droiddoc/templates-pdk/assets/jquery-1.6.2.min.js\n" + " tools/droiddoc/templates-pdk/assets/jquery-history.js\n" + " tools/droiddoc/templates-pdk/assets/jquery-resizable.min.js" + license_type: RESTRICTED +} diff --git a/make/OWNERS b/make/OWNERS new file mode 100644 index 0000000..4cac0f5 --- /dev/null +++ b/make/OWNERS @@ -0,0 +1 @@ +include platform/build/soong:/OWNERS diff --git a/make/PREUPLOAD.cfg b/make/PREUPLOAD.cfg new file mode 100644 index 0000000..ce75150 --- /dev/null +++ b/make/PREUPLOAD.cfg @@ -0,0 +1,2 @@ +[Hook Scripts] +do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT} diff --git a/make/README.md b/make/README.md new file mode 100644 index 0000000..47809a9 --- /dev/null +++ b/make/README.md @@ -0,0 +1,23 @@ +# Android Make Build System + +This is the Makefile-based portion of the Android Build System. + +For documentation on how to run a build, see [Usage.txt](Usage.txt) + +For a list of behavioral changes useful for Android.mk writers see +[Changes.md](Changes.md) + +For an outdated reference on Android.mk files, see +[build-system.html](/core/build-system.html). Our Android.mk files look similar, +but are entirely different from the Android.mk files used by the NDK build +system. When searching for documentation elsewhere, ensure that it is for the +platform build system -- most are not. + +This Makefile-based system is in the process of being replaced with [Soong], a +new build system written in Go. During the transition, all of these makefiles +are read by [Kati], and generate a ninja file instead of being executed +directly. That's combined with a ninja file read by Soong so that the build +graph of the two systems can be combined and run as one. + +[Kati]: https://github.com/google/kati +[Soong]: https://android.googlesource.com/platform/build/soong/+/master diff --git a/make/Usage.txt b/make/Usage.txt new file mode 100644 index 0000000..ea4788a --- /dev/null +++ b/make/Usage.txt @@ -0,0 +1,88 @@ +Android build system usage: + +m [-j] [] [=...] + + +Ways to specify what to build: + The common way to specify what to build is to set that information in the + environment via: + + # Set up the shell environment. + source build/envsetup.sh # Run "hmm" after sourcing for more info + # Select the device and variant to target. If no argument is given, it + # will list choices and prompt. + lunch [-] # Selects the device and variant to target. + # Invoke the configured build. + m [] [] [=...] + + is the device that the created image is intended to be run on. + This is saved in the shell environment as $TARGET_PRODUCT by `lunch`. + is one of "user", "userdebug", or "eng", and controls the + amount of debugging to be added into the generated image. + This gets saved in the shell environment as $TARGET_BUILD_VARIANT by + `lunch`. + + Each of , , and = is optional. + If no targets are specified, the build system will build the images + for the configured product and variant. + + A target may be a file path. For example, out/host/linux-x86/bin/adb . + Note that when giving a relative file path as a target, that path is + interpreted relative to the root of the source tree (rather than relative + to the current working directory). + + A target may also be any other target defined within a Makefile. Run + `m help` to view the names of some common targets. + + To view the modules and targets defined in a particular directory, look for: + files named *.mk (most commonly Android.mk) + these files are defined in Make syntax + files named Android.bp + these files are defined in Blueprint syntax + + During a build, a few log files are generated in ${OUT} (or ${DIST_DIR}/logs + for dist builds): + + verbose.log.gz + every command run, along with its outputs. This is similar to the + previous `m showcommands` option. + error.log + list of actions that failed during the build, and their outputs. + soong.log + verbose debug information from soong_ui + + For now, the full (extremely large) compiled list of targets can be found + (after running the build once), split among these two files: + + ${OUT}/build-*.ninja + ${OUT}/soong/build.ninja + + If you find yourself interacting with these files, you are encouraged to + provide a more convenient tool for browsing targets, and to mention the + tool here. + +Targets that adjust an existing build: + dist Copy into ${DIST_DIR} the portion of the build + that must be distributed + +Flags + -j Run processes at once + -j Autodetect the number of processes to run at once, + and run that many + +Variables + Variables can either be set in the surrounding shell environment or can be + passed as command-line arguments. For example: + export I_AM_A_SHELL_VAR=1 + I_AM_ANOTHER_SHELL_VAR=2 m droid I_AM_A_MAKE_VAR=3 + Here are some common variables and their meanings: + TARGET_PRODUCT The to build # as described above + TARGET_BUILD_VARIANT The to build # as described above + DIST_DIR The directory in which to place the distribution + artifacts. + OUT_DIR The directory in which to place non-distribution + artifacts. + + There is not yet known a convenient method by which to discover the full + list of supported variables. Please mention it here when there is. + diff --git a/make/banchanHelp.sh b/make/banchanHelp.sh new file mode 100755 index 0000000..eab22e4 --- /dev/null +++ b/make/banchanHelp.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# locate some directories +cd "$(dirname $0)" +SCRIPT_DIR="${PWD}" +cd ../.. +TOP="${PWD}" + +message='usage: banchan ... [|arm|x86|arm64|x86_64] [eng|userdebug|user] + +banchan selects individual APEX modules to be built by the Android build system. +Like "tapas", "banchan" does not request the building of images for a device but +instead configures it for an unbundled build of the given modules, suitable for +installing on any api-compatible device. + +The difference from "tapas" is that "banchan" sets the appropriate products etc +for building APEX modules rather than apps (APKs). + +The module names should match apex{} modules in Android.bp files, typically +starting with "com.android.". + +The product argument should be a product name ending in "_", where +is one of arm, x86, arm64, x86_64. It can also be just an arch, in which case +the standard product for building modules with that architecture is used, i.e. +module_. + +The usage of the other arguments matches that of the rest of the platform +build system and can be found by running `m help`' + +echo "$message" diff --git a/make/buildspec.mk.default b/make/buildspec.mk.default new file mode 100644 index 0000000..b31578a --- /dev/null +++ b/make/buildspec.mk.default @@ -0,0 +1,114 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +###################################################################### +# This is a do-nothing template file. To use it, copy it to a file +# named "buildspec.mk" in the root directory, and uncomment or change +# the variables necessary for your desired configuration. The file +# "buildspec.mk" should never be checked in to source control. +###################################################################### + +# Choose a product to build for. Look in the products directory for ones +# that work. +ifndef TARGET_PRODUCT +#TARGET_PRODUCT:=generic +endif + +# Choose a variant to build. If you don't pick one, the default is eng. +# User is what we ship. Userdebug is that, with a few flags turned on +# for debugging. Eng has lots of extra tools for development. +ifndef TARGET_BUILD_VARIANT +#TARGET_BUILD_VARIANT:=user +#TARGET_BUILD_VARIANT:=userdebug +#TARGET_BUILD_VARIANT:=eng +endif + +# Choose a targeted release. If you don't pick one, the default is the +# soonest future release. +ifndef TARGET_PLATFORM_RELEASE +#TARGET_PLATFORM_RELEASE:=OPR1 +endif + +# Choose additional targets to always install, even when building +# minimal targets like "make droid". This takes simple target names +# like "Browser" or "MyApp", the names used by LOCAL_MODULE or +# LOCAL_PACKAGE_NAME. Modules listed here will always be installed in +# /system, even if they'd usually go in /data. +ifndef CUSTOM_MODULES +#CUSTOM_MODULES:= +endif + +# Set this to debug or release if you care. Otherwise, it defaults to release. +ifndef TARGET_BUILD_TYPE +#TARGET_BUILD_TYPE:=release +endif + +# Uncomment this if you want the host tools built in debug mode. Otherwise +# it defaults to release. +ifndef HOST_BUILD_TYPE +#HOST_BUILD_TYPE:=debug +endif + +# Turn on debugging for selected modules. If DEBUG_MODULE_ is set +# to a non-empty value, the appropriate HOST_/TARGET_CUSTOM_DEBUG_CFLAGS +# will be added to LOCAL_CFLAGS when building the module. +#DEBUG_MODULE_ModuleName:=true + +# Specify the extra CFLAGS to use when building a module whose +# DEBUG_MODULE_ variable is set. Host and device flags are handled +# separately. +#HOST_CUSTOM_DEBUG_CFLAGS:= +#TARGET_CUSTOM_DEBUG_CFLAGS:= + +# Choose additional locales, like "en_US" or "it_IT", to add to any +# built product. Any locales that appear in CUSTOM_LOCALES but not in +# the locale list for the selected product will be added to the end +# of PRODUCT_LOCALES. +ifndef CUSTOM_LOCALES +#CUSTOM_LOCALES:= +endif + +# If you have a special place to put your ouput files, set this, otherwise +# it goes to /out +#OUT_DIR:=/tmp/stuff + +# If you want to always set certain system properties, add them to this list. +# E.g., "ADDITIONAL_BUILD_PROPERTIES += ro.prop1=5 prop2=value" +# This mechanism does not currently support values containing spaces. +#ADDITIONAL_BUILD_PROPERTIES += + +# If you want to reduce the system.img size by several meg, and are willing to +# lose access to CJK (and other) character sets, define NO_FALLBACK_FONT:=true +ifndef NO_FALLBACK_FONT +#NO_FALLBACK_FONT:=true +endif + +# OVERRIDE_RUNTIMES allows you to locally override PRODUCT_RUNTIMES. +# +# To only build ART, use "runtime_libart_default" +# To use Dalvik but also include ART, use "runtime_libdvm_default runtime_libart" +# To use ART but also include Dalvik, use "runtime_libart_default runtime_libdvm" +ifndef OVERRIDE_RUNTIMES +#OVERRIDE_RUNTIMES:=runtime_libart_default +#OVERRIDE_RUNTIMES:=runtime_libdvm_default runtime_libart +#OVERRIDE_RUNTIMES:=runtime_libart_default runtime_libdvm +endif + +# when the build system changes such that this file must be updated, this +# variable will be changed. After you have modified this file with the new +# changes (see buildspec.mk.default), update this to the new value from +# buildspec.mk.default. +BUILD_ENV_SEQUENCE_NUMBER := 13 diff --git a/make/common/core.mk b/make/common/core.mk new file mode 100644 index 0000000..7d505c0 --- /dev/null +++ b/make/common/core.mk @@ -0,0 +1,59 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Only use ANDROID_BUILD_SHELL to wrap around bash. +# DO NOT use other shells such as zsh. +ifdef ANDROID_BUILD_SHELL +SHELL := $(ANDROID_BUILD_SHELL) +else +# Use bash, not whatever shell somebody has installed as /bin/sh +# This is repeated from main.mk, since envsetup.sh runs this file +# directly. +SHELL := /bin/bash +endif + +# Utility variables. +empty := +space := $(empty) $(empty) +comma := , +# Note that make will eat the newline just before endef. +define newline + + +endef +# The pound character "#" +define pound +# +endef +# Unfortunately you can't simply define backslash as \ or \\. +backslash := \a +backslash := $(patsubst %a,%,$(backslash)) + +TOP :=$= . +TOPDIR :=$= + +# Prevent accidentally changing these variables +.KATI_READONLY := SHELL empty space comma newline pound backslash + +# Basic warning/error wrappers. These will be redefined to include the local +# module information when reading Android.mk files. +define pretty-warning +$(warning $(1)) +endef + +define pretty-error +$(error $(1)) +endef diff --git a/make/common/json.mk b/make/common/json.mk new file mode 100644 index 0000000..e376aab --- /dev/null +++ b/make/common/json.mk @@ -0,0 +1,38 @@ +4space :=$= $(space)$(space)$(space)$(space) +invert_bool =$= $(if $(strip $(1)),,true) + +# Converts a list to a JSON list. +# $1: List separator. +# $2: List. +_json_list =$= [$(if $(2),"$(subst $(1),"$(comma)",$(2))")] + +# Converts a space-separated list to a JSON list. +json_list =$= $(call _json_list,$(space),$(1)) + +# Converts a comma-separated list to a JSON list. +csv_to_json_list =$= $(call _json_list,$(comma),$(1)) + +# Adds or removes 4 spaces from _json_indent +json_increase_indent =$= $(eval _json_indent := $$(_json_indent)$$(4space)) +json_decrease_indent =$= $(eval _json_indent := $$(subst _,$$(space),$$(patsubst %____,%,$$(subst $$(space),_,$$(_json_indent))))) + +# 1: Key name +# 2: Value +add_json_val =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)"$$(strip $$(1))": $$(strip $$(2))$$(comma)$$(newline)) +add_json_str =$= $(call add_json_val,$(1),"$(strip $(2))") +add_json_list =$= $(call add_json_val,$(1),$(call json_list,$(patsubst %,%,$(2)))) +add_json_csv =$= $(call add_json_val,$(1),$(call csv_to_json_list,$(strip $(2)))) +add_json_bool =$= $(call add_json_val,$(1),$(if $(strip $(2)),true,false)) +add_json_map =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)"$$(strip $$(1))": {$$(newline))$(json_increase_indent) +add_json_map_anon =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent){$$(newline))$(json_increase_indent) +end_json_map =$= $(json_decrease_indent)$(eval _json_contents := $$(_json_contents)$$(if $$(filter %$$(comma),$$(lastword $$(_json_contents))),__SV_END)$$(_json_indent)},$$(newline)) +add_json_array =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)"$$(strip $$(1))": [$$(newline))$(json_increase_indent) +end_json_array =$= $(json_decrease_indent)$(eval _json_contents := $$(_json_contents)$$(if $$(filter %$$(comma),$$(lastword $$(_json_contents))),__SV_END)$$(_json_indent)],$$(newline)) + +# Clears _json_contents to start a new json file +json_start =$= $(eval _json_contents := {$$(newline))$(eval _json_indent := $$(4space)) + +# Adds the trailing close brace to _json_contents, and removes any trailing commas if necessary +json_end =$= $(eval _json_contents := $$(subst $$(comma)$$(newline)__SV_END,$$(newline),$$(_json_contents)__SV_END}$$(newline))) + +json_contents =$= $(_json_contents) diff --git a/make/common/math.mk b/make/common/math.mk new file mode 100644 index 0000000..0271ea8 --- /dev/null +++ b/make/common/math.mk @@ -0,0 +1,308 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +########################################################### +# Basic math functions for non-negative integers <= 100 +# +# (SDK versions for example) +########################################################### +__MATH_POS_NUMBERS := 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ + 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ + 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \ + 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \ + 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 +__MATH_NUMBERS := 0 $(__MATH_POS_NUMBERS) + +math-error = $(call pretty-error,$(1)) +math-expect := +math-expect-true := +math-expect := +math-expect-error := + +# Run the math tests with: +# make -f ${ANDROID_BUILD_TOP}/build/make/common/math.mk RUN_MATH_TESTS=true +# $(get_build_var CKATI) -f ${ANDROID_BUILD_TOP}//build/make/common/math.mk RUN_MATH_TESTS=true +ifdef RUN_MATH_TESTS + MATH_TEST_FAILURE := + MATH_TEST_ERROR := + math-error = $(if $(MATH_TEST_ERROR),,$(eval MATH_TEST_ERROR:=$(1))) + define math-expect + $(eval got:=$$$1) \ + $(if $(subst $(got),,$(2))$(subst $(2),,$(got))$(MATH_TEST_ERROR), \ + $(if $(MATH_TEST_ERROR),$(warning $(MATH_TEST_ERROR)),$(warning $$$1 '$(got)' != '$(2)')) \ + $(eval MATH_TEST_FAILURE := true)) \ + $(eval MATH_TEST_ERROR :=) \ + $(eval got:=) + endef + math-expect-true = $(call math-expect,$(1),true) + math-expect-false = $(call math-expect,$(1),) + + define math-expect-error + $(eval got:=$$$1) \ + $(if $(subst $(MATH_TEST_ERROR),,$(2))$(subst $(2),,$(MATH_TEST_ERROR)), \ + $(warning '$(MATH_TEST_ERROR)' != '$(2)') \ + $(eval MATH_TEST_FAILURE := true)) \ + $(eval MATH_TEST_ERROR :=) \ + $(eval got:=) + endef +endif + +# Returns true if $(1) is a non-negative integer <= 100, otherwise returns nothing. +define math_is_number +$(strip \ + $(if $(1),,$(call math-error,Argument missing)) \ + $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \ + $(if $(filter $(1),$(__MATH_NUMBERS)),true)) +endef + +define math_is_zero +$(strip \ + $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \ + $(if $(filter 0,$(1)),true)) +endef + +$(call math-expect-true,(call math_is_number,0)) +$(call math-expect-true,(call math_is_number,2)) +$(call math-expect-false,(call math_is_number,foo)) +$(call math-expect-false,(call math_is_number,-1)) +$(call math-expect-error,(call math_is_number,1 2),Multiple words in a single argument: 1 2) +$(call math-expect-error,(call math_is_number,no 2),Multiple words in a single argument: no 2) + +$(call math-expect-true,(call math_is_zero,0)) +$(call math-expect-false,(call math_is_zero,1)) +$(call math-expect-false,(call math_is_zero,foo)) +$(call math-expect-error,(call math_is_zero,1 2),Multiple words in a single argument: 1 2) +$(call math-expect-error,(call math_is_zero,no 2),Multiple words in a single argument: no 2) + +define _math_check_valid +$(if $(call math_is_number,$(1)),,$(call math-error,Only non-negative integers <= 100 are supported (not $(1)))) +endef + +$(call math-expect,(call _math_check_valid,0)) +$(call math-expect,(call _math_check_valid,1)) +$(call math-expect,(call _math_check_valid,100)) +$(call math-expect-error,(call _math_check_valid,-1),Only non-negative integers <= 100 are supported (not -1)) +$(call math-expect-error,(call _math_check_valid,101),Only non-negative integers <= 100 are supported (not 101)) +$(call math-expect-error,(call _math_check_valid,),Argument missing) +$(call math-expect-error,(call _math_check_valid,1 2),Multiple words in a single argument: 1 2) + +# return a list containing integers ranging from [$(1),$(2)] +define int_range_list +$(strip \ + $(call _math_check_valid,$(1))$(call _math_check_valid,$(2)) \ + $(if $(call math_is_zero,$(1)),0)\ + $(wordlist $(if $(call math_is_zero,$(1)),1,$(1)),$(2),$(__MATH_POS_NUMBERS))) +endef + +$(call math-expect,(call int_range_list,0,1),0 1) +$(call math-expect,(call int_range_list,1,1),1) +$(call math-expect,(call int_range_list,1,2),1 2) +$(call math-expect,(call int_range_list,2,1),) +$(call math-expect-error,(call int_range_list,1,101),Only non-negative integers <= 100 are supported (not 101)) + + +# Returns the greater of $1 or $2. +# If $1 or $2 is not a positive integer <= 100, then an error is generated. +define math_max +$(strip $(call _math_check_valid,$(1)) $(call _math_check_valid,$(2)) \ + $(lastword $(filter $(1) $(2),$(__MATH_NUMBERS)))) +endef + +# Returns the lesser of $1 or $2. +define math_min +$(strip $(call _math_check_valid,$(1)) $(call _math_check_valid,$(2)) \ + $(firstword $(filter $(1) $(2),$(__MATH_NUMBERS)))) +endef + +$(call math-expect-error,(call math_max),Argument missing) +$(call math-expect-error,(call math_max,1),Argument missing) +$(call math-expect-error,(call math_max,1 2,3),Multiple words in a single argument: 1 2) +$(call math-expect-error,(call math_min,1,2 3),Multiple words in a single argument: 2 3) +$(call math-expect,(call math_max,0,1),1) +$(call math-expect,(call math_max,1,0),1) +$(call math-expect,(call math_max,1,1),1) +$(call math-expect,(call math_max,5,42),42) +$(call math-expect,(call math_max,42,5),42) +$(call math-expect,(call math_min,0,1),0) +$(call math-expect,(call math_min,1,0),0) +$(call math-expect,(call math_min,1,1),1) +$(call math-expect,(call math_min,7,32),7) +$(call math-expect,(call math_min,32,7),7) + +define math_gt_or_eq +$(if $(filter $(1),$(call math_max,$(1),$(2))),true) +endef + +define math_gt +$(if $(call math_gt_or_eq,$(2),$(1)),,true) +endef + +define math_lt +$(if $(call math_gt_or_eq,$(1),$(2)),,true) +endef + +$(call math-expect-true,(call math_gt_or_eq, 2, 1)) +$(call math-expect-true,(call math_gt_or_eq, 1, 1)) +$(call math-expect-false,(call math_gt_or_eq, 1, 2)) +$(call math-expect-true,(call math_gt, 4, 3)) +$(call math-expect-false,(call math_gt, 5, 5)) +$(call math-expect-false,(call math_gt, 6, 7)) +$(call math-expect-false,(call math_lt, 1, 0)) +$(call math-expect-false,(call math_lt, 8, 8)) +$(call math-expect-true,(call math_lt, 10, 11)) + +# $1 is the variable name to increment +define inc_and_print +$(strip $(eval $(1) := $($(1)) .)$(words $($(1)))) +endef + +ifdef RUN_MATH_TESTS +a := +$(call math-expect,(call inc_and_print,a),1) +$(call math-expect,(call inc_and_print,a),2) +$(call math-expect,(call inc_and_print,a),3) +$(call math-expect,(call inc_and_print,a),4) +endif + +# Returns the words in $2 that are numbers and are less than $1 +define numbers_less_than +$(strip \ + $(foreach n,$2, \ + $(if $(call math_is_number,$(n)), \ + $(if $(call math_lt,$(n),$(1)), \ + $(n))))) +endef + +$(call math-expect,(call numbers_less_than,0,0 1 2 3),) +$(call math-expect,(call numbers_less_than,1,0 2 1 3),0) +$(call math-expect,(call numbers_less_than,2,0 2 1 3),0 1) +$(call math-expect,(call numbers_less_than,3,0 2 1 3),0 2 1) +$(call math-expect,(call numbers_less_than,4,0 2 1 3),0 2 1 3) +$(call math-expect,(call numbers_less_than,3,0 2 1 3 2),0 2 1 2) + +# Returns the words in $2 that are numbers and are greater or equal to $1 +define numbers_greater_or_equal_to +$(strip \ + $(foreach n,$2, \ + $(if $(call math_is_number,$(n)), \ + $(if $(call math_gt_or_eq,$(n),$(1)), \ + $(n))))) +endef + +$(call math-expect,(call numbers_greater_or_equal_to,4,0 1 2 3),) +$(call math-expect,(call numbers_greater_or_equal_to,3,0 2 1 3),3) +$(call math-expect,(call numbers_greater_or_equal_to,2,0 2 1 3),2 3) +$(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3),2 1 3) +$(call math-expect,(call numbers_greater_or_equal_to,0,0 2 1 3),0 2 1 3) +$(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3 2),2 1 3 2) + +_INT_LIMIT_WORDS := $(foreach a,x x,$(foreach b,x x x x x x x x x x x x x x x x,\ + $(foreach c,x x x x x x x x x x x x x x x x,x x x x x x x x x x x x x x x x))) + +define _int_encode +$(if $(filter $(words x $(_INT_LIMIT_WORDS)),$(words $(wordlist 1,$(1),x $(_INT_LIMIT_WORDS)))),\ + $(call math-error,integer greater than $(words $(_INT_LIMIT_WORDS)) is not supported!),\ + $(wordlist 1,$(1),$(_INT_LIMIT_WORDS))) +endef + +# _int_max returns the maximum of the two arguments +# input: two (x) lists; output: one (x) list +# integer cannot be passed in directly. It has to be converted using _int_encode. +define _int_max +$(subst xx,x,$(join $(1),$(2))) +endef + +# first argument is greater than second argument +# output: non-empty if true +# integer cannot be passed in directly. It has to be converted using _int_encode. +define _int_greater-than +$(filter-out $(words $(2)),$(words $(call _int_max,$(1),$(2)))) +endef + +# first argument equals to second argument +# output: non-empty if true +# integer cannot be passed in directly. It has to be converted using _int_encode. +define _int_equal +$(filter $(words $(1)),$(words $(2))) +endef + +# first argument is greater than or equal to second argument +# output: non-empty if true +# integer cannot be passed in directly. It has to be converted using _int_encode. +define _int_greater-or-equal +$(call _int_greater-than,$(1),$(2))$(call _int_equal,$(1),$(2)) +endef + +define int_plus +$(words $(call _int_encode,$(1)) $(call _int_encode,$(2))) +endef + +$(call math-expect,(call int_plus,0,0),0) +$(call math-expect,(call int_plus,0,1),1) +$(call math-expect,(call int_plus,1,0),1) +$(call math-expect,(call int_plus,1,100),101) +$(call math-expect,(call int_plus,100,100),200) + +define int_subtract +$(strip \ + $(if $(call _int_greater-or-equal,$(call _int_encode,$(1)),$(call _int_encode,$(2))),\ + $(words $(filter-out xx,$(join $(call _int_encode,$(1)),$(call _int_encode,$(2))))),\ + $(call math-error,subtract underflow $(1) - $(2)))) +endef + +$(call math-expect,(call int_subtract,0,0),0) +$(call math-expect,(call int_subtract,1,0),1) +$(call math-expect,(call int_subtract,1,1),0) +$(call math-expect,(call int_subtract,100,1),99) +$(call math-expect,(call int_subtract,200,100),100) +$(call math-expect-error,(call int_subtract,0,1),subtract underflow 0 - 1) + +define int_multiply +$(words $(foreach a,$(call _int_encode,$(1)),$(call _int_encode,$(2)))) +endef + +$(call math-expect,(call int_multiply,0,0),0) +$(call math-expect,(call int_multiply,1,0),0) +$(call math-expect,(call int_multiply,1,1),1) +$(call math-expect,(call int_multiply,100,1),100) +$(call math-expect,(call int_multiply,1,100),100) +$(call math-expect,(call int_multiply,4,100),400) +$(call math-expect,(call int_multiply,100,4),400) + +define int_divide +$(if $(filter 0,$(2)),$(call math-error,division by zero is not allowed!),$(strip \ + $(if $(call _int_greater-or-equal,$(call _int_encode,$(1)),$(call _int_encode,$(2))), \ + $(call int_plus,$(call int_divide,$(call int_subtract,$(1),$(2)),$(2)),1),0))) +endef + +$(call math-expect,(call int_divide,1,1),1) +$(call math-expect,(call int_divide,200,1),200) +$(call math-expect,(call int_divide,200,3),66) +$(call math-expect,(call int_divide,1,2),0) +$(call math-expect-error,(call int_divide,0,0),division by zero is not allowed!) +$(call math-expect-error,(call int_divide,1,0),division by zero is not allowed!) + +ifdef RUN_MATH_TESTS + ifdef MATH_TEST_FAILURE + math-tests: + @echo FAIL + @false + else + math-tests: + @echo PASS + endif + .PHONY: math-tests +endif diff --git a/make/common/strings.mk b/make/common/strings.mk new file mode 100644 index 0000000..768d061 --- /dev/null +++ b/make/common/strings.mk @@ -0,0 +1,153 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +########################################################### +## Convert to lower case without requiring a shell, which isn't cacheable. +## +## $(1): string +########################################################### +to-lower=$(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1)))))))))))))))))))))))))) + +########################################################### +## Convert to upper case without requiring a shell, which isn't cacheable. +## +## $(1): string +########################################################### +to-upper=$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$1)))))))))))))))))))))))))) + +# Test to-lower and to-upper +lower := abcdefghijklmnopqrstuvwxyz-_ +upper := ABCDEFGHIJKLMNOPQRSTUVWXYZ-_ + +ifneq ($(lower),$(call to-lower,$(upper))) + $(error to-lower sanity check failure) +endif + +ifneq ($(upper),$(call to-upper,$(lower))) + $(error to-upper sanity check failure) +endif + +lower := +upper := + +########################################################### +## Returns true if $(1) and $(2) are equal. Returns +## the empty string if they are not equal. +########################################################### +define streq +$(strip $(if $(strip $(1)),\ + $(if $(strip $(2)),\ + $(if $(filter-out __,_$(subst $(strip $(1)),,$(strip $(2)))$(subst $(strip $(2)),,$(strip $(1)))_),,true), \ + ),\ + $(if $(strip $(2)),\ + ,\ + true)\ + )) +endef + +########################################################### +## Convert "a b c" into "a:b:c" +########################################################### +define normalize-path-list +$(subst $(space),:,$(strip $(1))) +endef + +########################################################### +## Convert "a b c" into "a,b,c" +########################################################### +define normalize-comma-list +$(subst $(space),$(comma),$(strip $(1))) +endef + +########################################################### +## Read the word out of a colon-separated list of words. +## This has the same behavior as the built-in function +## $(word n,str). +## +## The individual words may not contain spaces. +## +## $(1): 1 based index +## $(2): value of the form a:b:c... +########################################################### + +define word-colon +$(word $(1),$(subst :,$(space),$(2))) +endef + +########################################################### +## Read a colon-separated sublist out of a colon-separated +## list of words. +## This has similar behavior to the built-in function +## $(wordlist s,e,str) except both the input and output +## word lists are colon-separated. +## +## The individual words may not contain spaces. +## +## $(1): 1 based index start +## $(2): 1 based index end (can be 0) +## $(3): value of the form a:b:c... +########################################################### + +define wordlist-colon +$(subst $(space),:,$(wordlist $(1),$(2),$(subst :,$(space),$(3)))) +endef + +########################################################### +## Convert "a=b c= d e = f = g h=" into "a=b c=d e= f=g h=" +## +## $(1): list to collapse +## $(2): if set, separator word; usually "=", ":", or ":=" +## Defaults to "=" if not set. +########################################################### + +define collapse-pairs +$(strip \ +$(eval _cpSEP := $(strip $(if $(2),$(2),=)))\ +$(eval _cpLHS :=)\ +$(eval _cpRET :=)\ +$(foreach w,$(subst $(space)$(_cpSEP),$(_cpSEP),$(strip \ + $(subst $(_cpSEP),$(space)$(_cpSEP)$(space),$(1)))),\ + $(if $(findstring $(_cpSEP),$(w)),\ + $(eval _cpRET += $(_cpLHS))$(eval _cpLHS := $(w)),\ + $(eval _cpRET += $(_cpLHS)$(w))$(eval _cpLHS :=)))\ +$(if $(_cpLHS),$(_cpRET)$(space)$(_cpLHS),$(_cpRET))\ +$(eval _cpSEP :=)\ +$(eval _cpLHS :=)\ +$(eval _cpRET :=)) +endef + +# Sanity check for collapse-pairs. +ifneq (a=b c=d e= f=g h=,$(call collapse-pairs,a=b c= d e = f = g h=)) + $(error collapse-pairs sanity check failure) +endif +ifneq (a:=b c:=d e:=f g:=h,$(call collapse-pairs,a:=b c:= d e :=f g := h,:=)) + $(error collapse-pairs sanity check failure) +endif + +########################################################### +## Given a list of pairs, if multiple pairs have the same +## first components, keep only the first pair. +## +## $(1): list of pairs +## $(2): the separator word, such as ":", "=", etc. +define uniq-pairs-by-first-component +$(eval _upbfc_fc_set :=)\ +$(strip $(foreach w,$(1), $(eval _first := $(word 1,$(subst $(2),$(space),$(w))))\ + $(if $(filter $(_upbfc_fc_set),$(_first)),,$(w)\ + $(eval _upbfc_fc_set += $(_first)))))\ +$(eval _upbfc_fc_set :=)\ +$(eval _first:=) +endef diff --git a/make/core/LINUX_KERNEL_COPYING b/make/core/LINUX_KERNEL_COPYING new file mode 100644 index 0000000..ca442d3 --- /dev/null +++ b/make/core/LINUX_KERNEL_COPYING @@ -0,0 +1,356 @@ + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + + Linus Torvalds + +---------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/make/core/Makefile b/make/core/Makefile new file mode 100644 index 0000000..74cec73 --- /dev/null +++ b/make/core/Makefile @@ -0,0 +1,7111 @@ +# Put some miscellaneous rules here + +# HACK: clear LOCAL_PATH from including last build target before calling +# intermedites-dir-for +LOCAL_PATH := $(BUILD_SYSTEM) + +SYSTEM_NOTICE_DEPS := +VENDOR_NOTICE_DEPS := +UNMOUNTED_NOTICE_DEPS := +ODM_NOTICE_DEPS := +OEM_NOTICE_DEPS := +PRODUCT_NOTICE_DEPS := +SYSTEM_EXT_NOTICE_DEPS := +VENDOR_DLKM_NOTICE_DEPS := +ODM_DLKM_NOTICE_DEPS := +SYSTEM_DLKM_NOTICE_DEPS := + +# ----------------------------------------------------------------- +# Define rules to copy PRODUCT_COPY_FILES defined by the product. +# PRODUCT_COPY_FILES contains words like :[:]. +# is relative to $(PRODUCT_OUT), so it should look like, +# e.g., "system/etc/file.xml". +# The filter part means "only eval the copy-one-file rule if this +# src:dest pair is the first one to match the same dest" +#$(1): the src:dest pair +#$(2): the dest +define check-product-copy-files +$(if $(filter-out $(TARGET_COPY_OUT_SYSTEM_OTHER)/%,$(2)), \ + $(if $(filter %.apk, $(2)),$(error \ + Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT instead!))) \ +$(if $(filter true,$(BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES)),, \ + $(if $(filter $(TARGET_COPY_OUT_SYSTEM)/etc/vintf/% \ + $(TARGET_COPY_OUT_SYSTEM)/manifest.xml \ + $(TARGET_COPY_OUT_SYSTEM)/compatibility_matrix.xml,$(2)), \ + $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), use vintf_fragments instead!)) \ + $(if $(filter $(TARGET_COPY_OUT_PRODUCT)/etc/vintf/%,$(2)), \ + $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ + use PRODUCT_MANIFEST_FILES / DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \ + $(if $(filter $(TARGET_COPY_OUT_SYSTEM_EXT)/etc/vintf/%,$(2)), \ + $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ + use vintf_compatibility_matrix / vintf_fragments instead!)) \ + $(if $(filter $(TARGET_COPY_OUT_VENDOR)/etc/vintf/% \ + $(TARGET_COPY_OUT_VENDOR)/manifest.xml \ + $(TARGET_COPY_OUT_VENDOR)/compatibility_matrix.xml,$(2)), \ + $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ + use DEVICE_MANIFEST_FILE / DEVICE_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \ + $(if $(filter $(TARGET_COPY_OUT_ODM)/etc/vintf/% \ + $(TARGET_COPY_OUT_ODM)/etc/manifest%,$(2)), \ + $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ + use ODM_MANIFEST_FILES / vintf_fragments instead!)) \ +) +endef + +# Phony target to check PRODUCT_COPY_FILES copy pairs don't contain ELF files +.PHONY: check-elf-prebuilt-product-copy-files +check-elf-prebuilt-product-copy-files: + +check_elf_prebuilt_product_copy_files := +ifneq (,$(filter true,$(BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES))) +check_elf_prebuilt_product_copy_files := +endif +check_elf_prebuilt_product_copy_files_hint := \ + found ELF prebuilt in PRODUCT_COPY_FILES, use cc_prebuilt_binary / cc_prebuilt_library_shared instead. + +# filter out the duplicate : pairs. +unique_product_copy_files_pairs := +$(foreach cf,$(PRODUCT_COPY_FILES), \ + $(if $(filter $(unique_product_copy_files_pairs),$(cf)),,\ + $(eval unique_product_copy_files_pairs += $(cf)))) +unique_product_copy_files_destinations := +product_copy_files_ignored := +$(foreach cf,$(unique_product_copy_files_pairs), \ + $(eval _src := $(call word-colon,1,$(cf))) \ + $(eval _dest := $(call word-colon,2,$(cf))) \ + $(call check-product-copy-files,$(cf),$(_dest)) \ + $(if $(filter $(unique_product_copy_files_destinations),$(_dest)), \ + $(eval product_copy_files_ignored += $(cf)), \ + $(eval _fulldest := $(call append-path,$(PRODUCT_OUT),$(_dest))) \ + $(if $(filter %.xml,$(_dest)),\ + $(eval $(call copy-xml-file-checked,$(_src),$(_fulldest))),\ + $(if $(and $(filter %.jar,$(_dest)),$(filter $(basename $(notdir $(_dest))),$(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))),\ + $(eval $(call copy-and-uncompress-dexs,$(_src),$(_fulldest))), \ + $(if $(filter init%rc,$(notdir $(_dest)))$(filter %/etc/init,$(dir $(_dest))),\ + $(eval $(call copy-init-script-file-checked,$(_src),$(_fulldest))),\ + $(if $(and $(filter true,$(check_elf_prebuilt_product_copy_files)), \ + $(filter bin lib lib64,$(subst /,$(space),$(_dest)))), \ + $(eval $(call copy-non-elf-file-checked,$(_src),$(_fulldest),$(check_elf_prebuilt_product_copy_files_hint))), \ + $(eval $(call copy-one-file,$(_src),$(_fulldest))))))) \ + $(eval unique_product_copy_files_destinations += $(_dest)))) + +# Dump a list of overriden (and ignored PRODUCT_COPY_FILES entries) +pcf_ignored_file := $(PRODUCT_OUT)/product_copy_files_ignored.txt +$(pcf_ignored_file): PRIVATE_IGNORED := $(sort $(product_copy_files_ignored)) +$(pcf_ignored_file): + echo "$(PRIVATE_IGNORED)" | tr " " "\n" >$@ + +$(call declare-0p-target,$(pcf_ignored_file)) + +$(call dist-for-goals,droidcore-unbundled,$(pcf_ignored_file):logs/$(notdir $(pcf_ignored_file))) + +pcf_ignored_file := +product_copy_files_ignored := +unique_product_copy_files_pairs := +unique_product_copy_files_destinations := + +# ----------------------------------------------------------------- +# Returns the max allowed size for an image suitable for hash verification +# (e.g., boot.img, recovery.img, etc). +# The value 69632 derives from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in $(AVBTOOL). +# $(1): partition size to flash the image +define get-hash-image-max-size +$(if $(1), \ + $(if $(filter true,$(BOARD_AVB_ENABLE)), \ + $(eval _hash_meta_size := 69632), \ + $(eval _hash_meta_size := 0)) \ + $(1)-$(_hash_meta_size)) +endef + +# ----------------------------------------------------------------- +# Define rules to copy headers defined in copy_headers.mk +# If more than one makefile declared a header, print a warning, +# then copy the last one defined. This matches the previous make +# behavior. +has_dup_copy_headers := +$(foreach dest,$(ALL_COPIED_HEADERS), \ + $(eval _srcs := $(ALL_COPIED_HEADERS.$(dest).SRC)) \ + $(eval _src := $(lastword $(_srcs))) \ + $(if $(call streq,$(_src),$(_srcs)),, \ + $(warning Duplicate header copy: $(dest)) \ + $(warning _ Using $(_src)) \ + $(warning __ from $(lastword $(ALL_COPIED_HEADERS.$(dest).MAKEFILE))) \ + $(eval _makefiles := $$(wordlist 1,$(call int_subtract,$(words $(ALL_COPIED_HEADERS.$(dest).MAKEFILE)),1),$$(ALL_COPIED_HEADERS.$$(dest).MAKEFILE))) \ + $(foreach src,$(wordlist 1,$(call int_subtract,$(words $(_srcs)),1),$(_srcs)), \ + $(warning _ Ignoring $(src)) \ + $(warning __ from $(firstword $(_makefiles))) \ + $(eval _makefiles := $$(wordlist 2,9999,$$(_makefiles)))) \ + $(eval has_dup_copy_headers := true)) \ + $(eval $(call copy-one-header,$(_src),$(dest)))) +all_copied_headers: $(ALL_COPIED_HEADERS) + +ifdef has_dup_copy_headers + has_dup_copy_headers := + $(error duplicate header copies are no longer allowed. For more information about headers, see: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers) +endif + +$(file >$(PRODUCT_OUT)/.copied_headers_list,$(TARGET_OUT_HEADERS) $(ALL_COPIED_HEADERS)) + +# ----------------------------------------------------------------- +# docs/index.html +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +gen := $(OUT_DOCS)/index.html +ALL_DOCS += $(gen) +$(gen): frameworks/base/docs/docs-redirect-index.html + @mkdir -p $(dir $@) + @cp -f $< $@ +endif + +ndk_doxygen_out := $(OUT_NDK_DOCS) +ndk_headers := $(SOONG_OUT_DIR)/ndk/sysroot/usr/include +ndk_docs_src_dir := frameworks/native/docs +ndk_doxyfile := $(ndk_docs_src_dir)/Doxyfile +ifneq ($(wildcard $(ndk_docs_src_dir)),) +ndk_docs_srcs := $(addprefix $(ndk_docs_src_dir)/,\ + $(call find-files-in-subdirs,$(ndk_docs_src_dir),"*",.)) +$(ndk_doxygen_out)/index.html: $(ndk_docs_srcs) $(SOONG_OUT_DIR)/ndk.timestamp + @mkdir -p $(ndk_doxygen_out) + @echo "Generating NDK docs to $(ndk_doxygen_out)" + @( cat $(ndk_doxyfile); \ + echo "INPUT=$(ndk_headers)"; \ + echo "HTML_OUTPUT=$(ndk_doxygen_out)" \ + ) | doxygen - + +$(call declare-1p-target,$(ndk_doxygen_out)/index.html,) + +# Note: Not a part of the docs target because we don't have doxygen available. +# You can run this target locally if you have doxygen installed. +ndk-docs: $(ndk_doxygen_out)/index.html +.PHONY: ndk-docs +endif + +ifeq ($(HOST_OS),linux) +$(call dist-for-goals,sdk,$(API_FINGERPRINT)) +endif + +INSTALLED_RECOVERYIMAGE_TARGET := +# Build recovery image if +# BUILDING_RECOVERY_IMAGE && !BOARD_USES_RECOVERY_AS_BOOT && !BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT. +# If BOARD_USES_RECOVERY_AS_BOOT is true, leave empty because INSTALLED_BOOTIMAGE_TARGET is built +# with recovery resources. +# If BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is true, leave empty to build recovery resources +# but not the final recovery image. +ifdef BUILDING_RECOVERY_IMAGE +ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true) +ifneq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) +INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img +endif +endif +endif + +include $(BUILD_SYSTEM)/sysprop.mk + +# ---------------------------------------------------------------- + +# ----------------------------------------------------------------- +# sdk-build.prop +# +# There are certain things in build.prop that we don't want to +# ship with the sdk; remove them. + +# This must be a list of entire property keys followed by +# "=" characters, without any internal spaces. +sdk_build_prop_remove := \ + ro.build.user= \ + ro.build.host= \ + ro.product.brand= \ + ro.product.manufacturer= \ + ro.product.device= +# TODO: Remove this soon-to-be obsolete property +sdk_build_prop_remove += ro.build.product= +INSTALLED_SDK_BUILD_PROP_TARGET := $(PRODUCT_OUT)/sdk/sdk-build.prop +$(INSTALLED_SDK_BUILD_PROP_TARGET): $(INSTALLED_BUILD_PROP_TARGET) + @echo SDK buildinfo: $@ + @mkdir -p $(dir $@) + $(hide) grep -v "$(subst $(space),\|,$(strip \ + $(sdk_build_prop_remove)))" $< > $@.tmp + $(hide) for x in $(strip $(sdk_build_prop_remove)); do \ + echo "$$x"generic >> $@.tmp; done + $(hide) mv $@.tmp $@ + +$(call declare-0p-target,$(INSTALLED_SDK_BUILD_PROP_TARGET)) + +# ----------------------------------------------------------------- +# declare recovery ramdisk files +ifeq ($(BUILDING_RECOVERY_IMAGE),true) +INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP := $(call intermediates-dir-for,PACKAGING,recovery)/ramdisk_files-timestamp +endif + +# ----------------------------------------------------------------- +# Declare vendor ramdisk fragments +INTERNAL_VENDOR_RAMDISK_FRAGMENTS := + +ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) + ifneq (,$(filter recovery,$(BOARD_VENDOR_RAMDISK_FRAGMENTS))) + $(error BOARD_VENDOR_RAMDISK_FRAGMENTS must not contain "recovery" if \ + BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT is set) + endif + INTERNAL_VENDOR_RAMDISK_FRAGMENTS += recovery + VENDOR_RAMDISK_FRAGMENT.recovery.STAGING_DIR := $(TARGET_RECOVERY_ROOT_OUT) + VENDOR_RAMDISK_FRAGMENT.recovery.FILES := $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) + BOARD_VENDOR_RAMDISK_FRAGMENT.recovery.MKBOOTIMG_ARGS += --ramdisk_type RECOVERY + .KATI_READONLY := VENDOR_RAMDISK_FRAGMENT.recovery.STAGING_DIR +endif + +# Validation check and assign default --ramdisk_type. +$(foreach vendor_ramdisk_fragment,$(BOARD_VENDOR_RAMDISK_FRAGMENTS), \ + $(if $(and $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \ + $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)), \ + $(error Must not specify KERNEL_MODULE_DIRS for prebuilt vendor ramdisk fragment "$(vendor_ramdisk_fragment)": $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS))) \ + $(eval VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR := $(call intermediates-dir-for,PACKAGING,vendor_ramdisk_fragment-stage-$(vendor_ramdisk_fragment))) \ + $(eval VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).FILES :=) \ + $(if $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \ + $(if $(filter --ramdisk_type,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)),, \ + $(eval BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS += --ramdisk_type DLKM))) \ +) + +# Create the "kernel module directory" to "vendor ramdisk fragment" inverse mapping. +$(foreach vendor_ramdisk_fragment,$(BOARD_VENDOR_RAMDISK_FRAGMENTS), \ + $(foreach kmd,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \ + $(eval kmd_vrf := KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd)) \ + $(if $($(kmd_vrf)),$(error Kernel module directory "$(kmd)" belongs to multiple vendor ramdisk fragments: "$($(kmd_vrf))" "$(vendor_ramdisk_fragment)", each kernel module directory should belong to exactly one or none vendor ramdisk fragment)) \ + $(eval $(kmd_vrf) := $(vendor_ramdisk_fragment)) \ + ) \ +) +INTERNAL_VENDOR_RAMDISK_FRAGMENTS += $(BOARD_VENDOR_RAMDISK_FRAGMENTS) + +# Strip the list in case of any whitespace. +INTERNAL_VENDOR_RAMDISK_FRAGMENTS := \ + $(strip $(INTERNAL_VENDOR_RAMDISK_FRAGMENTS)) + +# Assign --ramdisk_name for each vendor ramdisk fragment. +$(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \ + $(if $(filter --ramdisk_name,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)), \ + $(error Must not specify --ramdisk_name for vendor ramdisk fragment: $(vendor_ramdisk_fragment))) \ + $(eval BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS += --ramdisk_name $(vendor_ramdisk_fragment)) \ + $(eval .KATI_READONLY := BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS) \ +) + +# ----------------------------------------------------------------- +# kernel modules + +# Depmod requires a well-formed kernel version so 0.0 is used as a placeholder. +DEPMOD_STAGING_SUBDIR :=$= lib/modules/0.0 + +define copy-and-strip-kernel-module +$(2): $(1) + $(LLVM_STRIP) -o $(2) --strip-debug $(1) +endef + +# $(1): modules list +# $(2): output dir +# $(3): mount point +# $(4): staging dir +# $(5): module load list +# $(6): module load list filename +# $(7): module archive +# $(8): staging dir for stripped modules +# $(9): module directory name +# Returns a list of src:dest pairs to install the modules using copy-many-files. +define build-image-kernel-modules + $(if $(9), \ + $(eval _dir := $(9)/), \ + $(eval _dir :=)) \ + $(foreach module,$(1), \ + $(eval _src := $(module)) \ + $(if $(8), \ + $(eval _src := $(8)/$(notdir $(module))) \ + $(eval $(call copy-and-strip-kernel-module,$(module),$(_src)))) \ + $(_src):$(2)/lib/modules/$(_dir)$(notdir $(module))) \ + $(eval $(call build-image-kernel-modules-depmod,$(1),$(3),$(4),$(5),$(6),$(7),$(2),$(9))) \ + $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.dep:$(2)/lib/modules/$(_dir)modules.dep \ + $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.alias:$(2)/lib/modules/$(_dir)modules.alias \ + $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep:$(2)/lib/modules/$(_dir)modules.softdep \ + $(4)/$(DEPMOD_STAGING_SUBDIR)/$(6):$(2)/lib/modules/$(_dir)$(6) +endef + +# $(1): modules list +# $(2): mount point +# $(3): staging dir +# $(4): module load list +# $(5): module load list filename +# $(6): module archive +# $(7): output dir +# $(8): module directory name +# TODO(b/144844424): If a module archive is being used, this step (which +# generates obj/PACKAGING/.../modules.dep) also unzips the module archive into +# the output directory. This should be moved to a module with a +# LOCAL_POST_INSTALL_CMD so that if modules.dep is removed from the output dir, +# the archive modules are restored along with modules.dep. +define build-image-kernel-modules-depmod +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: .KATI_IMPLICIT_OUTPUTS := $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.alias $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(DEPMOD) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(strip $(1)) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MOUNT_POINT := $(2) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules/$(8) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_STAGING_DIR := $(3) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(strip $(4)) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_FILE := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_ARCHIVE := $(6) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_OUTPUT_DIR := $(7) +$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(1) $(6) + @echo depmod $$(PRIVATE_STAGING_DIR) + rm -rf $$(PRIVATE_STAGING_DIR) + mkdir -p $$(PRIVATE_MODULE_DIR) + $(if $(6),\ + unzip -qoDD -d $$(PRIVATE_MODULE_DIR) $$(PRIVATE_MODULE_ARCHIVE); \ + mkdir -p $$(PRIVATE_OUTPUT_DIR)/lib; \ + cp -r $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules $$(PRIVATE_OUTPUT_DIR)/lib/; \ + find $$(PRIVATE_MODULE_DIR) -type f -name *.ko | xargs basename -a > $$(PRIVATE_LOAD_FILE); \ + ) + $(if $(1),\ + cp $$(PRIVATE_MODULES) $$(PRIVATE_MODULE_DIR)/; \ + for MODULE in $$(PRIVATE_LOAD_MODULES); do \ + basename $$$$MODULE >> $$(PRIVATE_LOAD_FILE); \ + done; \ + ) + $(DEPMOD) -b $$(PRIVATE_STAGING_DIR) 0.0 + # Turn paths in modules.dep into absolute paths + sed -i.tmp -e 's|\([^: ]*lib/modules/[^: ]*\)|/\1|g' $$(PRIVATE_STAGING_DIR)/$$(DEPMOD_STAGING_SUBDIR)/modules.dep + touch $$(PRIVATE_LOAD_FILE) +endef + +# $(1): staging dir +# $(2): modules list +# $(3): module load list +# $(4): module load list filename +# $(5): output dir +define module-load-list-copy-paths + $(eval $(call build-image-module-load-list,$(1),$(2),$(3),$(4))) \ + $(1)/$(DEPMOD_STAGING_SUBDIR)/$(4):$(5)/lib/modules/$(4) +endef + +# $(1): staging dir +# $(2): modules list +# $(3): module load list +# $(4): module load list filename +define build-image-module-load-list +$(1)/$(DEPMOD_STAGING_SUBDIR)/$(4): PRIVATE_LOAD_MODULES := $(3) +$(1)/$(DEPMOD_STAGING_SUBDIR)/$(4): $(2) + @echo load-list $$(@) + @echo '$$(strip $$(notdir $$(PRIVATE_LOAD_MODULES)))' | tr ' ' '\n' > $$(@) +endef + +# $(1): source options file +# $(2): destination pathname +# Returns a build rule that checks the syntax of and installs a kernel modules +# options file. Strip and squeeze any extra space and blank lines. +# For use via $(eval). +define build-image-kernel-modules-options-file +$(2): $(1) + @echo "libmodprobe options $$(@)" + $(hide) mkdir -p "$$(dir $$@)" + $(hide) rm -f "$$@" + $(hide) awk <"$$<" >"$$@" \ + '/^#/ { print; next } \ + NF == 0 { next } \ + NF < 2 || $$$$1 != "options" \ + { print "Invalid options line " FNR ": " $$$$0 >"/dev/stderr"; \ + exit_status = 1; next } \ + { $$$$1 = $$$$1; print } \ + END { exit exit_status }' +endef + +# $(1): source blocklist file +# $(2): destination pathname +# Returns a build rule that checks the syntax of and installs a kernel modules +# blocklist file. Strip and squeeze any extra space and blank lines. +# For use via $(eval). +define build-image-kernel-modules-blocklist-file +$(2): $(1) + @echo "libmodprobe blocklist $$(@)" + $(hide) mkdir -p "$$(dir $$@)" + $(hide) rm -f "$$@" + $(hide) awk <"$$<" >"$$@" \ + '/^#/ { print; next } \ + NF == 0 { next } \ + NF != 2 || $$$$1 != "blocklist" \ + { print "Invalid blocklist line " FNR ": " $$$$0 >"/dev/stderr"; \ + exit_status = 1; next } \ + { $$$$1 = $$$$1; print } \ + END { exit exit_status }' +endef + +# $(1): image name +# $(2): build output directory (TARGET_OUT_VENDOR, TARGET_RECOVERY_ROOT_OUT, etc) +# $(3): mount point +# $(4): module load filename +# $(5): stripped staging directory +# $(6): kernel module directory name (top is an out of band value for no directory) +define build-image-kernel-modules-dir +$(if $(filter top,$(6)),\ + $(eval _kver :=)$(eval _sep :=),\ + $(eval _kver := $(6))$(eval _sep :=_))\ +$(if $(5),\ + $(eval _stripped_staging_dir := $(5)$(_sep)$(_kver)),\ + $(eval _stripped_staging_dir :=))\ +$(if $(strip $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver))$(BOARD_$(1)_KERNEL_MODULES_ARCHIVE$(_sep)$(_kver))),\ + $(if $(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver)),,\ + $(eval BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver) := $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)))) \ + $(call copy-many-files,$(call build-image-kernel-modules,$(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)),$(2),$(3),$(call intermediates-dir-for,PACKAGING,depmod_$(1)$(_sep)$(_kver)),$(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver)),$(4),$(BOARD_$(1)_KERNEL_MODULES_ARCHIVE$(_sep)$(_kver)),$(_stripped_staging_dir),$(_kver)))) \ +$(if $(_kver), \ + $(eval _dir := $(_kver)/), \ + $(eval _dir :=)) \ +$(if $(BOARD_$(1)_KERNEL_MODULES_OPTIONS_FILE$(_sep)$(_kver)), \ + $(eval $(call build-image-kernel-modules-options-file, \ + $(BOARD_$(1)_KERNEL_MODULES_OPTIONS_FILE$(_sep)$(_kver)), \ + $(2)/lib/modules/$(_dir)modules.options)) \ + $(2)/lib/modules/$(_dir)modules.options) \ +$(if $(BOARD_$(1)_KERNEL_MODULES_BLOCKLIST_FILE$(_sep)$(_kver)), \ + $(eval $(call build-image-kernel-modules-blocklist-file, \ + $(BOARD_$(1)_KERNEL_MODULES_BLOCKLIST_FILE$(_sep)$(_kver)), \ + $(2)/lib/modules/$(_dir)modules.blocklist)) \ + $(2)/lib/modules/$(_dir)modules.blocklist) +endef + +# $(1): kernel module directory name (top is an out of band value for no directory) +define build-recovery-as-boot-load +$(if $(filter top,$(1)),\ + $(eval _kver :=)$(eval _sep :=),\ + $(eval _kver := $(1))$(eval _sep :=_))\ + $(if $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ + $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,ramdisk_module_list$(_sep)$(_kver)),$(BOARD_GENERIC_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load,$(TARGET_RECOVERY_ROOT_OUT)))) +endef + +# $(1): kernel module directory name (top is an out of band value for no directory) +define build-vendor-ramdisk-recovery-load +$(if $(filter top,$(1)),\ + $(eval _kver :=)$(eval _sep :=),\ + $(eval _kver := $(1))$(eval _sep :=_))\ + $(if $(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ + $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_ramdisk_recovery_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.recovery,$(TARGET_VENDOR_RAMDISK_OUT)))) +endef + +# $(1): kernel module directory name (top is an out of band value for no directory) +define build-vendor-charger-load +$(if $(filter top,$(1)),\ + $(eval _kver :=)$(eval _sep :=),\ + $(eval _kver := $(1))$(eval _sep :=_))\ + $(if $(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ + $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_OUT_VENDOR)))) +endef + +ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true) + # If there is no vendor boot partition, store vendor ramdisk kernel modules in the + # boot ramdisk. + BOARD_GENERIC_RAMDISK_KERNEL_MODULES += $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES) + BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD += $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD) +endif + +ifeq ($(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD),) + BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES) +endif + +ifneq ($(strip $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES)),) + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT), true) + BOARD_RECOVERY_KERNEL_MODULES += $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES) + endif +endif + +ifneq ($(BOARD_DO_NOT_STRIP_RECOVERY_MODULES),true) + RECOVERY_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_recovery_stripped) +else + RECOVERY_STRIPPED_MODULE_STAGING_DIR := +endif + +ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_MODULES),true) + VENDOR_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_stripped) +else + VENDOR_STRIPPED_MODULE_STAGING_DIR := +endif + +ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES),true) + VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_ramdisk_stripped) +else + VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR := +endif + +ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_KERNEL_RAMDISK_MODULES),true) + VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_kernel_ramdisk_stripped) +else + VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR := +endif + +BOARD_KERNEL_MODULE_DIRS += top +$(foreach kmd,$(BOARD_KERNEL_MODULE_DIRS), \ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,$(RECOVERY_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ + $(eval vendor_ramdisk_fragment := $(KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd))) \ + $(if $(vendor_ramdisk_fragment), \ + $(eval output_dir := $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR)) \ + $(eval result_var := VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).FILES) \ + $(eval ### else ###), \ + $(eval output_dir := $(TARGET_VENDOR_RAMDISK_OUT)) \ + $(eval result_var := ALL_DEFAULT_INSTALLED_MODULES)) \ + $(eval $(result_var) += $(call build-image-kernel-modules-dir,VENDOR_RAMDISK,$(output_dir),,modules.load,$(VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR_KERNEL_RAMDISK,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT),,modules.load,$(VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-ramdisk-recovery-load,$(kmd))) \ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR,$(if $(filter true,$(BOARD_USES_VENDOR_DLKMIMAGE)),$(TARGET_OUT_VENDOR_DLKM),$(TARGET_OUT_VENDOR)),vendor,modules.load,$(VENDOR_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-charger-load,$(kmd))) \ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,ODM,$(if $(filter true,$(BOARD_USES_ODM_DLKMIMAGE)),$(TARGET_OUT_ODM_DLKM),$(TARGET_OUT_ODM)),odm,modules.load,,$(kmd))) \ + $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-recovery-as-boot-load,$(kmd))),\ + $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,GENERIC_RAMDISK,$(TARGET_RAMDISK_OUT),,modules.load,,$(kmd))))) + +# ----------------------------------------------------------------- +# Cert-to-package mapping. Used by the post-build signing tools. +# Use a macro to add newline to each echo command +# $1 stem name of the package +# $2 certificate +# $3 private key +# $4 compressed +# $5 partition tag +# $6 output file +define _apkcerts_write_line +$(hide) echo -n 'name="$(1).apk" certificate="$2" private_key="$3"' >> $6 +$(if $(4), $(hide) echo -n ' compressed="$4"' >> $6) +$(if $(5), $(hide) echo -n ' partition="$5"' >> $6) +$(hide) echo '' >> $6 + +endef + +# ----------------------------------------------------------------- +# Merge an individual apkcerts output into the final apkcerts.txt output. +# Use a macro to make it compatible with _apkcerts_write_line +# $1 apkcerts file to be merged +# $2 output file +define _apkcerts_merge +$(hide) cat $1 >> $2 + +endef + +name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + name := $(name)_debug +endif +name := $(name)-apkcerts-$(FILE_NAME_TAG) +intermediates := \ + $(call intermediates-dir-for,PACKAGING,apkcerts) +APKCERTS_FILE := $(intermediates)/$(name).txt +all_apkcerts_files := $(sort $(foreach p,$(PACKAGES),$(PACKAGES.$(p).APKCERTS_FILE))) +$(APKCERTS_FILE): $(all_apkcerts_files) +# We don't need to really build all the modules. +# TODO: rebuild APKCERTS_FILE if any app change its cert. +$(APKCERTS_FILE): + @echo APK certs list: $@ + @mkdir -p $(dir $@) + @rm -f $@ + $(foreach p,$(sort $(PACKAGES)),\ + $(if $(PACKAGES.$(p).APKCERTS_FILE),\ + $(call _apkcerts_merge,$(PACKAGES.$(p).APKCERTS_FILE), $@),\ + $(if $(PACKAGES.$(p).EXTERNAL_KEY),\ + $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),EXTERNAL,,$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@),\ + $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),$(PACKAGES.$(p).CERTIFICATE),$(PACKAGES.$(p).PRIVATE_KEY),$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@)))) + $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),\ + $(call _apkcerts_write_line,$(notdir $(basename $(FSVERITY_APK_OUT))),$(FSVERITY_APK_KEY_PATH).x509.pem,$(FSVERITY_APK_KEY_PATH).pk8,,system,$@)) + # In case value of PACKAGES is empty. + $(hide) touch $@ + +$(call declare-0p-target,$(APKCERTS_FILE)) + +.PHONY: apkcerts-list +apkcerts-list: $(APKCERTS_FILE) + +ifneq (,$(TARGET_BUILD_APPS)) + $(call dist-for-goals, apps_only, $(APKCERTS_FILE):apkcerts.txt) + $(call dist-for-goals, apps_only, $(SOONG_APEX_KEYS_FILE):apexkeys.txt) +endif + + +# ----------------------------------------------------------------- +# build system stats +BUILD_SYSTEM_STATS := $(PRODUCT_OUT)/build_system_stats.txt +$(BUILD_SYSTEM_STATS): + @rm -f $@ + @$(foreach s,$(STATS.MODULE_TYPE),echo "modules_type_make,$(s),$(words $(STATS.MODULE_TYPE.$(s)))" >>$@;) + @$(foreach s,$(STATS.SOONG_MODULE_TYPE),echo "modules_type_soong,$(s),$(STATS.SOONG_MODULE_TYPE.$(s))" >>$@;) +$(call declare-1p-target,$(BUILD_SYSTEM_STATS),build) +$(call dist-for-goals,droidcore-unbundled,$(BUILD_SYSTEM_STATS)) + +# ----------------------------------------------------------------- +# build /product/etc/security/avb/system_other.avbpubkey if needed +ifdef BUILDING_SYSTEM_OTHER_IMAGE +ifeq ($(BOARD_AVB_ENABLE),true) +INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET := $(TARGET_OUT_PRODUCT_ETC)/security/avb/system_other.avbpubkey +ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET) +endif # BOARD_AVB_ENABLE +endif # BUILDING_SYSTEM_OTHER_IMAGE + +# ----------------------------------------------------------------- +# Modules ready to be converted to Soong, ordered by how many +# modules depend on them. +SOONG_CONV := $(sort $(SOONG_CONV)) +SOONG_CONV_DATA := $(call intermediates-dir-for,PACKAGING,soong_conversion)/soong_conv_data +$(SOONG_CONV_DATA): + @rm -f $@ + @$(foreach s,$(SOONG_CONV),echo "$(s),$(SOONG_CONV.$(s).TYPE),$(sort $(SOONG_CONV.$(s).PROBLEMS)),$(sort $(filter-out $(SOONG_ALREADY_CONV),$(SOONG_CONV.$(s).DEPS))),$(sort $(SOONG_CONV.$(s).MAKEFILES)),$(sort $(SOONG_CONV.$(s).INSTALLED))" >>$@;) + +$(call declare-1p-target,$(SOONG_CONV_DATA),build) + +SOONG_TO_CONVERT_SCRIPT := build/make/tools/soong_to_convert.py +SOONG_TO_CONVERT := $(PRODUCT_OUT)/soong_to_convert.txt +$(SOONG_TO_CONVERT): $(SOONG_CONV_DATA) $(SOONG_TO_CONVERT_SCRIPT) + @rm -f $@ + $(hide) $(SOONG_TO_CONVERT_SCRIPT) $< >$@ +$(call declare-1p-target,$(SOONG_TO_CONVERT),build) +$(call dist-for-goals,droidcore-unbundled,$(SOONG_TO_CONVERT)) + +$(PRODUCT_OUT)/product_packages.txt: + @rm -f $@ + echo "" > $@ + $(foreach x,$(PRODUCT_PACKAGES),echo $(x) >> $@$(newline)) + +MK2BP_CATALOG_SCRIPT := build/make/tools/mk2bp_catalog.py +PRODUCT_PACKAGES_TXT := $(PRODUCT_OUT)/product_packages.txt +MK2BP_REMAINING_HTML := $(PRODUCT_OUT)/mk2bp_remaining.html +$(MK2BP_REMAINING_HTML): PRIVATE_CODE_SEARCH_BASE_URL := "https://cs.android.com/android/platform/superproject/+/master:" +$(MK2BP_REMAINING_HTML): $(SOONG_CONV_DATA) $(MK2BP_CATALOG_SCRIPT) $(PRODUCT_PACKAGES_TXT) + @rm -f $@ + $(hide) $(MK2BP_CATALOG_SCRIPT) \ + --device=$(TARGET_DEVICE) \ + --product-packages=$(PRODUCT_PACKAGES_TXT) \ + --title="Remaining Android.mk files for $(TARGET_DEVICE)-$(TARGET_BUILD_VARIANT)" \ + --codesearch=$(PRIVATE_CODE_SEARCH_BASE_URL) \ + --out-dir="$(OUT_DIR)" \ + --mode=html \ + > $@ +$(call declare-1p-target,$(MK2BP_REMAINING_HTML),build) +$(call dist-for-goals,droidcore-unbundled,$(MK2BP_REMAINING_HTML)) + +MK2BP_REMAINING_CSV := $(PRODUCT_OUT)/mk2bp_remaining.csv +$(MK2BP_REMAINING_CSV): $(SOONG_CONV_DATA) $(MK2BP_CATALOG_SCRIPT) $(PRODUCT_PACKAGES_TXT) + @rm -f $@ + $(hide) $(MK2BP_CATALOG_SCRIPT) \ + --device=$(TARGET_DEVICE) \ + --product-packages=$(PRODUCT_PACKAGES_TXT) \ + --out-dir="$(OUT_DIR)" \ + --mode=csv \ + > $@ +$(call declare-1p-target,$(MK2BP_REMAINING_CSV)) +$(call dist-for-goals,droidcore-unbundled,$(MK2BP_REMAINING_CSV)) + +# ----------------------------------------------------------------- +# Modules use -Wno-error, or added default -Wall -Werror +WALL_WERROR := $(PRODUCT_OUT)/wall_werror.txt +$(WALL_WERROR): + @rm -f $@ + echo "# Modules using -Wno-error" >> $@ + for m in $(sort $(SOONG_MODULES_USING_WNO_ERROR) $(MODULES_USING_WNO_ERROR)); do echo $$m >> $@; done + echo "# Modules added default -Wall" >> $@ + for m in $(sort $(SOONG_MODULES_ADDED_WALL) $(MODULES_ADDED_WALL)); do echo $$m >> $@; done + +$(call declare-0p-target,$(WALL_WERROR)) + +$(call dist-for-goals,droidcore-unbundled,$(WALL_WERROR)) + +# ----------------------------------------------------------------- +# C/C++ flag information for modules +$(call dist-for-goals,droidcore-unbundled,$(SOONG_MODULES_CFLAG_ARTIFACTS)) + +$(foreach a,$(SOONG_MODULES_CFLAG_ARTIFACTS),$(call declare-0p-target,$(call word-colon,1,$(a)))) + +# ----------------------------------------------------------------- +# Modules missing profile files +PGO_PROFILE_MISSING := $(PRODUCT_OUT)/pgo_profile_file_missing.txt +$(PGO_PROFILE_MISSING): + @rm -f $@ + echo "# Modules missing PGO profile files" >> $@ + for m in $(SOONG_MODULES_MISSING_PGO_PROFILE_FILE); do echo $$m >> $@; done + +$(call declare-0p-target,$(PGO_PROFILE_MISSING)) + +$(call dist-for-goals,droidcore,$(PGO_PROFILE_MISSING)) + +CERTIFICATE_VIOLATION_MODULES_FILENAME := $(PRODUCT_OUT)/certificate_violation_modules.txt +$(CERTIFICATE_VIOLATION_MODULES_FILENAME): + rm -f $@ + $(foreach m,$(sort $(CERTIFICATE_VIOLATION_MODULES)), echo $(m) >> $@;) +$(call declare-0p-target,$(CERTIFICATE_VIOLATION_MODULES_FILENAME)) +$(call dist-for-goals,droidcore,$(CERTIFICATE_VIOLATION_MODULES_FILENAME)) + +# ----------------------------------------------------------------- +# The dev key is used to sign this package, and as the key required +# for future OTA packages installed by this system. Actual product +# deliverables will be re-signed by hand. We expect this file to +# exist with the suffixes ".x509.pem" and ".pk8". +DEFAULT_KEY_CERT_PAIR := $(strip $(DEFAULT_SYSTEM_DEV_CERTIFICATE)) + + +# Rules that need to be present for the all targets, even +# if they don't do anything. +.PHONY: systemimage +systemimage: + +# ----------------------------------------------------------------- + +.PHONY: event-log-tags + +# Produce an event logs tag file for everything we know about, in order +# to properly allocate numbers. Then produce a file that's filtered +# for what's going to be installed. + +all_event_log_tags_file := $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt + +event_log_tags_file := $(TARGET_OUT)/etc/event-log-tags + +# Include tags from all packages that we know about +all_event_log_tags_src := \ + $(sort $(foreach m, $(ALL_MODULES), $(ALL_MODULES.$(m).EVENT_LOG_TAGS))) + +$(all_event_log_tags_file): PRIVATE_SRC_FILES := $(all_event_log_tags_src) +$(all_event_log_tags_file): $(all_event_log_tags_src) $(MERGETAGS) build/make/tools/event_log_tags.py + $(hide) mkdir -p $(dir $@) + $(hide) $(MERGETAGS) -o $@ $(PRIVATE_SRC_FILES) + +$(call declare-0p-target,$(all_event_log_tags_file)) + +# Include tags from all packages included in this product, plus all +# tags that are part of the system (ie, not in a vendor/ or device/ +# directory). +event_log_tags_src := \ + $(sort $(foreach m,\ + $(call resolve-bitness-for-modules,TARGET,$(PRODUCT_PACKAGES)) \ + $(call module-names-for-tag-list,user), \ + $(ALL_MODULES.$(m).EVENT_LOG_TAGS)) \ + $(filter-out vendor/% device/% out/%,$(all_event_log_tags_src))) + +$(event_log_tags_file): PRIVATE_SRC_FILES := $(event_log_tags_src) +$(event_log_tags_file): PRIVATE_MERGED_FILE := $(all_event_log_tags_file) +$(event_log_tags_file): $(event_log_tags_src) $(all_event_log_tags_file) $(MERGETAGS) build/make/tools/event_log_tags.py + $(hide) mkdir -p $(dir $@) + $(hide) $(MERGETAGS) -o $@ -m $(PRIVATE_MERGED_FILE) $(PRIVATE_SRC_FILES) + +$(eval $(call declare-0p-target,$(event_log_tags_file))) + +event-log-tags: $(event_log_tags_file) + +ALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file) + +# Initialize INSTALLED_FILES_OUTSIDE_IMAGES with the list of all device files, +# files installed in images will be filtered out later. +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out \ + $(PRODUCT_OUT)/apex/% \ + $(PRODUCT_OUT)/fake_packages/% \ + $(PRODUCT_OUT)/testcases/%, \ + $(filter $(PRODUCT_OUT)/%,$(ALL_DEFAULT_INSTALLED_MODULES))) + +# ################################################################# +# Targets for boot/OS images +# ################################################################# +ifneq ($(strip $(TARGET_NO_BOOTLOADER)),true) + INSTALLED_BOOTLOADER_MODULE := $(PRODUCT_OUT)/bootloader + ifdef BOARD_PREBUILT_BOOTLOADER + $(eval $(call copy-one-file,$(BOARD_PREBUILT_BOOTLOADER),$(INSTALLED_BOOTLOADER_MODULE))) + $(call dist-for-goals,dist_files,$(INSTALLED_BOOTLOADER_MODULE)) + endif # BOARD_PREBUILT_BOOTLOADER + ifeq ($(strip $(TARGET_BOOTLOADER_IS_2ND)),true) + INSTALLED_2NDBOOTLOADER_TARGET := $(PRODUCT_OUT)/2ndbootloader + else + INSTALLED_2NDBOOTLOADER_TARGET := + endif +else + INSTALLED_BOOTLOADER_MODULE := + INSTALLED_2NDBOOTLOADER_TARGET := +endif # TARGET_NO_BOOTLOADER +ifneq ($(strip $(TARGET_NO_KERNEL)),true) + ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) + INSTALLED_KERNEL_TARGET := $(foreach k,$(BOARD_KERNEL_BINARIES), \ + $(PRODUCT_OUT)/$(k)) + else + INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel + endif +else + INSTALLED_KERNEL_TARGET := +endif + +# ----------------------------------------------------------------- +# the root dir +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_ROOT_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +INTERNAL_ROOT_FILES := $(filter $(TARGET_ROOT_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + + +INSTALLED_FILES_FILE_ROOT := $(PRODUCT_OUT)/installed-files-root.txt +INSTALLED_FILES_JSON_ROOT := $(INSTALLED_FILES_FILE_ROOT:.txt=.json) +$(INSTALLED_FILES_FILE_ROOT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ROOT) +$(INSTALLED_FILES_FILE_ROOT) : $(INTERNAL_ROOT_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(TARGET_ROOT_OUT) + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_ROOT_OUT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_ROOT)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_ROOT)) + +#------------------------------------------------------------------ +# dtb +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG +INSTALLED_DTBIMAGE_TARGET := $(PRODUCT_OUT)/dtb.img +ifdef BOARD_PREBUILT_DTBIMAGE_DIR +$(INSTALLED_DTBIMAGE_TARGET) : $(sort $(wildcard $(BOARD_PREBUILT_DTBIMAGE_DIR)/*.dtb)) + cat $^ > $@ +endif +endif + +# ----------------------------------------------------------------- +# the ramdisk +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_RAMDISK_IMAGE +INTERNAL_RAMDISK_FILES := $(filter $(TARGET_RAMDISK_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk.txt +INSTALLED_FILES_JSON_RAMDISK := $(INSTALLED_FILES_FILE_RAMDISK:.txt=.json) +$(INSTALLED_FILES_FILE_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RAMDISK) +$(INSTALLED_FILES_FILE_RAMDISK) : $(INTERNAL_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(TARGET_RAMDISK_OUT) + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_RAMDISK_OUT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_RAMDISK))) +$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_RAMDISK))) + +BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img + +ifeq ($(BOARD_RAMDISK_USE_LZ4),true) +# -l enables the legacy format used by the Linux kernel +COMPRESSION_COMMAND_DEPS := $(LZ4) +COMPRESSION_COMMAND := $(LZ4) -l -12 --favor-decSpeed +RAMDISK_EXT := .lz4 +else +COMPRESSION_COMMAND_DEPS := $(MINIGZIP) +COMPRESSION_COMMAND := $(MINIGZIP) +RAMDISK_EXT := .gz +endif + +# We just build this directly to the install location. +INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET) +$(INSTALLED_RAMDISK_TARGET): PRIVATE_DIRS := debug_ramdisk dev metadata mnt proc second_stage_resources sys +$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES) $(INSTALLED_FILES_FILE_RAMDISK) | $(COMPRESSION_COMMAND_DEPS) + $(call pretty,"Target ramdisk: $@") + $(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(PRIVATE_DIRS)) +ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) + $(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(PRIVATE_DIRS)) +endif + $(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@ + +$(call declare-1p-container,$(INSTALLED_RAMDISK_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_RAMDISK_TARGET),$(INTERNAL_RAMDISK_FILE),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_RAMDISK_TARGET) + +.PHONY: ramdisk-nodeps +ramdisk-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + @echo "make $@: ignoring dependencies" + $(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $(INSTALLED_RAMDISK_TARGET) + +endif # BUILDING_RAMDISK_IMAGE + +# ----------------------------------------------------------------- +# the boot image, which is a collection of other images. + +# This is defined here since we may be building recovery as boot +# below and only want to define this once +ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) + BUILT_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot,$(BOARD_KERNEL_BINARIES)), $(PRODUCT_OUT)/$(k).img) +else + BUILT_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img +endif + +INTERNAL_PREBUILT_BOOTIMAGE := + +my_installed_prebuilt_gki_apex := $(strip $(foreach package,$(PRODUCT_PACKAGES),$(if $(ALL_MODULES.$(package).EXTRACTED_BOOT_IMAGE),$(package)))) +ifdef my_installed_prebuilt_gki_apex + ifneq (1,$(words $(my_installed_prebuilt_gki_apex))) # len(my_installed_prebuilt_gki_apex) > 1 + $(error More than one prebuilt GKI APEXes are installed: $(my_installed_prebuilt_gki_apex)) + endif # len(my_installed_prebuilt_gki_apex) > 1 + + ifdef BOARD_PREBUILT_BOOTIMAGE + $(error Must not define BOARD_PREBUILT_BOOTIMAGE because a prebuilt GKI APEX is installed: $(my_installed_prebuilt_gki_apex)) + endif # BOARD_PREBUILT_BOOTIMAGE defined + + my_apex_extracted_boot_image := $(ALL_MODULES.$(my_installed_prebuilt_gki_apex).EXTRACTED_BOOT_IMAGE) + INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img + $(eval $(call copy-one-file,$(my_apex_extracted_boot_image),$(INSTALLED_BOOTIMAGE_TARGET))) + $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) + + INTERNAL_PREBUILT_BOOTIMAGE := $(my_apex_extracted_boot_image) + +else # my_installed_prebuilt_gki_apex not defined + +# $1: boot image target +# returns the kernel used to make the bootimage +define bootimage-to-kernel + $(if $(BOARD_KERNEL_BINARIES),\ + $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $(1)))),\ + $(INSTALLED_KERNEL_TARGET)) +endef + +ifdef BOARD_BOOTIMAGE_PARTITION_SIZE + BOARD_KERNEL_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE) +endif + +# $1: boot image file name +# $2: boot image variant (boot, boot-debug, boot-test-harness) +define get-bootimage-partition-size +$(BOARD_$(call to-upper,$(subst .img,,$(subst $(2),kernel,$(notdir $(1)))))_BOOTIMAGE_PARTITION_SIZE) +endef + +# $1: partition size +define get-partition-size-argument + $(if $(1),--partition_size $(1),--dynamic_partition_size) +endef + +ifneq ($(strip $(TARGET_NO_KERNEL)),true) +INTERNAL_BOOTIMAGE_ARGS := \ + $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) + +# TODO(b/229701033): clean up BOARD_BUILD_GKI_BOOT_IMAGE_WITHOUT_RAMDISK. +ifneq ($(BOARD_BUILD_GKI_BOOT_IMAGE_WITHOUT_RAMDISK),true) + ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + ifneq ($(BUILDING_INIT_BOOT_IMAGE),true) + INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET) + endif + endif +endif + +ifndef BUILDING_VENDOR_BOOT_IMAGE +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG + INTERNAL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) +endif +endif + +INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS)) + +ifeq ($(PRODUCT_SUPPORTS_VERITY),true) +ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) +VERITY_KEYID := veritykeyid=id:`openssl x509 -in $(PRODUCT_VERITY_SIGNING_KEY).x509.pem -text \ + | grep keyid | sed 's/://g' | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/keyid//g'` +endif +endif + +INTERNAL_KERNEL_CMDLINE := $(strip $(INTERNAL_KERNEL_CMDLINE) buildvariant=$(TARGET_BUILD_VARIANT) $(VERITY_KEYID)) + +# kernel cmdline/base/pagesize in boot. +# - If using GKI, use GENERIC_KERNEL_CMDLINE. Remove kernel base and pagesize because they are +# device-specific. +# - If not using GKI: +# - If building vendor_boot, INTERNAL_KERNEL_CMDLINE, base and pagesize goes in vendor_boot. +# - Otherwise, put them in boot. +ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) + ifdef GENERIC_KERNEL_CMDLINE + INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(GENERIC_KERNEL_CMDLINE)" + endif +else ifndef BUILDING_VENDOR_BOOT_IMAGE # && BOARD_USES_GENERIC_KERNEL_IMAGE != true + ifdef INTERNAL_KERNEL_CMDLINE + INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(INTERNAL_KERNEL_CMDLINE)" + endif + ifdef BOARD_KERNEL_BASE + INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) + endif + ifdef BOARD_KERNEL_PAGESIZE + INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) + endif +endif # BUILDING_VENDOR_BOOT_IMAGE == "" && BOARD_USES_GENERIC_KERNEL_IMAGE != true + +ifdef BOARD_GKI_SIGNING_KEY_PATH + # GKI boot images will not set system version & SPL value in the header. + # They can be set by the device manufacturer in the AVB properties instead. + INTERNAL_MKBOOTIMG_VERSION_ARGS := +else + INTERNAL_MKBOOTIMG_VERSION_ARGS := \ + --os_version $(PLATFORM_VERSION_LAST_STABLE) \ + --os_patch_level $(PLATFORM_SECURITY_PATCH) +endif # BOARD_GKI_SIGNING_KEY_PATH + +# $(1): image target to certify +# $(2): out certificate target +# $(3): image name +# $(4): additional AVB arguments +define generate_generic_boot_image_certificate + rm -rf "$(2)" + mkdir -p "$(dir $(2))" + $(GENERATE_GKI_CERTIFICATE) $(INTERNAL_GKI_CERTIFICATE_ARGS) \ + --additional_avb_args "$(4)" \ + --name "$(3)" --output "$(2)" "$(1)" +endef + +INTERNAL_GKI_CERTIFICATE_ARGS := +INTERNAL_GKI_CERTIFICATE_DEPS := +ifdef BOARD_GKI_SIGNING_KEY_PATH + ifndef BOARD_GKI_SIGNING_ALGORITHM + $(error BOARD_GKI_SIGNING_ALGORITHM should be defined with BOARD_GKI_SIGNING_KEY_PATH) + endif + + INTERNAL_GKI_CERTIFICATE_ARGS := \ + --key "$(BOARD_GKI_SIGNING_KEY_PATH)" \ + --algorithm "$(BOARD_GKI_SIGNING_ALGORITHM)" \ + --avbtool "$(AVBTOOL)" + + # Quote and pass BOARD_GKI_SIGNING_SIGNATURE_ARGS as a single string argument. + ifdef BOARD_GKI_SIGNING_SIGNATURE_ARGS + INTERNAL_GKI_CERTIFICATE_ARGS += --additional_avb_args "$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)" + endif + + INTERNAL_GKI_CERTIFICATE_DEPS := \ + $(GENERATE_GKI_CERTIFICATE) \ + $(BOARD_GKI_SIGNING_KEY_PATH) \ + $(AVBTOOL) + +endif + +# Define these only if we are building boot +ifdef BUILDING_BOOT_IMAGE +INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET) + +ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true) +$(error TARGET_BOOTIMAGE_USE_EXT2 is not supported anymore) +endif # TARGET_BOOTIMAGE_USE_EXT2 + +$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b))))) + +ifeq (true,$(BOARD_AVB_ENABLE)) + +# $1: boot image target +define build_boot_board_avb_enabled + $(eval kernel := $(call bootimage-to-kernel,$(1))) + $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) + $(if $(BOARD_GKI_SIGNING_KEY_PATH), \ + $(eval boot_signature := $(call intermediates-dir-for,PACKAGING,generic_boot)/$(notdir $(1)).boot_signature) \ + $(eval kernel_signature := $(call intermediates-dir-for,PACKAGING,generic_kernel)/$(notdir $(kernel)).boot_signature) \ + $(call generate_generic_boot_image_certificate,$(1),$(boot_signature),boot,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \ + $(call generate_generic_boot_image_certificate,$(kernel),$(kernel_signature),generic_kernel,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \ + cat $(kernel_signature) >> $(boot_signature) $(newline) \ + $(call assert-max-image-size,$(boot_signature),16 << 10) $(newline) \ + truncate -s $$(( 16 << 10 )) $(boot_signature) $(newline) \ + cat "$(boot_signature)" >> $(1)) + $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot))) + $(AVBTOOL) add_hash_footer \ + --image $(1) \ + $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) \ + --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \ + $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) +endef + +$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS) + $(call pretty,"Target boot image: $@") + $(call build_boot_board_avb_enabled,$@) + +$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) +$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES) $(INTERNAL_GKI_CERTIFICATE_DEPS),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) + +.PHONY: bootimage-nodeps +bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS) + @echo "make $@: ignoring dependencies" + $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_board_avb_enabled,$(b))) + +else ifeq (true,$(PRODUCT_SUPPORTS_BOOT_SIGNER)) # BOARD_AVB_ENABLE != true + +# $1: boot image target +define build_boot_supports_boot_signer + $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) + $(BOOT_SIGNER) /boot $@ $(PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCT_VERITY_SIGNING_KEY).x509.pem $(1) + $(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),boot)) +endef + +$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(BOOT_SIGNER) + $(call pretty,"Target boot image: $@") + $(call build_boot_supports_boot_signer,$@) + +$(call declare-1p-container,$(INSTALLED_BOOTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) + +.PHONY: bootimage-nodeps +bootimage-nodeps: $(MKBOOTIMG) $(BOOT_SIGNER) + @echo "make $@: ignoring dependencies" + $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_supports_boot_signer,$(b))) + +else ifeq (true,$(PRODUCT_SUPPORTS_VBOOT)) # PRODUCT_SUPPORTS_BOOT_SIGNER != true + +# $1: boot image target +define build_boot_supports_vboot + $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1).unsigned + $(VBOOT_SIGNER) $(FUTILITY) $(1).unsigned $(PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $(1).keyblock $(1) + $(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),boot)) +endef + +$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(VBOOT_SIGNER) $(FUTILITY) + $(call pretty,"Target boot image: $@") + $(call build_boot_supports_vboot,$@) + +$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) +$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) + +.PHONY: bootimage-nodeps +bootimage-nodeps: $(MKBOOTIMG) $(VBOOT_SIGNER) $(FUTILITY) + @echo "make $@: ignoring dependencies" + $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_supports_vboot,$(b))) + +else # PRODUCT_SUPPORTS_VBOOT != true + +# $1: boot image target +define build_boot_novboot + $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) + $(call assert-max-image-size,$1,$(call get-bootimage-partition-size,$(1),boot)) +endef + +$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) + $(call pretty,"Target boot image: $@") + $(call build_boot_novboot,$@) + +$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) +$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) + +.PHONY: bootimage-nodeps +bootimage-nodeps: $(MKBOOTIMG) + @echo "make $@: ignoring dependencies" + $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_novboot,$(b))) + +endif # BOARD_AVB_ENABLE +endif # BUILDING_BOOT_IMAGE + +else # TARGET_NO_KERNEL == "true" +ifdef BOARD_PREBUILT_BOOTIMAGE +INTERNAL_PREBUILT_BOOTIMAGE := $(BOARD_PREBUILT_BOOTIMAGE) +INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_BOOTIMAGE) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) + cp $(INTERNAL_PREBUILT_BOOTIMAGE) $@ + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_BOOTIMAGE_PARTITION_SIZE)) \ + --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \ + $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) + +$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",bool) +$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_PREBUILT_BOOTIMAGE),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) +else +$(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_BOOTIMAGE) + cp $(INTERNAL_PREBUILT_BOOTIMAGE) $@ +endif # BOARD_AVB_ENABLE + +else # BOARD_PREBUILT_BOOTIMAGE not defined +INSTALLED_BOOTIMAGE_TARGET := +endif # BOARD_PREBUILT_BOOTIMAGE +endif # TARGET_NO_KERNEL +endif # my_installed_prebuilt_gki_apex not defined + +my_apex_extracted_boot_image := +my_installed_prebuilt_gki_apex := + +# ----------------------------------------------------------------- +# init boot image +ifeq ($(BUILDING_INIT_BOOT_IMAGE),true) + +INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img +$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_RAMDISK_TARGET) + +INTERNAL_INIT_BOOT_IMAGE_ARGS := --ramdisk $(INSTALLED_RAMDISK_TARGET) + +ifdef BOARD_KERNEL_PAGESIZE + INTERNAL_INIT_BOOT_IMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) +endif + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH) + $(call pretty,"Target init_boot image: $@") + $(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output "$@" + $(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \ + --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \ + $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS) + +$(call declare-1p-container,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),$(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE),$(PRODUCT_OUT)/:/) +else +$(INSTALLED_INIT_BOOT_IMAGE_TARGET): + $(call pretty,"Target init_boot image: $@") + $(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output $@ + $(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) + +$(call declare-1p-target,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) +endif + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_INIT_BOOT_IMAGE_TARGET) + +else # BUILDING_INIT_BOOT_IMAGE is not true + +ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE +INTERNAL_PREBUILT_INIT_BOOT_IMAGE := $(BOARD_PREBUILT_INIT_BOOT_IMAGE) +INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH) + cp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@ + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \ + --partition_name boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \ + $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS) + +$(call declare-1p-container,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),$(INTERNAL_PREBUILT_INIT_BOOT_IMAGE),$(PRODUCT_OUT)/:/) +else +$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) + cp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@ + +$(call declare-1p-target,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) +endif # BOARD_AVB_ENABLE + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_INIT_BOOT_IMAGE_TARGET) + +else # BOARD_PREBUILT_INIT_BOOT_IMAGE not defined +INSTALLED_INIT_BOOT_IMAGE_TARGET := +endif # BOARD_PREBUILT_INIT_BOOT_IMAGE + +endif # BUILDING_INIT_BOOT_IMAGE is not true + +# ----------------------------------------------------------------- +# vendor boot image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_VENDOR_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true) + +ifeq ($(PRODUCT_SUPPORTS_VERITY),true) + $(error vboot 1.0 does not support vendor_boot partition) +endif + +INTERNAL_VENDOR_RAMDISK_FILES := $(filter $(TARGET_VENDOR_RAMDISK_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INTERNAL_VENDOR_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot)/vendor_ramdisk.cpio$(RAMDISK_EXT) + +# Exclude recovery files in the default vendor ramdisk if including a standalone +# recovery ramdisk in vendor_boot. +ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) +ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) +$(INTERNAL_VENDOR_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) +$(INTERNAL_VENDOR_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT) +endif +endif + +$(INTERNAL_VENDOR_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS) + $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@ + +INSTALLED_VENDOR_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk.img +$(INSTALLED_VENDOR_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET) + @echo "Target vendor ramdisk: $@" + $(copy-file-to-target) + +$(call declare-1p-container,$(INSTALLED_VENDOR_RAMDISK_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_RAMDISK_TARGET),$(INTERNAL_VENDOR_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) + +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_RAMDISK_TARGET) + +INSTALLED_FILES_FILE_VENDOR_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk.txt +INSTALLED_FILES_JSON_VENDOR_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_RAMDISK:.txt=.json) +$(INSTALLED_FILES_FILE_VENDOR_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) +$(INSTALLED_FILES_FILE_VENDOR_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_TARGET) +$(INSTALLED_FILES_FILE_VENDOR_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_VENDOR_RAMDISK_OUT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_RAMDISK))) +$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_RAMDISK))) + +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG + ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true) + # If we have vendor_kernel_boot partition, we migrate dtb image to that image + # and allow dtb in vendor_boot to be empty. + INTERNAL_VENDOR_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) + endif +endif +ifdef BOARD_KERNEL_BASE + INTERNAL_VENDOR_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) +endif +ifdef BOARD_KERNEL_PAGESIZE + INTERNAL_VENDOR_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) +endif +ifdef INTERNAL_KERNEL_CMDLINE + INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_cmdline "$(INTERNAL_KERNEL_CMDLINE)" +endif + +ifdef INTERNAL_BOOTCONFIG + INTERNAL_VENDOR_BOOTCONFIG_TARGET := $(PRODUCT_OUT)/vendor-bootconfig.img + $(INTERNAL_VENDOR_BOOTCONFIG_TARGET): + rm -f $@ + $(foreach param,$(INTERNAL_BOOTCONFIG), \ + printf "%s\n" $(param) >> $@;) + INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_bootconfig $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) +endif + +# $(1): Build target name +# $(2): Staging dir to be compressed +# $(3): Build dependencies +define build-vendor-ramdisk-fragment-target +$(1): $(3) $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + $(MKBOOTFS) -d $(TARGET_OUT) $(2) | $(COMPRESSION_COMMAND) > $$@ +endef + +# $(1): Ramdisk name +define build-vendor-ramdisk-fragment +$(strip \ + $(eval build_target := $(call intermediates-dir-for,PACKAGING,vendor_ramdisk_fragments)/$(1).cpio$(RAMDISK_EXT)) \ + $(eval $(call build-vendor-ramdisk-fragment-target,$(build_target),$(VENDOR_RAMDISK_FRAGMENT.$(1).STAGING_DIR),$(VENDOR_RAMDISK_FRAGMENT.$(1).FILES))) \ + $(build_target) \ +) +endef + +# $(1): Ramdisk name +# $(2): Prebuilt file path +define build-prebuilt-vendor-ramdisk-fragment +$(strip \ + $(eval build_target := $(call intermediates-dir-for,PACKAGING,prebuilt_vendor_ramdisk_fragments)/$(1)) \ + $(eval $(call copy-one-file,$(2),$(build_target))) \ + $(build_target) \ +) +endef + +INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS := +INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS := + +$(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \ + $(eval prebuilt_vendor_ramdisk_fragment_file := $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)) \ + $(if $(prebuilt_vendor_ramdisk_fragment_file), \ + $(eval vendor_ramdisk_fragment_target := $(call build-prebuilt-vendor-ramdisk-fragment,$(vendor_ramdisk_fragment),$(prebuilt_vendor_ramdisk_fragment_file))) \ + $(eval ### else ###), \ + $(eval vendor_ramdisk_fragment_target := $(call build-vendor-ramdisk-fragment,$(vendor_ramdisk_fragment)))) \ + $(eval INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS += $(vendor_ramdisk_fragment_target)) \ + $(eval INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS += $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS) --vendor_ramdisk_fragment $(vendor_ramdisk_fragment_target)) \ +) + +INSTALLED_VENDOR_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot.img +$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DTBIMAGE_TARGET) +$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOTIMAGE_KEY_PATH) + $(call pretty,"Target vendor_boot image: $@") + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ + $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \ + --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_SIGNING_ARGS) \ + $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS) +else +$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): + $(call pretty,"Target vendor_boot image: $@") + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ + $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) +endif + +$(call declare-1p-container,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DTB_IMAGE_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(INTERNAL_VENDOR_BOOTCONDIG_TARGET),$(PRODUCT_OUT)/:/) +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) +endif # BUILDING_VENDOR_BOOT_IMAGE + +# ----------------------------------------------------------------- +# vendor kernel boot image +ifeq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true) + +INTERNAL_VENDOR_KERNEL_RAMDISK_FILES := $(filter $(TARGET_VENDOR_KERNEL_RAMDISK_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_kernel_boot)/vendor_kernel_ramdisk.cpio$(RAMDISK_EXT) + +$(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS) + $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_KERNEL_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@ + +INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_kernel_ramdisk.img +$(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET): $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) + @echo "Target vendor kernel ramdisk: $@" + $(copy-file-to-target) + +INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-kernel-ramdisk.txt +INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK:.txt=.json) +$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) +$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) +$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_VENDOR_KERNEL_RAMDISK_OUT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK)) + +INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS := --vendor_ramdisk $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) +INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_kernel_boot.img +$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) + +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG + INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) + $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET) +endif +ifdef BOARD_KERNEL_PAGESIZE + INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) +endif + + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_KERNEL_BOOTIMAGE_KEY_PATH) + $(call pretty,"Target vendor_kernel_boot image: $@") + $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_boot $@ + $(call assert-max-image-size,$@,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) \ + --partition_name vendor_kernel_boot $(INTERNAL_AVB_VENDOR_KERNEL_BOOT_SIGNING_ARGS) \ + $(BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS) +else +$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): + $(call pretty,"Target vendor_kernel_boot image: $@") + $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_boot $@ + $(call assert-max-image-size,$@,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) +endif +$(call declare-1p-container,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),) +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG +$(call declare-container-license-deps,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),\ + $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) $(INSTALLED_DTBIMAGE_TARGET),\ + $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):) +else +$(call declare-container-license-deps,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET),$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):) +endif +endif # BUILDING_VENDOR_KERNEL_BOOT_IMAGE + +# ----------------------------------------------------------------- +# NOTICE files +# +# We are required to publish the licenses for all code under BSD, GPL and +# Apache licenses (and possibly other more exotic ones as well). We err on the +# side of caution, so the licenses for other third-party code are included here +# too. +# +# This needs to be before the systemimage rules, because it adds to +# ALL_DEFAULT_INSTALLED_MODULES, which those use to pick which files +# go into the systemimage. +INSTALLED_LOADER_TARGET := $(strip $(wildcard $(TARGET_DEVICE_DIR)/ota/loader/RK*Loader*.bin)) + +INSTALLED_UBOOT_TARGET := $(strip $(wildcard u-boot/uboot.img)) +HAVE_TRUST_WITH_TA := $(shell test -f u-boot/trust_with_ta.img && echo yes) +ifeq ($(HAVE_TRUST_WITH_TA),yes) +INSTALLED_TRUST_TARGET := $(strip $(wildcard u-boot/trust_with_ta.img)) +else +INSTALLED_TRUST_TARGET := $(strip $(wildcard u-boot/trust.img)) +endif + +.PHONY: notice_files + +# Convert license metadata into xml notice file. +# $(1) - Output target notice filename +# $(2) - Product name +# $(3) - File title +# $(4) - License metadata file roots +# $(5) - Prefixes to strip +# +define xml-notice-rule +$(1): PRIVATE_PRODUCT := $(2) +$(1): PRIVATE_MESSAGE := $(3) +$(1): PRIVATE_DEPS := $(call corresponding-license-metadata,$(4)) +$(1): $(call corresponding-license-metadata,$(4)) $(XMLNOTICE) $(BUILD_SYSTEM)/Makefile + OUT_DIR=$(OUT_DIR) $(XMLNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $$(PRIVATE_DEPS) + +notice_files: $(1) +endef + +# Convert license metadata into text notice file. +# $(1) - Output target notice filename +# $(2) - Product name +# $(3) - File title +# $(4) - License metadata file roots +# $(5) - Prefixes to strip +# +define text-notice-rule +$(1): PRIVATE_PRODUCT := $(2) +$(1): PRIVATE_MESSAGE := $(3) +$(1): $(call corresponding-license-metadata,$(4)) $(TEXTNOTICE) $(BUILD_SYSTEM)/Makefile + OUT_DIR=$(OUT_DIR) $(TEXTNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $(call corresponding-license-metadata,$(4)) + +notice_files: $(1) +endef + +# Conversion license metadata into html notice file. +# $(1) - Output target notice filename +# $(2) - Product name +# $(3) - File title +# $(4) - License metadata file roots +# $(5) - Prefixes to strip +# +define html-notice-rule +$(1): PRIVATE_PRODUCT := $(2) +$(1): PRIVATE_MESSAGE := $(3) +$(1): $(call corresponding-license-metadata,$(4)) $(HTMLNOTICE) $(BUILD_SYSTEM)/Makefile + OUT_DIR=$(OUT_DIR) $(HTMLNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $(call corresponding-license-metadata,$(4)) + +notice_files: $(1) +endef + +$(KATI_obsolete_var combine-notice-files, To create notice files use xml-notice-rule, html-notice-rule, or text-notice-rule.) + +# Notice file logic isn't relevant for TARGET_BUILD_APPS +ifndef TARGET_BUILD_APPS + +# TODO These intermediate NOTICE.txt/NOTICE.html files should go into +# TARGET_OUT_NOTICE_FILES now that the notice files are gathered from +# the src subdirectory. +kernel_notice_file := $(TARGET_OUT_NOTICE_FILES)/src/kernel.txt + +# Some targets get included under $(PRODUCT_OUT) for debug symbols or other +# reasons--not to be flashed onto any device. Targets under these directories +# need no associated notice file on the device UI. +exclude_target_dirs := apex + +# TODO(b/69865032): Make PRODUCT_NOTICE_SPLIT the default behavior. +ifneq ($(PRODUCT_NOTICE_SPLIT),true) +#target_notice_file_html := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html +target_notice_file_html_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html.gz +installed_notice_html_or_xml_gz := $(TARGET_OUT)/etc/NOTICE.html.gz + +$(call declare-0p-target,$(target_notice_file_html_gz)) +$(call declare-0p-target,$(installed_notice_html_or_xml_gz)) +else +# target_notice_file_xml := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml +target_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml.gz +installed_notice_html_or_xml_gz := $(TARGET_OUT)/etc/NOTICE.xml.gz + +target_vendor_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR.txt +target_vendor_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR.xml.gz +installed_vendor_notice_xml_gz := $(TARGET_OUT_VENDOR)/etc/NOTICE.xml.gz + +target_product_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_PRODUCT.txt +target_product_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_PRODUCT.xml.gz +installed_product_notice_xml_gz := $(TARGET_OUT_PRODUCT)/etc/NOTICE.xml.gz + +target_system_ext_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_EXT.txt +target_system_ext_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_EXT.xml.gz +installed_system_ext_notice_xml_gz := $(TARGET_OUT_SYSTEM_EXT)/etc/NOTICE.xml.gz + +target_odm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM.txt +target_odm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM.xml.gz +installed_odm_notice_xml_gz := $(TARGET_OUT_ODM)/etc/NOTICE.xml.gz + +target_vendor_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR_DLKM.txt +target_vendor_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR_DLKM.xml.gz +installed_vendor_dlkm_notice_xml_gz := $(TARGET_OUT_VENDOR_DLKM)/etc/NOTICE.xml.gz + +target_odm_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM_DLKM.txt +target_odm_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM_DLKM.xml.gz +installed_odm_dlkm_notice_xml_gz := $(TARGET_OUT_ODM_DLKM)/etc/NOTICE.xml.gz + +target_system_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_DLKM.txt +target_system_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_DLKM.xml.gz +installed_system_dlkm_notice_xml_gz := $(TARGET_OUT_SYSTEM_DLKM)/etc/NOTICE.xml.gz + +# Notice files are copied to TARGET_OUT_NOTICE_FILES as a side-effect of their module +# being built. A notice xml file must depend on all modules that could potentially +# install a license file relevant to it. +license_modules := $(ALL_DEFAULT_INSTALLED_MODULES) $(kernel_notice_file) +# Only files copied to a system image need system image notices. +license_modules := $(filter $(PRODUCT_OUT)/%,$(license_modules)) +# Phonys/fakes don't have notice files (though their deps might) +license_modules := $(filter-out $(TARGET_OUT_FAKE)/%,$(license_modules)) +# testcases are not relevant to the system image. +license_modules := $(filter-out $(TARGET_OUT_TESTCASES)/%,$(license_modules)) +# filesystem images: system, vendor, product, system_ext, odm, vendor_dlkm, and odm_dlkm +license_modules_system := $(filter $(TARGET_OUT)/%,$(license_modules)) +# system_other is relevant to system partition. +license_modules_system += $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(license_modules)) +license_modules_vendor := $(filter $(TARGET_OUT_VENDOR)/%,$(license_modules)) +license_modules_product := $(filter $(TARGET_OUT_PRODUCT)/%,$(license_modules)) +license_modules_system_ext := $(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(license_modules)) +license_modules_odm := $(filter $(TARGET_OUT_ODM)/%,$(license_modules)) +license_modules_vendor_dlkm := $(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(license_modules)) +license_modules_odm_dlkm := $(filter $(TARGET_OUT_ODM_DLKM)/%,$(license_modules)) +license_modules_odm_dlkm := $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(license_modules)) +license_modules_agg := $(license_modules_system) \ + $(license_modules_vendor) \ + $(license_modules_product) \ + $(license_modules_system_ext) \ + $(license_modules_odm) \ + $(license_modules_vendor_dlkm) \ + $(license_modules_odm_dlkm) \ + $(license_modules_system_dlkm) +# targets used for debug symbols only and do not get copied to the device +license_modules_symbols_only := $(filter $(PRODUCT_OUT)/apex/%,$(license_modules)) + +license_modules_rest := $(filter-out $(license_modules_agg),$(license_modules)) +license_modules_rest := $(filter-out $(license_modules_symbols_only),$(license_modules_rest)) + +# Identify the other targets we expect to have notices for: +# targets copied to the device but are not readable by the UI (e.g. must boot +# into a different partition to read or don't have an associated /etc +# directory) must have their notices built somewhere readable. +license_modules_rehomed := $(filter-out $(PRODUCT_OUT)/%/%,$(license_modules_rest)) # files in root have no /etc +license_modules_rehomed += $(filter $(PRODUCT_OUT)/recovery/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/root/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/data/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/ramdisk/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/debug_ramdisk/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/vendor_ramdisk/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/persist/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/persist.img,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/system_other/%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/kernel%,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/%.img,$(license_modules_rest)) +license_modules_rehomed += $(filter $(PRODUCT_OUT)/%.bin,$(license_modules_rest)) + +# after removing targets in system images, targets reported in system images, and +# targets used for debug symbols that do not need notices, nothing must remain. +license_modules_rest := $(filter-out $(license_modules_rehomed),$(license_modules_rest)) +$(call maybe-print-list-and-error, $(license_modules_rest), \ + "Targets added under $(PRODUCT_OUT)/ unaccounted for notice handling.") + +# If we are building in a configuration that includes a prebuilt vendor.img, we can't +# update its notice file, so include those notices in the system partition instead +ifdef BOARD_PREBUILT_VENDORIMAGE +license_modules_system += $(license_modules_rehomed) +system_xml_directories := xml_excluded_vendor_product_odm_vendor_dlkm_odm_dlkm +system_notice_file_message := "Notices for files contained in all filesystem images except vendor/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:" +else +license_modules_vendor += $(license_modules_rehomed) +system_xml_directories := xml_system +system_notice_file_message := "Notices for files contained in the system filesystem image in this directory:" +endif + +endif # PRODUCT_NOTICE_SPLIT + +ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_or_xml_gz) + +need_vendor_notice:=false +ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true) + need_vendor_notice:=true +endif + +ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE + need_vendor_notice:=true +endif + +ifdef BUILDING_VENDOR_IMAGE + need_vendor_notice:=true +endif + +ifeq (true,$(need_vendor_notice)) +ifneq (,$(installed_vendor_notice_xml_gz)) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_notice_xml_gz) +endif +endif + +need_vendor_notice:= + +ifdef BUILDING_ODM_IMAGE +ifneq (,$(installed_odm_notice_xml_gz)) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_odm_notice_xml_gz) +endif +endif + +ifdef BUILDING_PRODUCT_IMAGE +ifneq (,$(installed_product_notice_xml_gz)) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_product_notice_xml_gz) +endif +endif + +ifdef BUILDING_SYSTEM_EXT_IMAGE +ifneq (,$(installed_system_ext_notice_xml_gz)) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_system_ext_notice_xml_gz) +endif +endif + +ifdef BUILDING_VENDOR_DLKM_IMAGE +ifneq (,$(installed_vendor_dlkm_notice_xml_gz) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_dlkm_notice_xml_gz) +endif +endif + +ifdef BUILDING_ODM_DLKM_IMAGE +ifneq (,$(installed_odm_dlkm_notice_xml_gz)) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_odm_dlkm_notice_xml_gz) +endif +endif + +ifdef BUILDING_SYSTEM_DLKM_IMAGE +ifneq (,$(installed_system_dlkm_notice_xml_gz)) +ALL_DEFAULT_INSTALLED_MODULES += $(installed_system_dlkm_notice_xml_gz) +endif +endif + +endif # TARGET_BUILD_APPS + +# Presently none of the prebuilts etc. comply with policy to have a license text. Fake one here. +$(eval $(call copy-one-file,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,$(kernel_notice_file))) + +ifneq (,$(strip $(INSTALLED_KERNEL_TARGET))) +$(call declare-license-metadata,$(INSTALLED_KERNEL_TARGET),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel) +endif + +# No matter where it gets copied from, a copied linux kernel is licensed under "GPL 2.0 only" +$(eval $(call declare-copy-files-license-metadata,,:kernel,SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,kernel)) + + +# ################################################################# +# Targets for user images +# ################################################################# + +INTERNAL_USERIMAGES_EXT_VARIANT := +ifeq ($(TARGET_USERIMAGES_USE_EXT2),true) +INTERNAL_USERIMAGES_EXT_VARIANT := ext2 +else +ifeq ($(TARGET_USERIMAGES_USE_EXT3),true) +INTERNAL_USERIMAGES_EXT_VARIANT := ext3 +else +ifeq ($(TARGET_USERIMAGES_USE_EXT4),true) +INTERNAL_USERIMAGES_EXT_VARIANT := ext4 +endif +endif +endif + +# These options tell the recovery updater/installer how to mount the partitions writebale. +# =[|]... +# fstype_opts := [,]... +# opt := [=] +# The following worked on Nexus devices with Kernel 3.1, 3.4, 3.10 +DEFAULT_TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS := ext4=max_batch_time=0,commit=1,data=ordered,barrier=1,errors=panic,nodelalloc + +ifneq (true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)) + INTERNAL_USERIMAGES_SPARSE_EXT_FLAG := -s +endif +ifneq (true,$(TARGET_USERIMAGES_SPARSE_EROFS_DISABLED)) + INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG := -s +endif +ifneq (true,$(TARGET_USERIMAGES_SPARSE_SQUASHFS_DISABLED)) + INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG := -s +endif +ifneq (true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)) + INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG := -S +endif + +INTERNAL_USERIMAGES_DEPS := \ + $(BUILD_IMAGE) \ + $(MKE2FS_CONF) \ + $(MKEXTUSERIMG) + +$(call declare-1p-target,$(MKE2FS_CONF),system/extras) + +ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) +INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) +endif + +ifneq ($(filter \ + $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ + ,erofs),) +INTERNAL_USERIMAGES_DEPS += $(MKEROFS) +ifeq ($(BOARD_EROFS_USE_LEGACY_COMPRESSION),true) +BOARD_EROFS_COMPRESSOR ?= "lz4" +else +BOARD_EROFS_COMPRESSOR ?= "lz4hc,9" +endif +endif + +ifneq ($(filter \ + $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ + $(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ + ,squashfs),) +INTERNAL_USERIMAGES_DEPS += $(MKSQUASHFSUSERIMG) +endif + +ifeq (true,$(PRODUCT_SUPPORTS_VERITY)) +INTERNAL_USERIMAGES_DEPS += $(BUILD_VERITY_METADATA) $(BUILD_VERITY_TREE) $(APPEND2SIMG) $(VERITY_SIGNER) +ifeq (true,$(PRODUCT_SUPPORTS_VERITY_FEC)) +INTERNAL_USERIMAGES_DEPS += $(FEC) +endif +endif + +ifeq ($(BOARD_AVB_ENABLE),true) +INTERNAL_USERIMAGES_DEPS += $(AVBTOOL) +endif + +# Get a colon-separated list of search paths. +INTERNAL_USERIMAGES_BINARY_PATHS := $(subst $(space),:,$(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))) + +# Collects file_contexts files from modules to be installed +$(call merge-fc-files, \ + $(sort $(foreach m,$(product_MODULES),$(ALL_MODULES.$(m).FILE_CONTEXTS))),\ + $(call intermediates-dir-for,ETC,file_contexts.bin)/file_contexts.modules.tmp) + +SELINUX_FC := $(call intermediates-dir-for,ETC,file_contexts.bin)/file_contexts.bin + +INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC) + +ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) + +ifeq ($(PRODUCT_SUPPORTS_VERITY),true) + $(error vboot 1.0 doesn't support logical partition) +endif + +endif # PRODUCT_USE_DYNAMIC_PARTITIONS + +# $(1) the partition name (eg system) +# $(2) the image prop file +define add-common-flags-to-image-props +$(eval _var := $(call to-upper,$(1))) +$(hide) echo "$(1)_selinux_fc=$(SELINUX_FC)" >> $(2) +$(hide) echo "building_$(1)_image=$(BUILDING_$(_var)_IMAGE)" >> $(2) +endef + +# $(1) the partition name (eg system) +# $(2) the image prop file +define add-common-ro-flags-to-image-props +$(eval _var := $(call to-upper,$(1))) +$(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR),$(hide) echo "$(1)_erofs_compressor=$(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE),$(hide) echo "$(1)_erofs_pcluster_size=$(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT),$(hide) echo "$(1)_extfs_inode_count=$(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT),$(hide) echo "$(1)_extfs_rsv_pct=$(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "$(1)_f2fs_sldc_flags=$(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "$(1)_f2fs_compress=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE),$(hide) echo "$(1)_fs_type=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_JOURNAL_SIZE),$(hide) echo "$(1)_journal_size=$(BOARD_$(_var)IMAGE_JOURNAL_SIZE)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "$(1)_reserved_size=$(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_PARTITION_SIZE),$(hide) echo "$(1)_size=$(BOARD_$(_var)IMAGE_PARTITION_SIZE)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "$(1)_squashfs_block_size=$(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "$(1)_squashfs_compressor=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "$(1)_squashfs_compressor_opt=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(2)) +$(if $(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "$(1)_squashfs_disable_4k_align=$(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(2)) +$(if $(PRODUCT_$(_var)_BASE_FS_PATH),$(hide) echo "$(1)_base_fs_file=$(PRODUCT_$(_var)_BASE_FS_PATH)" >> $(2)) +$(eval _size := $(BOARD_$(_var)IMAGE_PARTITION_SIZE)) +$(eval _reserved := $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)) +$(eval _headroom := $(PRODUCT_$(_var)_HEADROOM)) +$(if $(or $(_size), $(_reserved), $(_headroom)),, + $(hide) echo "$(1)_disable_sparse=true" >> $(2)) +$(call add-common-flags-to-image-props,$(1),$(2)) +endef + +# $(1): the path of the output dictionary file +# $(2): a subset of "system vendor cache userdata product system_ext oem odm vendor_dlkm odm_dlkm system_dlkm" +# $(3): additional "key=value" pairs to append to the dictionary file. +define generate-image-prop-dictionary +$(if $(filter $(2),system),\ + $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo "system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)" >> $(1)) + $(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo "system_headroom=$(PRODUCT_SYSTEM_HEADROOM)" >> $(1)) + $(call add-common-ro-flags-to-image-props,system,$(1)) +) +$(if $(filter $(2),system_other),\ + $(hide) echo "building_system_other_image=$(BUILDING_SYSTEM_OTHER_IMAGE)" >> $(1) + $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),, + $(hide) echo "system_other_disable_sparse=true" >> $(1)) +) +$(if $(filter $(2),userdata),\ + $(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1)) + $(if $(BOARD_USERDATAIMAGE_PARTITION_SIZE),$(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)" >> $(1)) + $(if $(PRODUCT_FS_CASEFOLD),$(hide) echo "needs_casefold=$(PRODUCT_FS_CASEFOLD)" >> $(1)) + $(if $(PRODUCT_QUOTA_PROJID),$(hide) echo "needs_projid=$(PRODUCT_QUOTA_PROJID)" >> $(1)) + $(if $(PRODUCT_FS_COMPRESSION),$(hide) echo "needs_compress=$(PRODUCT_FS_COMPRESSION)" >> $(1)) + $(call add-common-flags-to-image-props,userdata,$(1)) +) +$(if $(filter $(2),cache),\ + $(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1)) + $(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1)) + $(call add-common-flags-to-image-props,cache,$(1)) +) +$(if $(filter $(2),vendor),\ + $(call add-common-ro-flags-to-image-props,vendor,$(1)) +) +$(if $(filter $(2),product),\ + $(call add-common-ro-flags-to-image-props,product,$(1)) +) +$(if $(filter $(2),system_ext),\ + $(call add-common-ro-flags-to-image-props,system_ext,$(1)) +) +$(if $(filter $(2),odm),\ + $(call add-common-ro-flags-to-image-props,odm,$(1)) +) +$(if $(filter $(2),vendor_dlkm),\ + $(call add-common-ro-flags-to-image-props,vendor_dlkm,$(1)) +) +$(if $(filter $(2),odm_dlkm),\ + $(call add-common-ro-flags-to-image-props,odm_dlkm,$(1)) +) +$(if $(filter $(2),system_dlkm),\ + $(call add-common-ro-flags-to-image-props,system_dlkm,$(1)) +) +$(if $(filter $(2),oem),\ + $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1)) + $(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1)) + $(if $(BOARD_OEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "oem_extfs_inode_count=$(BOARD_OEMIMAGE_EXTFS_INODE_COUNT)" >> $(1)) + $(if $(BOARD_OEMIMAGE_EXTFS_RSV_PCT),$(hide) echo "oem_extfs_rsv_pct=$(BOARD_OEMIMAGE_EXTFS_RSV_PCT)" >> $(1)) + $(call add-common-flags-to-image-props,oem,$(1)) +) +$(hide) echo "ext_mkuserimg=$(notdir $(MKEXTUSERIMG))" >> $(1) + +$(if $(INTERNAL_USERIMAGES_EXT_VARIANT),$(hide) echo "fs_type=$(INTERNAL_USERIMAGES_EXT_VARIANT)" >> $(1)) +$(if $(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG),$(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(1)) +$(if $(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG),$(hide) echo "erofs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG)" >> $(1)) +$(if $(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG),$(hide) echo "squashfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG)" >> $(1)) +$(if $(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG),$(hide) echo "f2fs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG)" >> $(1)) +$(if $(BOARD_EROFS_COMPRESSOR),$(hide) echo "erofs_default_compressor=$(BOARD_EROFS_COMPRESSOR)" >> $(1)) +$(if $(BOARD_EROFS_PCLUSTER_SIZE),$(hide) echo "erofs_pcluster_size=$(BOARD_EROFS_PCLUSTER_SIZE)" >> $(1)) +$(if $(BOARD_EROFS_SHARE_DUP_BLOCKS),$(hide) echo "erofs_share_dup_blocks=$(BOARD_EROFS_SHARE_DUP_BLOCKS)" >> $(1)) +$(if $(BOARD_EROFS_USE_LEGACY_COMPRESSION),$(hide) echo "erofs_use_legacy_compression=$(BOARD_EROFS_USE_LEGACY_COMPRESSION)" >> $(1)) +$(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo "ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)" >> $(1)) +$(if $(BOARD_FLASH_LOGICAL_BLOCK_SIZE), $(hide) echo "flash_logical_block_size=$(BOARD_FLASH_LOGICAL_BLOCK_SIZE)" >> $(1)) +$(if $(BOARD_FLASH_ERASE_BLOCK_SIZE), $(hide) echo "flash_erase_block_size=$(BOARD_FLASH_ERASE_BLOCK_SIZE)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_BOOT_SIGNER),$(hide) echo "boot_signer=$(PRODUCT_SUPPORTS_BOOT_SIGNER)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity=$(PRODUCT_SUPPORTS_VERITY)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity_key=$(PRODUCT_VERITY_SIGNING_KEY)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity_signer_cmd=$(notdir $(VERITY_SIGNER))" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VERITY_FEC),$(hide) echo "verity_fec=$(PRODUCT_SUPPORTS_VERITY_FEC)" >> $(1)) +$(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo "verity_disable=true" >> $(1)) +$(if $(PRODUCT_SYSTEM_VERITY_PARTITION),$(hide) echo "system_verity_block_device=$(PRODUCT_SYSTEM_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_VENDOR_VERITY_PARTITION),$(hide) echo "vendor_verity_block_device=$(PRODUCT_VENDOR_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_PRODUCT_VERITY_PARTITION),$(hide) echo "product_verity_block_device=$(PRODUCT_PRODUCT_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_SYSTEM_EXT_VERITY_PARTITION),$(hide) echo "system_ext_verity_block_device=$(PRODUCT_SYSTEM_EXT_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_VENDOR_DLKM_VERITY_PARTITION),$(hide) echo "vendor_dlkm_verity_block_device=$(PRODUCT_VENDOR_DLKM_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_ODM_DLKM_VERITY_PARTITION),$(hide) echo "odm_dlkm_verity_block_device=$(PRODUCT_ODM_DLKM_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_SYSTEM_DLKM_VERITY_PARTITION),$(hide) echo "system_dlkm_verity_block_device=$(PRODUCT_SYSTEM_DLKM_VERITY_PARTITION)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot=$(PRODUCT_SUPPORTS_VBOOT)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_key=$(PRODUCT_VBOOT_SIGNING_KEY)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_subkey=$(PRODUCT_VBOOT_SIGNING_SUBKEY)" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VBOOT),$(hide) echo "futility=$(notdir $(FUTILITY))" >> $(1)) +$(if $(PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_signer_cmd=$(VBOOT_SIGNER)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_avbtool=$(notdir $(AVBTOOL))" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_SYSTEM_KEY_PATH),\ + $(hide) echo "avb_system_key_path=$(BOARD_AVB_SYSTEM_KEY_PATH)" >> $(1) + $(hide) echo "avb_system_algorithm=$(BOARD_AVB_SYSTEM_ALGORITHM)" >> $(1) + $(hide) echo "avb_system_rollback_index_location=$(BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_other_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_other_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH),\ + $(hide) echo "avb_system_other_key_path=$(BOARD_AVB_SYSTEM_OTHER_KEY_PATH)" >> $(1) + $(hide) echo "avb_system_other_algorithm=$(BOARD_AVB_SYSTEM_OTHER_ALGORITHM)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_vendor_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_vendor_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_VENDOR_KEY_PATH),\ + $(hide) echo "avb_vendor_key_path=$(BOARD_AVB_VENDOR_KEY_PATH)" >> $(1) + $(hide) echo "avb_vendor_algorithm=$(BOARD_AVB_VENDOR_ALGORITHM)" >> $(1) + $(hide) echo "avb_vendor_rollback_index_location=$(BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_product_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_product_add_hashtree_footer_args=$(BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_PRODUCT_KEY_PATH),\ + $(hide) echo "avb_product_key_path=$(BOARD_AVB_PRODUCT_KEY_PATH)" >> $(1) + $(hide) echo "avb_product_algorithm=$(BOARD_AVB_PRODUCT_ALGORITHM)" >> $(1) + $(hide) echo "avb_product_rollback_index_location=$(BOARD_AVB_PRODUCT_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_ext_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(hide) echo "avb_system_ext_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_SYSTEM_EXT_KEY_PATH),\ + $(hide) echo "avb_system_ext_key_path=$(BOARD_AVB_SYSTEM_EXT_KEY_PATH)" >> $(1) + $(hide) echo "avb_system_ext_algorithm=$(BOARD_AVB_SYSTEM_EXT_ALGORITHM)" >> $(1) + $(hide) echo "avb_system_ext_rollback_index_location=$(BOARD_AVB_SYSTEM_EXT_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_odm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_odm_add_hashtree_footer_args=$(BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_ODM_KEY_PATH),\ + $(hide) echo "avb_odm_key_path=$(BOARD_AVB_ODM_KEY_PATH)" >> $(1) + $(hide) echo "avb_odm_algorithm=$(BOARD_AVB_ODM_ALGORITHM)" >> $(1) + $(hide) echo "avb_odm_rollback_index_location=$(BOARD_AVB_ODM_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_vendor_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(hide) echo "avb_vendor_dlkm_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_VENDOR_DLKM_KEY_PATH),\ + $(hide) echo "avb_vendor_dlkm_key_path=$(BOARD_AVB_VENDOR_DLKM_KEY_PATH)" >> $(1) + $(hide) echo "avb_vendor_dlkm_algorithm=$(BOARD_AVB_VENDOR_DLKM_ALGORITHM)" >> $(1) + $(hide) echo "avb_vendor_dlkm_rollback_index_location=$(BOARD_AVB_VENDOR_DLKM_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_odm_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(hide) echo "avb_odm_dlkm_add_hashtree_footer_args=$(BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_ODM_DLKM_KEY_PATH),\ + $(hide) echo "avb_odm_dlkm_key_path=$(BOARD_AVB_ODM_DLKM_KEY_PATH)" >> $(1) + $(hide) echo "avb_odm_dlkm_algorithm=$(BOARD_AVB_ODM_DLKM_ALGORITHM)" >> $(1) + $(hide) echo "avb_odm_dlkm_rollback_index_location=$(BOARD_AVB_ODM_DLKM_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(hide) echo "avb_system_dlkm_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)) +$(if $(BOARD_AVB_ENABLE),\ + $(if $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH),\ + $(hide) echo "avb_system_dlkm_key_path=$(BOARD_AVB_SYSTEM_DLKM_KEY_PATH)" >> $(1) + $(hide) echo "avb_system_dlkm_algorithm=$(BOARD_AVB_SYSTEM_DLKM_ALGORITHM)" >> $(1) + $(hide) echo "avb_system_dlkm_rollback_index_location=$(BOARD_SYSTEM_SYSTEM_DLKM_ROLLBACK_INDEX_LOCATION)" >> $(1))) +$(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\ + $(hide) echo "recovery_as_boot=true" >> $(1)) +$(if $(filter true,$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)),\ + $(hide) echo "system_root_image=true" >> $(1)) +$(if $(filter true,$(BOARD_BUILD_GKI_BOOT_IMAGE_WITHOUT_RAMDISK)),\ + $(hide) echo "gki_boot_image_without_ramdisk=true" >> $(1)) +$(hide) echo "root_dir=$(TARGET_ROOT_OUT)" >> $(1) +$(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)),\ + $(hide) echo "use_dynamic_partition_size=true" >> $(1)) +$(if $(3),$(hide) $(foreach kv,$(3),echo "$(kv)" >> $(1);)) +endef + +# $(1): the path of the output dictionary file +# $(2): additional "key=value" pairs to append to the dictionary file. +PROP_DICTIONARY_IMAGES := oem +ifdef BUILDING_CACHE_IMAGE + PROP_DICTIONARY_IMAGES += cache +endif +ifdef BUILDING_SYSTEM_IMAGE + PROP_DICTIONARY_IMAGES += system +endif +ifdef BUILDING_SYSTEM_OTHER_IMAGE + PROP_DICTIONARY_IMAGES += system_other +endif +ifdef BUILDING_USERDATA_IMAGE + PROP_DICTIONARY_IMAGES += userdata +endif +ifdef BUILDING_VENDOR_IMAGE + PROP_DICTIONARY_IMAGES += vendor +endif +ifdef BUILDING_PRODUCT_IMAGE + PROP_DICTIONARY_IMAGES += product +endif +ifdef BUILDING_SYSTEM_EXT_IMAGE + PROP_DICTIONARY_IMAGES += system_ext +endif +ifdef BUILDING_ODM_IMAGE + PROP_DICTIONARY_IMAGES += odm +endif +ifdef BUILDING_VENDOR_DLKM_IMAGE + PROP_DICTIONARY_IMAGES += vendor_dlkm +endif +ifdef BUILDING_ODM_DLKM_IMAGE + PROP_DICTIONARY_IMAGES += odm_dlkm +endif +ifdef BUILDING_SYSTEM_DLKM_IMAGE + PROP_DICTIONARY_IMAGES += system_dlkm +endif +define generate-userimage-prop-dictionary + $(call generate-image-prop-dictionary,$(1),$(PROP_DICTIONARY_IMAGES),$(2)) +endef + +# $(1): the path of the input dictionary file, where each line has the format key=value +# $(2): the key to look up +define read-image-prop-dictionary +$$(grep '$(2)=' $(1) | cut -f2- -d'=') +endef + +# ----------------------------------------------------------------- +# Recovery image + +# Recovery image exists if we are building recovery, or building recovery as boot. +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RECOVERY_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_RECOVERY_IMAGE + +INTERNAL_RECOVERYIMAGE_FILES := $(filter $(TARGET_RECOVERY_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_RECOVERY := $(PRODUCT_OUT)/installed-files-recovery.txt +INSTALLED_FILES_JSON_RECOVERY := $(INSTALLED_FILES_FILE_RECOVERY:.txt=.json) + +ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) +INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET) +endif + +# TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other +# INSTALLED_FILES_FILE_* rules. Because currently there're cp/rsync/rm commands in +# build-recoveryimage-target, which would touch the files under TARGET_RECOVERY_OUT and race with +# the call to FILELIST. +$(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) + +$(INSTALLED_FILES_FILE_RECOVERY): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RECOVERY) +$(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERYIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_RECOVERY_ROOT_OUT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_RECOVERY))) +$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_RECOVERY))) + +recovery_sepolicy := \ + $(TARGET_RECOVERY_ROOT_OUT)/sepolicy \ + $(TARGET_RECOVERY_ROOT_OUT)/plat_file_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/plat_service_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/plat_property_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/system_ext_file_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/system_ext_service_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/system_ext_property_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/vendor_file_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/vendor_service_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/vendor_property_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/odm_file_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/odm_property_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/product_file_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/product_service_contexts \ + $(TARGET_RECOVERY_ROOT_OUT)/product_property_contexts + +# Passed into rsync from non-recovery root to recovery root, to avoid overwriting recovery-specific +# SELinux files +IGNORE_RECOVERY_SEPOLICY := $(patsubst $(TARGET_RECOVERY_OUT)/%,--exclude=/%,$(recovery_sepolicy)) + +# if building multiple boot images from multiple kernels, use the first kernel listed +# for the recovery image +recovery_kernel := $(firstword $(INSTALLED_KERNEL_TARGET)) +recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img +recovery_resources_common := bootable/recovery/res + +# Set recovery_density to a density bucket based on TARGET_SCREEN_DENSITY, PRODUCT_AAPT_PREF_CONFIG, +# or mdpi, in order of preference. We support both specific buckets (e.g. xdpi) and numbers, +# which get remapped to a bucket. +recovery_density := $(or $(TARGET_SCREEN_DENSITY),$(PRODUCT_AAPT_PREF_CONFIG),mdpi) +ifeq (,$(filter xxxhdpi xxhdpi xhdpi hdpi mdpi,$(recovery_density))) +recovery_density_value := $(patsubst %dpi,%,$(recovery_density)) +# We roughly use the medium point between the primary densities to split buckets. +# ------160------240------320----------480------------640------ +# mdpi hdpi xhdpi xxhdpi xxxhdpi +recovery_density := $(strip \ + $(or $(if $(filter $(shell echo $$(($(recovery_density_value) >= 560))),1),xxxhdpi),\ + $(if $(filter $(shell echo $$(($(recovery_density_value) >= 400))),1),xxhdpi),\ + $(if $(filter $(shell echo $$(($(recovery_density_value) >= 280))),1),xhdpi),\ + $(if $(filter $(shell echo $$(($(recovery_density_value) >= 200))),1),hdpi,mdpi))) +endif + +ifneq (,$(wildcard $(recovery_resources_common)-$(recovery_density))) +recovery_resources_common := $(recovery_resources_common)-$(recovery_density) +else +recovery_resources_common := $(recovery_resources_common)-xhdpi +endif + +# Select the 18x32 font on high-density devices (xhdpi and up); and the 12x22 font on other devices. +# Note that the font selected here can be overridden for a particular device by putting a font.png +# in its private recovery resources. +ifneq (,$(filter xxxhdpi xxhdpi xhdpi,$(recovery_density))) +recovery_font := bootable/recovery/fonts/18x32.png +else +recovery_font := bootable/recovery/fonts/12x22.png +endif + + +# We will only generate the recovery background text images if the variable +# TARGET_RECOVERY_UI_SCREEN_WIDTH is defined. For devices with xxxhdpi and xxhdpi, we set the +# variable to the commonly used values here, if it hasn't been intialized elsewhere. While for +# devices with lower density, they must have TARGET_RECOVERY_UI_SCREEN_WIDTH defined in their +# BoardConfig in order to use this feature. +ifeq ($(recovery_density),xxxhdpi) +TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1440 +else ifeq ($(recovery_density),xxhdpi) +TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1080 +endif + +ifneq ($(TARGET_RECOVERY_UI_SCREEN_WIDTH),) +# Subtracts the margin width and menu indent from the screen width; it's safe to be conservative. +ifeq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),) + recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - 10)) +else + recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - $(TARGET_RECOVERY_UI_MARGIN_WIDTH) - 10)) +endif + + +RECOVERY_INSTALLING_TEXT_FILE := $(call intermediates-dir-for,ETC,recovery_text_res)/installing_text.png +RECOVERY_INSTALLING_SECURITY_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/installing_security_text.png +RECOVERY_ERASING_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/erasing_text.png +RECOVERY_ERROR_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/error_text.png +RECOVERY_NO_COMMAND_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/no_command_text.png + +RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/cancel_wipe_data_text.png +RECOVERY_FACTORY_DATA_RESET_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/factory_data_reset_text.png +RECOVERY_TRY_AGAIN_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/try_again_text.png +RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_confirmation_text.png +RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_menu_header_text.png + +generated_recovery_text_files := \ + $(RECOVERY_INSTALLING_TEXT_FILE) \ + $(RECOVERY_INSTALLING_SECURITY_TEXT_FILE) \ + $(RECOVERY_ERASING_TEXT_FILE) \ + $(RECOVERY_ERROR_TEXT_FILE) \ + $(RECOVERY_NO_COMMAND_TEXT_FILE) \ + $(RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE) \ + $(RECOVERY_FACTORY_DATA_RESET_TEXT_FILE) \ + $(RECOVERY_TRY_AGAIN_TEXT_FILE) \ + $(RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE) \ + $(RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE) + +resource_dir := bootable/recovery/tools/recovery_l10n/res/ +resource_dir_deps := $(sort $(shell find $(resource_dir) -name *.xml -not -name .*)) +image_generator_jar := $(HOST_OUT_JAVA_LIBRARIES)/RecoveryImageGenerator.jar +zopflipng := $(HOST_OUT_EXECUTABLES)/zopflipng +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_SOURCE_FONTS := $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_FONT_FILES_DIR := $(call intermediates-dir-for,ETC,recovery_font_files) +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RESOURCE_DIR := $(resource_dir) +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_IMAGE_GENERATOR_JAR := $(image_generator_jar) +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_ZOPFLIPNG := $(zopflipng) +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_IMAGE_WIDTH := $(recovery_image_width) +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST := \ + recovery_installing \ + recovery_installing_security \ + recovery_erasing \ + recovery_error \ + recovery_no_command +$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST := \ + recovery_cancel_wipe_data \ + recovery_factory_data_reset \ + recovery_try_again \ + recovery_wipe_data_menu_header \ + recovery_wipe_data_confirmation +$(RECOVERY_INSTALLING_TEXT_FILE): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(RECOVERY_INSTALLING_TEXT_FILE),$(generated_recovery_text_files)) +$(RECOVERY_INSTALLING_TEXT_FILE): $(image_generator_jar) $(resource_dir_deps) $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) $(zopflipng) + # Prepares the font directory. + @rm -rf $(PRIVATE_RECOVERY_FONT_FILES_DIR) + @mkdir -p $(PRIVATE_RECOVERY_FONT_FILES_DIR) + $(foreach filename,$(PRIVATE_SOURCE_FONTS), cp $(filename) $(PRIVATE_RECOVERY_FONT_FILES_DIR) &&) true + @rm -rf $(dir $@) + @mkdir -p $(dir $@) + $(foreach text_name,$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST) $(PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST), \ + $(eval output_file := $(dir $@)/$(patsubst recovery_%,%_text.png,$(text_name))) \ + $(eval center_alignment := $(if $(filter $(text_name),$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST)), --center_alignment)) \ + java -jar $(PRIVATE_IMAGE_GENERATOR_JAR) \ + --image_width $(PRIVATE_RECOVERY_IMAGE_WIDTH) \ + --text_name $(text_name) \ + --font_dir $(PRIVATE_RECOVERY_FONT_FILES_DIR) \ + --resource_dir $(PRIVATE_RESOURCE_DIR) \ + --output_file $(output_file) $(center_alignment) && \ + $(PRIVATE_ZOPFLIPNG) -y --iterations=1 --filters=0 $(output_file) $(output_file) > /dev/null &&) true +else +RECOVERY_INSTALLING_TEXT_FILE := +RECOVERY_INSTALLING_SECURITY_TEXT_FILE := +RECOVERY_ERASING_TEXT_FILE := +RECOVERY_ERROR_TEXT_FILE := +RECOVERY_NO_COMMAND_TEXT_FILE := +RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE := +RECOVERY_FACTORY_DATA_RESET_TEXT_FILE := +RECOVERY_TRY_AGAIN_TEXT_FILE := +RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE := +RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE := +endif # TARGET_RECOVERY_UI_SCREEN_WIDTH + +ifndef TARGET_PRIVATE_RES_DIRS +TARGET_PRIVATE_RES_DIRS := $(wildcard $(TARGET_DEVICE_DIR)/recovery/res) +endif +recovery_resource_deps := $(shell find $(recovery_resources_common) \ + $(TARGET_PRIVATE_RES_DIRS) -type f) +recovery_resource_deps += $(generated_recovery_text_files) + + +ifdef TARGET_RECOVERY_FSTAB +recovery_fstab := $(TARGET_RECOVERY_FSTAB) +else ifdef TARGET_RECOVERY_FSTAB_GENRULE +# Specifies a soong genrule module that generates an fstab. +recovery_fstab := $(call intermediates-dir-for,ETC,$(TARGET_RECOVERY_FSTAB_GENRULE))/$(TARGET_RECOVERY_FSTAB_GENRULE) +else +recovery_fstab := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery.fstab)) +endif +ifdef TARGET_RECOVERY_WIPE +recovery_wipe := $(TARGET_RECOVERY_WIPE) +else +recovery_wipe := +endif + +# Traditionally with non-A/B OTA we have: +# boot.img + recovery-from-boot.p + recovery-resource.dat = recovery.img. +# recovery-resource.dat is needed only if we carry an imgdiff patch of the boot and recovery images +# and invoke install-recovery.sh on the first boot post an OTA update. +# +# We no longer need that if one of the following conditions holds: +# a) We carry a full copy of the recovery image - no patching needed +# (BOARD_USES_FULL_RECOVERY_IMAGE = true); +# b) We build a single image that contains boot and recovery both - no recovery image to install +# (BOARD_USES_RECOVERY_AS_BOOT = true); +# c) We mount the system image as / and therefore do not have a ramdisk in boot.img +# (BOARD_BUILD_SYSTEM_ROOT_IMAGE = true). +# d) We include the recovery DTBO image within recovery - not needing the resource file as we +# do bsdiff because boot and recovery will contain different number of entries +# (BOARD_INCLUDE_RECOVERY_DTBO = true). +# e) We include the recovery ACPIO image within recovery - not needing the resource file as we +# do bsdiff because boot and recovery will contain different number of entries +# (BOARD_INCLUDE_RECOVERY_ACPIO = true). +# f) We build a single image that contains vendor_boot and recovery both - no recovery image to +# install +# (BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT = true). + +ifeq (,$(filter true, $(BOARD_USES_FULL_RECOVERY_IMAGE) $(BOARD_USES_RECOVERY_AS_BOOT) \ + $(BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO) \ + $(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))) +# Named '.dat' so we don't attempt to use imgdiff for patching it. +RECOVERY_RESOURCE_ZIP := $(TARGET_OUT_VENDOR)/etc/recovery-resource.dat +ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_RESOURCE_ZIP) +else +RECOVERY_RESOURCE_ZIP := +endif + +INSTALLED_RECOVERY_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/prop.default + +$(INSTALLED_RECOVERY_BUILD_PROP_TARGET): PRIVATE_RECOVERY_UI_PROPERTIES := \ + TARGET_RECOVERY_UI_ANIMATION_FPS:animation_fps \ + TARGET_RECOVERY_UI_MARGIN_HEIGHT:margin_height \ + TARGET_RECOVERY_UI_MARGIN_WIDTH:margin_width \ + TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS:menu_unusable_rows \ + TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE:progress_bar_baseline \ + TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD:touch_low_threshold \ + TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD:touch_high_threshold \ + TARGET_RECOVERY_UI_VR_STEREO_OFFSET:vr_stereo_offset + +# Parses the given list of build variables and writes their values as build properties if defined. +# For example, if a target defines `TARGET_RECOVERY_UI_MARGIN_HEIGHT := 100`, +# `ro.recovery.ui.margin_height=100` will be appended to the given output file. +# $(1): Map from the build variable names to property names +# $(2): Output file +define append-recovery-ui-properties +echo "#" >> $(2) +echo "# RECOVERY UI BUILD PROPERTIES" >> $(2) +echo "#" >> $(2) +$(foreach prop,$(1), \ + $(eval _varname := $(call word-colon,1,$(prop))) \ + $(eval _propname := $(call word-colon,2,$(prop))) \ + $(eval _value := $($(_varname))) \ + $(if $(_value), \ + echo ro.recovery.ui.$(_propname)=$(_value) >> $(2) &&)) true +endef + +$(INSTALLED_RECOVERY_BUILD_PROP_TARGET): \ + $(INSTALLED_BUILD_PROP_TARGET) \ + $(INSTALLED_VENDOR_BUILD_PROP_TARGET) \ + $(INSTALLED_ODM_BUILD_PROP_TARGET) \ + $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) \ + $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) + @echo "Target recovery buildinfo: $@" + $(hide) mkdir -p $(dir $@) + $(hide) rm -f $@ + $(hide) cat $(INSTALLED_BUILD_PROP_TARGET) >> $@ + $(hide) cat $(INSTALLED_VENDOR_BUILD_PROP_TARGET) >> $@ + $(hide) cat $(INSTALLED_ODM_BUILD_PROP_TARGET) >> $@ + $(hide) cat $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) >> $@ + $(hide) cat $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) >> $@ + $(call append-recovery-ui-properties,$(PRIVATE_RECOVERY_UI_PROPERTIES),$@) + +$(call declare-1p-target,$(INSTALLED_RECOVERY_BUILD_PROP_TARGET),build) +$(call declare-license-deps,$(INSTALLED_RECOVERY_BUILD_PROP_TARGET),\ + $(INSTALLED_BUILD_PROP_TARGET) $(INSTALLED_VENDOR_BUILD_PROP_TARGET) $(INSTALLED_ODM_BUILD_PROP_TARGET) \ + $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET)) + +# Only install boot/etc/build.prop to recovery image on recovery_as_boot. +# On device with dedicated recovery partition, the file should come from the boot +# ramdisk. +ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) +INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/$(RAMDISK_BUILD_PROP_REL_PATH) +$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET): $(INSTALLED_RAMDISK_BUILD_PROP_TARGET) + $(copy-file-to-target) + +$(call declare-1p-target,$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET),build) +$(call declare-license-deps,$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET),$(INSTALLED_RAMDISK_BUILD_PROP_TARGET)) +endif + +INTERNAL_RECOVERYIMAGE_ARGS := --ramdisk $(recovery_ramdisk) + +ifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT))) +INTERNAL_RECOVERYIMAGE_ARGS += $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) +# Assumes this has already been stripped +ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) +ifdef INTERNAL_KERNEL_CMDLINE + INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(INTERNAL_KERNEL_CMDLINE)" +endif # INTERNAL_KERNEL_CMDLINE != "" +endif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true +ifdef BOARD_KERNEL_BASE + INTERNAL_RECOVERYIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) +endif +ifdef BOARD_KERNEL_PAGESIZE + INTERNAL_RECOVERYIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) +endif +ifdef BOARD_INCLUDE_RECOVERY_DTBO +ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE + INTERNAL_RECOVERYIMAGE_ARGS += --recovery_dtbo $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) +else + INTERNAL_RECOVERYIMAGE_ARGS += --recovery_dtbo $(BOARD_PREBUILT_DTBOIMAGE) +endif +endif # BOARD_INCLUDE_RECOVERY_DTBO +ifdef BOARD_INCLUDE_RECOVERY_ACPIO + INTERNAL_RECOVERYIMAGE_ARGS += --recovery_acpio $(BOARD_RECOVERY_ACPIO) +endif +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG + INTERNAL_RECOVERYIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) +endif +endif # (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT) +ifndef BOARD_RECOVERY_MKBOOTIMG_ARGS + BOARD_RECOVERY_MKBOOTIMG_ARGS := $(BOARD_MKBOOTIMG_ARGS) +endif + +$(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP): $(MKBOOTFS) $(COMPRESSION_COMMAND_DEPS) \ + $(INTERNAL_ROOT_FILES) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INTERNAL_RECOVERYIMAGE_FILES) \ + $(recovery_sepolicy) \ + $(INSTALLED_2NDBOOTLOADER_TARGET) \ + $(INSTALLED_RECOVERY_BUILD_PROP_TARGET) \ + $(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET) \ + $(recovery_resource_deps) \ + $(recovery_fstab) + # Making recovery image + mkdir -p $(TARGET_RECOVERY_OUT) + mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sdcard $(TARGET_RECOVERY_ROOT_OUT)/tmp + # Copying baseline ramdisk... + # Use rsync because "cp -Rf" fails to overwrite broken symlinks on Mac. + rsync -a --exclude=sdcard $(IGNORE_RECOVERY_SEPOLICY) $(IGNORE_CACHE_LINK) $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT) + # Modifying ramdisk contents... + $(if $(filter true,$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)),, \ + ln -sf /system/bin/init $(TARGET_RECOVERY_ROOT_OUT)/init) + # Removes $(TARGET_RECOVERY_ROOT_OUT)/init*.rc EXCEPT init.recovery*.rc. + find $(TARGET_RECOVERY_ROOT_OUT) -maxdepth 1 -name 'init*.rc' -type f -not -name "init.recovery.*.rc" | xargs rm -f + cp $(TARGET_ROOT_OUT)/init.recovery.*.rc $(TARGET_RECOVERY_ROOT_OUT)/ 2> /dev/null || true # Ignore error when the src file doesn't exist. + mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/res + rm -rf $(TARGET_RECOVERY_ROOT_OUT)/res/* + cp -rf $(recovery_resources_common)/* $(TARGET_RECOVERY_ROOT_OUT)/res + $(foreach recovery_text_file,$(generated_recovery_text_files), \ + cp -rf $(recovery_text_file) $(TARGET_RECOVERY_ROOT_OUT)/res/images/ &&) true + cp -f $(recovery_font) $(TARGET_RECOVERY_ROOT_OUT)/res/images/font.png + $(foreach item,$(TARGET_PRIVATE_RES_DIRS), \ + cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/$(newline)) + $(foreach item,$(recovery_fstab), \ + cp -f $(item) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.fstab) + $(if $(strip $(recovery_wipe)), \ + cp -f $(recovery_wipe) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.wipe) + ln -sf prop.default $(TARGET_RECOVERY_ROOT_OUT)/default.prop + $(BOARD_RECOVERY_IMAGE_PREPARE) + $(hide) touch $@ + +$(recovery_ramdisk): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) + $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RECOVERY_ROOT_OUT) | $(COMPRESSION_COMMAND) > $(recovery_ramdisk) + +# $(1): output file +# $(2): optional kernel file +define build-recoveryimage-target + $(if $(filter true,$(PRODUCT_SUPPORTS_VBOOT)), \ + $(MKBOOTIMG) $(if $(strip $(2)),--kernel $(strip $(2))) $(INTERNAL_RECOVERYIMAGE_ARGS) \ + $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_RECOVERY_MKBOOTIMG_ARGS) \ + --output $(1).unsigned, \ + $(MKBOOTIMG) $(if $(strip $(2)),--kernel $(strip $(2))) $(INTERNAL_RECOVERYIMAGE_ARGS) \ + $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ + $(BOARD_RECOVERY_MKBOOTIMG_ARGS) --output $(1)) + $(if $(filter true,$(PRODUCT_SUPPORTS_BOOT_SIGNER)),\ + $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\ + $(BOOT_SIGNER) /boot $(1) $(PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCT_VERITY_SIGNING_KEY).x509.pem $(1),\ + $(BOOT_SIGNER) /recovery $(1) $(PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCT_VERITY_SIGNING_KEY).x509.pem $(1)\ + )\ + ) + $(if $(filter true,$(PRODUCT_SUPPORTS_VBOOT)), \ + $(VBOOT_SIGNER) $(FUTILITY) $(1).unsigned $(PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $(1).keyblock $(1)) + $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \ + $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot))), \ + $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)))) + $(if $(filter true,$(BOARD_AVB_ENABLE)), \ + $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \ + $(AVBTOOL) add_hash_footer --image $(1) $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS),\ + $(AVBTOOL) add_hash_footer --image $(1) $(call get-partition-size-argument,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)) --partition_name recovery $(INTERNAL_AVB_RECOVERY_SIGNING_ARGS) $(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS))) +endef + +recoveryimage-deps := $(MKBOOTIMG) $(recovery_ramdisk) $(recovery_kernel) +ifeq (true,$(PRODUCT_SUPPORTS_BOOT_SIGNER)) + recoveryimage-deps += $(BOOT_SIGNER) +endif +ifeq (true,$(PRODUCT_SUPPORTS_VBOOT)) + recoveryimage-deps += $(VBOOT_SIGNER) +endif +ifeq (true,$(BOARD_AVB_ENABLE)) + recoveryimage-deps += $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) +endif +ifdef BOARD_INCLUDE_RECOVERY_DTBO + ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE + recoveryimage-deps += $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) + else + recoveryimage-deps += $(BOARD_PREBUILT_DTBOIMAGE) + endif +endif +ifdef BOARD_INCLUDE_RECOVERY_ACPIO + recoveryimage-deps += $(BOARD_RECOVERY_ACPIO) +endif +ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG + recoveryimage-deps += $(INSTALLED_DTBIMAGE_TARGET) +endif + +ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) +$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b))))) +$(INSTALLED_BOOTIMAGE_TARGET): $(recoveryimage-deps) + $(call pretty,"Target boot image from recovery: $@") + $(call build-recoveryimage-target, $@, $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $@)))) + +$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",bool) +$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(recoveryimage-deps),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) +endif # BOARD_USES_RECOVERY_AS_BOOT + +$(INSTALLED_RECOVERYIMAGE_TARGET): $(recoveryimage-deps) + $(call build-recoveryimage-target, $@, \ + $(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel))) + +ifdef RECOVERY_RESOURCE_ZIP +$(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ZIPTIME) + $(hide) mkdir -p $(dir $@) + $(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrjX $@ -@ + $(remove-timestamps-from-package) +endif + + +$(call declare-1p-container,$(INSTALLED_RECOVERYIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_RECOVERYIMAGE_TARGET),$(recoveryimage-deps),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_RECOVERYIMAGE_TARGET) + +.PHONY: recoveryimage-nodeps +recoveryimage-nodeps: + @echo "make $@: ignoring dependencies" + $(call build-recoveryimage-target, $(INSTALLED_RECOVERYIMAGE_TARGET), \ + $(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel))) + +else # BUILDING_RECOVERY_IMAGE +RECOVERY_RESOURCE_ZIP := +endif # BUILDING_RECOVERY_IMAGE + +.PHONY: recoveryimage +recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET) $(RECOVERY_RESOURCE_ZIP) + +ifneq ($(BOARD_NAND_PAGE_SIZE),) +$(error MTD device is no longer supported and thus BOARD_NAND_PAGE_SIZE is deprecated.) +endif + +ifneq ($(BOARD_NAND_SPARE_SIZE),) +$(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.) +endif + + +# ----------------------------------------------------------------- +# Build debug ramdisk and debug boot image. +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_DEBUG_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifneq ($(BUILDING_DEBUG_BOOT_IMAGE)$(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),) + +INTERNAL_DEBUG_RAMDISK_FILES := $(filter $(TARGET_DEBUG_RAMDISK_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +# Directories to be picked into the debug ramdisk. +# As these directories are all merged into one single cpio archive, the order +# matters. If there are multiple files with the same pathname, then the last one +# wins. +# +# ramdisk-debug.img will merge the content from either ramdisk.img or +# ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT +# is set or not. +ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RECOVERY_ROOT_OUT) + INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(recovery_ramdisk) +else # BOARD_USES_RECOVERY_AS_BOOT == true + INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RAMDISK_OUT) + INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(INSTALLED_RAMDISK_TARGET) +endif # BOARD_USES_RECOVERY_AS_BOOT != true + +INTERNAL_DEBUG_RAMDISK_SRC_DIRS += $(TARGET_DEBUG_RAMDISK_OUT) +INTERNAL_DEBUG_RAMDISK_SRC_DEPS := $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET) $(INTERNAL_DEBUG_RAMDISK_FILES) + +# INSTALLED_FILES_FILE_DEBUG_RAMDISK would ensure TARGET_DEBUG_RAMDISK_OUT is created. +INSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt +INSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json) +$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) +$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INTERNAL_DEBUG_RAMDISK_SRC_DEPS) +$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL) + @echo "Installed file list: $@" + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) $(TARGET_DEBUG_RAMDISK_OUT) + touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable + $(FILESLIST) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_DEBUG_RAMDISK))) +$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_DEBUG_RAMDISK))) + +ifdef BUILDING_DEBUG_BOOT_IMAGE + +# ----------------------------------------------------------------- +# the debug ramdisk, which is the original ramdisk plus additional +# files: force_debuggable, adb_debug.prop and userdebug sepolicy. +# When /force_debuggable is present, /init will load userdebug sepolicy +# and property files to allow adb root, if the device is unlocked. +INSTALLED_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img + +$(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) +$(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + @echo "Target debug ramdisk: $@" + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) + $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ + +$(call declare-1p-container,$(INSTALLED_DEBUG_RAMDISK_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_DEBUG_RAMDISK_TARGET) + +.PHONY: ramdisk_debug-nodeps +ramdisk_debug-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + @echo "make $@: ignoring dependencies" + $(hide) rm -f $(INSTALLED_DEBUG_RAMDISK_TARGET) + $(hide) mkdir -p $(dir $(INSTALLED_DEBUG_RAMDISK_TARGET)) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) + $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET) + +# ----------------------------------------------------------------- +# the boot-debug.img, which is the kernel plus ramdisk-debug.img +# +# Note: it's intentional to skip signing for boot-debug.img, because it +# can only be used if the device is unlocked with verification error. +ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) + INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-debug,$(BOARD_KERNEL_BINARIES)), \ + $(PRODUCT_OUT)/$(k).img) +else + INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-debug.img +endif + +# Replace ramdisk.img in $(MKBOOTIMG) ARGS with ramdisk-debug.img to build boot-debug.img +$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET) +ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_RECOVERYIMAGE_ARGS)) +else + INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_BOOTIMAGE_ARGS)) +endif + +# If boot.img is chained but boot-debug.img is not signed, libavb in bootloader +# will fail to find valid AVB metadata from the end of /boot, thus stop booting. +# Using a test key to sign boot-debug.img to continue booting with the mismatched +# public key, if the device is unlocked. +ifneq ($(BOARD_AVB_BOOT_KEY_PATH),) +$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH) +endif + +BOARD_AVB_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem +INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_BOOT_TEST_KEY_PATH) +# $(1): the bootimage to sign +# $(2): boot image variant (boot, boot-debug, boot-test-harness) +define test-key-sign-bootimage +$(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),$(2)))) +$(AVBTOOL) add_hash_footer \ + --image $(1) \ + $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),$(2)))\ + --partition_name boot $(INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS) \ + $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) +$(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),$(2))) +endef + +# $(1): output file +define build-debug-bootimage-target + $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-debug,kernel,$(notdir $(1)))) \ + $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ + $(BOARD_MKBOOTIMG_ARGS) --output $1 + $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$1,boot-debug)) +endef + +# Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img +$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(AVBTOOL) + $(call pretty,"Target boot debug image: $@") + $(call build-debug-bootimage-target, $@) + +$(call declare-container-license-metadata,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) +$(call declare-container-license-deps,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(INSTALLED_BOOTIMAGE_TARGET),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) + +.PHONY: bootimage_debug-nodeps +bootimage_debug-nodeps: $(MKBOOTIMG) $(AVBTOOL) + echo "make $@: ignoring dependencies" + $(foreach b,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call build-debug-bootimage-target,$b)) + +endif # BUILDING_DEBUG_BOOT_IMAGE + +# ----------------------------------------------------------------- +# vendor debug ramdisk +# Combines vendor ramdisk files and debug ramdisk files to build the vendor debug ramdisk. +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE + +INTERNAL_VENDOR_DEBUG_RAMDISK_FILES := $(filter $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +# The debug vendor ramdisk combines vendor ramdisk and debug ramdisk. +INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS := $(TARGET_VENDOR_RAMDISK_OUT) +INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS := $(INTERNAL_VENDOR_RAMDISK_TARGET) + +# Exclude recovery files in the default vendor ramdisk if including a standalone +# recovery ramdisk in vendor_boot. +ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) + ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) + INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_RECOVERY_ROOT_OUT) + INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) + endif # BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT != true +endif # BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT == true + +INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT) +INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) + +# INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK would ensure TARGET_VENDOR_DEBUG_RAMDISK_OUT is created. +INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt +INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json) +$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) +$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS) +$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL) + @echo "Installed file list: $@" + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) + $(FILESLIST) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)) + +INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT) + +$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) +$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) + $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ + +INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-debug.img +$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) + @echo "Target debug vendor ramdisk: $@" + $(copy-file-to-target) + +$(call declare-1p-container,$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET),$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) + +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) + +# ----------------------------------------------------------------- +# vendor_boot-debug.img. +INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot-debug.img + +# The util to sign vendor_boot-debug.img with a test key. +BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem +INTERNAL_AVB_VENDOR_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH) +# $(1): the vendor bootimage to sign +define test-key-sign-vendor-bootimage +$(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))) +$(AVBTOOL) add_hash_footer \ + --image $(1) \ + $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \ + --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_TEST_SIGNING_ARGS) \ + $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS) +$(call assert-max-image-size,$(1),$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) +endef + +ifneq ($(BOARD_AVB_VENDOR_BOOT_KEY_PATH),) +$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH) +endif + +# Depends on vendor_boot.img and vendor-ramdisk-debug.cpio.gz to build the new vendor_boot-debug.img +$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) +$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) + $(call pretty,"Target vendor_boot debug image: $@") + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ + $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) + $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@)) + +$(call declare-1p-container,$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) + +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) + +endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE + +# Appends a few test harness specific properties into the adb_debug.prop. +ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1 +ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1 + +INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET := $(strip $(filter $(TARGET_DEBUG_RAMDISK_OUT)/adb_debug.prop,$(INTERNAL_DEBUG_RAMDISK_FILES))) +INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop +$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET): $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) +ifdef INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET + $(hide) cp $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) $@ +endif + $(hide) echo "" >> $@ + $(hide) echo "#" >> $@ + $(hide) echo "# ADDITIONAL TEST HARNESS PROPERTIES" >> $@ + $(hide) echo "#" >> $@ + $(hide) $(foreach line,$(ADDITIONAL_TEST_HARNESS_PROPERTIES), \ + echo "$(line)" >> $@;) + +$(call declare-1p-target,$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET)) + +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +INTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \ + $(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET) \ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +# The order is important here. The test harness ramdisk staging directory has to +# come last so that it can override the adb_debug.prop in the debug ramdisk +# staging directory. +INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT) +INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) + +ifdef BUILDING_DEBUG_BOOT_IMAGE + +# ----------------------------------------------------------------- +# The test harness ramdisk, which is based off debug_ramdisk, plus a +# few additional test-harness-specific properties in adb_debug.prop. +INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img + +$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS) +$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + @echo "Target test harness ramdisk: $@" + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) + $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ + +$(call declare-1p-container,$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) + +.PHONY: ramdisk_test_harness-nodeps +ramdisk_test_harness-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + @echo "make $@: ignoring dependencies" + $(hide) rm -f $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) + $(hide) mkdir -p $(dir $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) + $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) + +# ----------------------------------------------------------------- +# the boot-test-harness.img, which is the kernel plus ramdisk-test-harness.img +# +# Note: it's intentional to skip signing for boot-test-harness.img, because it +# can only be used if the device is unlocked with verification error. +ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) + INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-test-harness,$(BOARD_KERNEL_BINARIES)), \ + $(PRODUCT_OUT)/$(k).img) +else + INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-test-harness.img +endif + +# Replace ramdisk-debug.img in $(MKBOOTIMG) ARGS with ramdisk-test-harness.img to build boot-test-harness.img +$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) +INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS := $(subst $(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_DEBUG_BOOTIMAGE_ARGS)) + +# If boot.img is chained but boot-test-harness.img is not signed, libavb in bootloader +# will fail to find valid AVB metadata from the end of /boot, thus stop booting. +# Using a test key to sign boot-test-harness.img to continue booting with the mismatched +# public key, if the device is unlocked. +ifneq ($(BOARD_AVB_BOOT_KEY_PATH),) +$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH) +endif + +# $(1): output file +define build-boot-test-harness-target + $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-test-harness,kernel,$(notdir $(1)))) \ + $(INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ + $(BOARD_MKBOOTIMG_ARGS) --output $@ + $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$@,boot-test-harness)) +endef + +# Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img. +$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(AVBTOOL) + $(call pretty,"Target boot test harness image: $@") + $(call build-boot-test-harness-target,$@) + +$(call declare-1p-container,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) + +.PHONY: bootimage_test_harness-nodeps +bootimage_test_harness-nodeps: $(MKBOOTIMG) $(AVBTOOL) + echo "make $@: ignoring dependencies" + $(foreach b,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(call build-boot-test-harness-target,$b)) + +endif # BUILDING_DEBUG_BOOT_IMAGE + +# ----------------------------------------------------------------- +# vendor test harness ramdisk, which is a vendor ramdisk combined with +# a test harness ramdisk. +ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE + +INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-test-harness)/vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT) + +# The order is important here. The test harness ramdisk staging directory has to +# come last so that it can override the adb_debug.prop in the debug ramdisk +# staging directory. +INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT) +INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) + +$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS) +$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) + $(hide) rm -f $@ + $(hide) mkdir -p $(dir $@) + $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ + +INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-test-harness.img +$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) + @echo "Target test harness vendor ramdisk: $@" + $(copy-file-to-target) + +$(call declare-1p-container,$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) + +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) + +# ----------------------------------------------------------------- +# vendor_boot-test-harness.img. +INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot-test-harness.img + +ifneq ($(BOARD_AVB_VENDOR_BOOT_KEY_PATH),) +$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH) +endif + +# Depends on vendor_boot.img and vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT) to build the new vendor_boot-test-harness.img +$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) +$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) +$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) + $(call pretty,"Target vendor_boot test harness image: $@") + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ + $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) + $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@)) + +$(call declare-1p-container,$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) + +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) + +endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE + +endif # BUILDING_DEBUG_BOOT_IMAGE || BUILDING_DEBUG_VENDOR_BOOT_IMAGE + + +# Creates a compatibility symlink between two partitions, e.g. /system/vendor to /vendor +# $1: from location (e.g $(TARGET_OUT)/vendor) +# $2: destination location (e.g. /vendor) +# $3: partition image name (e.g. vendor.img) +define create-partition-compat-symlink +$(eval \ +$1: + @echo Symlink $(patsubst $(PRODUCT_OUT)/%,%,$1) to $2 + mkdir -p $(dir $1) + if [ -d $1 ] && [ ! -h $1 ]; then \ + echo 'Non-symlink $1 detected!' 1>&2; \ + echo 'You cannot install files to $1 while building a separate $3!' 1>&2; \ + exit 1; \ + fi + ln -sfn $2 $1 +$1: .KATI_SYMLINK_OUTPUTS := $1 +) +$1 +endef + + +# ----------------------------------------------------------------- +# system image + +# FSVerity metadata generation +# Generate fsverity metadata files (.fsv_meta) and build manifest +# (system/etc/security/fsverity/BuildManifest.apk) BEFORE filtering systemimage files below +ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true) + +# Generate fsv_meta +fsverity-metadata-targets := $(sort $(filter \ + $(TARGET_OUT)/framework/% \ + $(TARGET_OUT)/etc/boot-image.prof \ + $(TARGET_OUT)/etc/dirty-image-objects \ + $(TARGET_OUT)/etc/preloaded-classes \ + $(TARGET_OUT)/etc/classpaths/%.pb, \ + $(ALL_DEFAULT_INSTALLED_MODULES))) + +define fsverity-generate-metadata +$(1).fsv_meta: PRIVATE_SRC := $(1) +$(1).fsv_meta: PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity +$(1).fsv_meta: $(HOST_OUT_EXECUTABLES)/fsverity_metadata_generator $(HOST_OUT_EXECUTABLES)/fsverity $(1) + $$< --fsverity-path $$(PRIVATE_FSVERITY) --signature none \ + --hash-alg sha256 --output $$@ $$(PRIVATE_SRC) +endef + +$(foreach f,$(fsverity-metadata-targets),$(eval $(call fsverity-generate-metadata,$(f)))) +ALL_DEFAULT_INSTALLED_MODULES += $(addsuffix .fsv_meta,$(fsverity-metadata-targets)) + +# Generate BuildManifest.apk +FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) +FSVERITY_APK_OUT := $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk +FSVERITY_APK_MANIFEST_PATH := system/security/fsverity/AndroidManifest.xml +$(FSVERITY_APK_OUT): PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity +$(FSVERITY_APK_OUT): PRIVATE_AAPT2 := $(HOST_OUT_EXECUTABLES)/aapt2 +$(FSVERITY_APK_OUT): PRIVATE_MIN_SDK_VERSION := $(DEFAULT_APP_TARGET_SDK) +$(FSVERITY_APK_OUT): PRIVATE_VERSION_CODE := $(PLATFORM_SDK_VERSION) +$(FSVERITY_APK_OUT): PRIVATE_VERSION_NAME := $(APPS_DEFAULT_VERSION_NAME) +$(FSVERITY_APK_OUT): PRIVATE_APKSIGNER := $(HOST_OUT_EXECUTABLES)/apksigner +$(FSVERITY_APK_OUT): PRIVATE_MANIFEST := $(FSVERITY_APK_MANIFEST_PATH) +$(FSVERITY_APK_OUT): PRIVATE_FRAMEWORK_RES := $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk +$(FSVERITY_APK_OUT): PRIVATE_KEY := $(FSVERITY_APK_KEY_PATH) +$(FSVERITY_APK_OUT): PRIVATE_INPUTS := $(fsverity-metadata-targets) +$(FSVERITY_APK_OUT): $(HOST_OUT_EXECUTABLES)/fsverity_manifest_generator \ + $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 \ + $(HOST_OUT_EXECUTABLES)/apksigner $(FSVERITY_APK_MANIFEST_PATH) \ + $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8 \ + $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk \ + $(fsverity-metadata-targets) + $< --fsverity-path $(PRIVATE_FSVERITY) --aapt2-path $(PRIVATE_AAPT2) \ + --min-sdk-version $(PRIVATE_MIN_SDK_VERSION) \ + --version-code $(PRIVATE_VERSION_CODE) \ + --version-name $(PRIVATE_VERSION_NAME) \ + --apksigner-path $(PRIVATE_APKSIGNER) --apk-key-path $(PRIVATE_KEY) \ + --apk-manifest-path $(PRIVATE_MANIFEST) --framework-res $(PRIVATE_FRAMEWORK_RES) \ + --output $@ \ + --base-dir $(PRODUCT_OUT) $(PRIVATE_INPUTS) + +ALL_DEFAULT_INSTALLED_MODULES += $(FSVERITY_APK_OUT) + +endif # PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA + +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \ + $(ALL_DEFAULT_INSTALLED_MODULES))) + +# Create symlink /system/vendor to /vendor if necessary. +ifdef BOARD_USES_VENDORIMAGE + INTERNAL_SYSTEMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT)/vendor,/vendor,vendor.img) +endif + +# Create symlink /system/product to /product if necessary. +ifdef BOARD_USES_PRODUCTIMAGE + INTERNAL_SYSTEMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT)/product,/product,product.img) +endif + +# Create symlink /system/system_ext to /system_ext if necessary. +ifdef BOARD_USES_SYSTEM_EXTIMAGE + INTERNAL_SYSTEMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT)/system_ext,/system_ext,system_ext.img) +endif + +# ----------------------------------------------------------------- +# system_dlkm partition image + +# Create symlinks for system_dlkm on devices with a system_dlkm partition: +# /system/lib/modules -> /system_dlkm/lib/modules +# +# On devices with a system_dlkm partition, +# - /system/lib/modules is a symlink to a directory that stores system DLKMs. +# - The system_dlkm partition is mounted at /system_dlkm at runtime. +ifdef BOARD_USES_SYSTEM_DLKMIMAGE + INTERNAL_SYSTEMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT)/lib/modules,/system_dlkm/lib/modules,system_dlkm.img) +endif + +FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS) + +# ASAN libraries in the system image - add dependency. +ASAN_IN_SYSTEM_INSTALLED := $(TARGET_OUT)/asan.tar.bz2 +ifneq (,$(filter address, $(SANITIZE_TARGET))) + ifeq (true,$(SANITIZE_TARGET_SYSTEM)) + FULL_SYSTEMIMAGE_DEPS += $(ASAN_IN_SYSTEM_INSTALLED) + endif +endif + +FULL_SYSTEMIMAGE_DEPS += $(INTERNAL_ROOT_FILES) $(INSTALLED_FILES_FILE_ROOT) + +# ----------------------------------------------------------------- +ifdef BUILDING_SYSTEM_IMAGE + +# Install system linker configuration +# Collect all available stub libraries installed in system and install with predefined linker configuration +SYSTEM_LINKER_CONFIG := $(TARGET_OUT)/etc/linker.config.pb +SYSTEM_LINKER_CONFIG_SOURCE := $(call intermediates-dir-for,ETC,system_linker_config)/system_linker_config +$(SYSTEM_LINKER_CONFIG): PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE := $(SYSTEM_LINKER_CONFIG_SOURCE) +$(SYSTEM_LINKER_CONFIG) : $(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE) | conv_linker_config + $(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $(PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE) \ + --output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT)" + +$(call declare-1p-target,$(SYSTEM_LINKER_CONFIG),) +$(call declare-license-deps,$(SYSTEM_LINKER_CONFIG),$(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE)) + +FULL_SYSTEMIMAGE_DEPS += $(SYSTEM_LINKER_CONFIG) + +# installed file list +# Depending on anything that $(BUILT_SYSTEMIMAGE) depends on. +# We put installed-files.txt ahead of image itself in the dependency graph +# so that we can get the size stat even if the build fails due to too large +# system image. +INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt +INSTALLED_FILES_JSON := $(INSTALLED_FILES_FILE:.txt=.json) +$(INSTALLED_FILES_FILE): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON) +$(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE))) +$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON))) + +.PHONY: installed-file-list +installed-file-list: $(INSTALLED_FILES_FILE) + +systemimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,systemimage) +BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img + +# $(1): output file +define build-systemimage-target + @echo "Target system fs image: $(1)" + @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt + $(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \ + skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \ + || ( mkdir -p $${DIST_DIR}; \ + cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \ + exit 1 ) +endef + +ifeq ($(BOARD_AVB_ENABLE),true) +$(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH) +endif +$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) + $(call build-systemimage-target,$@) + +$(call declare-1p-container,$(BUILT_SYSTEMIMAGE),system/extras) +$(call declare-container-license-deps,$(BUILT_SYSTEMIMAGE),$(FULL_SYSTEMIMAGE_DEPS),$(PRODUCT_OUT)/:/) + +INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img +SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT) + +# INSTALLED_SYSTEMIMAGE_TARGET used to be named INSTALLED_SYSTEMIMAGE. Create an alias for backward +# compatibility, in case device-specific Makefiles still refer to the old name. +INSTALLED_SYSTEMIMAGE := $(INSTALLED_SYSTEMIMAGE_TARGET) + +# The system partition needs room for the recovery image as well. We +# now store the recovery image as a binary patch using the boot image +# as the source (since they are very similar). Generate the patch so +# we can see how big it's going to be, and include that in the system +# image size check calculation. +ifneq ($(INSTALLED_BOOTIMAGE_TARGET),) +ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) +ifneq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true) +ifneq (,$(filter true, $(BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO))) +diff_tool := $(HOST_OUT_EXECUTABLES)/bsdiff +else +diff_tool := $(HOST_OUT_EXECUTABLES)/imgdiff +endif +intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch) +RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p +$(RECOVERY_FROM_BOOT_PATCH): PRIVATE_DIFF_TOOL := $(diff_tool) +$(RECOVERY_FROM_BOOT_PATCH): \ + $(INSTALLED_RECOVERYIMAGE_TARGET) \ + $(firstword $(INSTALLED_BOOTIMAGE_TARGET)) \ + $(diff_tool) + @echo "Construct recovery from boot" + mkdir -p $(dir $@) + $(PRIVATE_DIFF_TOOL) $(firstword $(INSTALLED_BOOTIMAGE_TARGET)) $(INSTALLED_RECOVERYIMAGE_TARGET) $@ +else # $(BOARD_USES_FULL_RECOVERY_IMAGE) == true +RECOVERY_FROM_BOOT_PATCH := $(INSTALLED_RECOVERYIMAGE_TARGET) +endif # BOARD_USES_FULL_RECOVERY_IMAGE +endif # INSTALLED_RECOVERYIMAGE_TARGET +endif # INSTALLED_BOOTIMAGE_TARGET + +$(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) + @echo "Install system fs image: $@" + $(copy-file-to-target) + $(hide) $(call assert-max-image-size,$@,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)) + +$(call declare-1p-container,$(INSTALLED_SYSTEMIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BUILT_SYSTEMIMAGE),$(BUILT_SYSTEMIMAGE):/) + +systemimage: $(INSTALLED_SYSTEMIMAGE_TARGET) + +SYSTEM_NOTICE_DEPS += $(INSTALLED_SYSTEMIMAGE_TARGET) + +.PHONY: systemimage-nodeps snod +systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \ + | $(INTERNAL_USERIMAGES_DEPS) + @echo "make $@: ignoring dependencies" + $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET)) + $(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)) +ifeq (true,$(WITH_DEXPREOPT)) + $(warning Warning: with dexpreopt enabled, you may need a full rebuild.) +endif + +endif # BUILDING_SYSTEM_IMAGE + +.PHONY: sync syncsys +sync syncsys: $(INTERNAL_SYSTEMIMAGE_FILES) + +# ----------------------------------------------------------------- +# Old PDK fusion targets +.PHONY: platform +platform: + echo "Warning: 'platform' is obsolete" + +.PHONY: platform-java +platform-java: + echo "Warning: 'platform-java' is obsolete" + +# ----------------------------------------------------------------- +# data partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_DATA)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +INTERNAL_USERDATAIMAGE_FILES := \ + $(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) + +ifdef BUILDING_USERDATA_IMAGE +userdataimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,userdata) +BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img + +define build-userdataimage-target + $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_DATA) + @mkdir -p $(userdataimage_intermediates) && rm -rf $(userdataimage_intermediates)/userdata_image_info.txt + $(call generate-image-prop-dictionary, $(userdataimage_intermediates)/userdata_image_info.txt,userdata,skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt \ + $(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET) +INSTALLED_USERDATAIMAGE_TARGET_DEPS := \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_USERDATAIMAGE_FILES) +$(INSTALLED_USERDATAIMAGE_TARGET): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS) + $(build-userdataimage-target) + +$(call declare-1p-container,$(INSTALLED_USERDATAIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_USERDATAIMAGE_TARGET),$(INSTALLED_USERDATAIMAGE_TARGET_DEPS),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_USERDATAIMAGE_TARGET) + +.PHONY: userdataimage-nodeps +userdataimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) + $(build-userdataimage-target) + +endif # BUILDING_USERDATA_IMAGE + +# ASAN libraries in the system image - build rule. +ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL := $(sort $(patsubst $(PRODUCT_OUT)/%,%,\ + $(TARGET_OUT_SHARED_LIBRARIES) \ + $(2ND_TARGET_OUT_SHARED_LIBRARIES) \ + $(TARGET_OUT_VENDOR_SHARED_LIBRARIES) \ + $(2ND_TARGET_OUT_VENDOR_SHARED_LIBRARIES))) +# Extra options: Enforce the system user for the files to avoid having to change ownership. +ASAN_SYSTEM_INSTALL_OPTIONS := --owner=1000 --group=1000 +# Note: experimentally, it seems not worth it to try to get "best" compression. We don't save +# enough space. +$(ASAN_IN_SYSTEM_INSTALLED): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS) + tar cfj $(ASAN_IN_SYSTEM_INSTALLED) $(ASAN_SYSTEM_INSTALL_OPTIONS) -C $(TARGET_OUT_DATA)/.. $(ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL) >/dev/null + +# ----------------------------------------------------------------- +# partition table image +ifdef BOARD_BPT_INPUT_FILES + +BUILT_BPTIMAGE_TARGET := $(PRODUCT_OUT)/partition-table.img +BUILT_BPTJSON_TARGET := $(PRODUCT_OUT)/partition-table.bpt + +INTERNAL_BVBTOOL_MAKE_TABLE_ARGS := \ + --output_gpt $(BUILT_BPTIMAGE_TARGET) \ + --output_json $(BUILT_BPTJSON_TARGET) \ + $(foreach file, $(BOARD_BPT_INPUT_FILES), --input $(file)) + +ifdef BOARD_BPT_DISK_SIZE +INTERNAL_BVBTOOL_MAKE_TABLE_ARGS += --disk_size $(BOARD_BPT_DISK_SIZE) +endif + +define build-bptimage-target + $(call pretty,"Target partition table image: $(INSTALLED_BPTIMAGE_TARGET)") + $(hide) $(BPTTOOL) make_table $(INTERNAL_BVBTOOL_MAKE_TABLE_ARGS) $(BOARD_BPT_MAKE_TABLE_ARGS) +endef + +INSTALLED_BPTIMAGE_TARGET := $(BUILT_BPTIMAGE_TARGET) +$(BUILT_BPTJSON_TARGET): $(INSTALLED_BPTIMAGE_TARGET) + $(hide) touch -c $(BUILT_BPTJSON_TARGET) + +$(INSTALLED_BPTIMAGE_TARGET): $(BPTTOOL) $(BOARD_BPT_INPUT_FILES) + $(build-bptimage-target) + +$(call declare-1p-container,$(INSTALLED_BPTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_BPTIMAGE_TARGET),$(BOARD_BPT_INPUT_FILES),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_BPTIMAGE_TARGET) + +.PHONY: bptimage-nodeps +bptimage-nodeps: + $(build-bptimage-target) + +endif # BOARD_BPT_INPUT_FILES + +# ----------------------------------------------------------------- +# cache partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_CACHE)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_CACHE_IMAGE +INTERNAL_CACHEIMAGE_FILES := \ + $(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) + +cacheimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,cache) +BUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img + +define build-cacheimage-target + $(call pretty,"Target cache fs image: $(INSTALLED_CACHEIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_CACHE) + @mkdir -p $(cacheimage_intermediates) && rm -rf $(cacheimage_intermediates)/cache_image_info.txt + $(call generate-image-prop-dictionary, $(cacheimage_intermediates)/cache_image_info.txt,cache,skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt \ + $(INSTALLED_CACHEIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET) +$(INSTALLED_CACHEIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES) + $(build-cacheimage-target) + +$(call declare-1p-container,$(INSTALLED_CACHEIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_CACHEIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_CACHEIMAGE_TARGET) + +.PHONY: cacheimage-nodeps +cacheimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) + $(build-cacheimage-target) + +else # BUILDING_CACHE_IMAGE +# we need to ignore the broken cache link when doing the rsync +IGNORE_CACHE_LINK := --exclude=cache +endif # BUILDING_CACHE_IMAGE + +# ----------------------------------------------------------------- +# system_other partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_OTHER)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_SYSTEM_OTHER_IMAGE +ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true) +# Marker file to identify that odex files are installed +INSTALLED_SYSTEM_OTHER_ODEX_MARKER := $(TARGET_OUT_SYSTEM_OTHER)/system-other-odex-marker +ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_SYSTEM_OTHER_ODEX_MARKER) +$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER): + $(hide) touch $@ + +$(call declare-0p-target,$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER)) +endif + +INTERNAL_SYSTEMOTHERIMAGE_FILES := \ + $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +# system_other dex files are installed as a side-effect of installing system image files +INTERNAL_SYSTEMOTHERIMAGE_FILES += $(INTERNAL_SYSTEMIMAGE_FILES) + +INSTALLED_FILES_FILE_SYSTEMOTHER := $(PRODUCT_OUT)/installed-files-system-other.txt +INSTALLED_FILES_JSON_SYSTEMOTHER := $(INSTALLED_FILES_FILE_SYSTEMOTHER:.txt=.json) +$(INSTALLED_FILES_FILE_SYSTEMOTHER): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEMOTHER) +$(INSTALLED_FILES_FILE_SYSTEMOTHER) : $(INTERNAL_SYSTEMOTHERIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_SYSTEM_OTHER) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEMOTHER))) +$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEMOTHER))) + +# Determines partition size for system_other.img. +ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true) +ifneq ($(filter system,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),) +INTERNAL_SYSTEM_OTHER_PARTITION_SIZE := $(BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE) +endif +endif + +ifndef INTERNAL_SYSTEM_OTHER_PARTITION_SIZE +INTERNAL_SYSTEM_OTHER_PARTITION_SIZE:= $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) +endif + +systemotherimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,system_other) +BUILT_SYSTEMOTHERIMAGE_TARGET := $(PRODUCT_OUT)/system_other.img + +# Note that we assert the size is SYSTEMIMAGE_PARTITION_SIZE since this is the 'b' system image. +define build-systemotherimage-target + $(call pretty,"Target system_other fs image: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_SYSTEM_OTHER) + @mkdir -p $(systemotherimage_intermediates) && rm -rf $(systemotherimage_intermediates)/system_other_image_info.txt + $(call generate-image-prop-dictionary, $(systemotherimage_intermediates)/system_other_image_info.txt,system,skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_SYSTEM_OTHER) $(systemotherimage_intermediates)/system_other_image_info.txt \ + $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_SYSTEMOTHERIMAGE_TARGET := $(BUILT_SYSTEMOTHERIMAGE_TARGET) +ifneq (true,$(SANITIZE_LITE)) +# Only create system_other when not building the second stage of a SANITIZE_LITE build. +$(INSTALLED_SYSTEMOTHERIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEMOTHERIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEMOTHER) + $(build-systemotherimage-target) + +$(call declare-1p-container,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEMOTHERIMAGE_FILES),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) +endif + +.PHONY: systemotherimage-nodeps +systemotherimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) + $(build-systemotherimage-target) + +endif # BUILDING_SYSTEM_OTHER_IMAGE + + +# ----------------------------------------------------------------- +# vendor partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_VENDOR)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_VENDOR_IMAGE +INTERNAL_VENDORIMAGE_FILES := \ + $(filter $(TARGET_OUT_VENDOR)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + + +# Create symlink /vendor/odm to /odm if necessary. +ifdef BOARD_USES_ODMIMAGE + INTERNAL_VENDORIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/odm,/odm,odm.img) +endif + +# Create symlinks for vendor_dlkm on devices with a vendor_dlkm partition: +# /vendor/lib/modules -> /vendor_dlkm/lib/modules +# +# On devices with a vendor_dlkm partition, +# - /vendor/lib/modules is a symlink to a directory that stores vendor DLKMs. +# - /vendor_dlkm/{etc,...} store other vendor_dlkm files directly. The vendor_dlkm partition is +# mounted at /vendor_dlkm at runtime and the symlinks created in system/core/rootdir/Android.mk +# are hidden. +# On devices without a vendor_dlkm partition, +# - /vendor/lib/modules stores vendor DLKMs directly. +# - /vendor_dlkm/{etc,...} are symlinks to directories that store other vendor_dlkm files. +# See system/core/rootdir/Android.mk for a list of created symlinks. +# The vendor DLKMs and other vendor_dlkm files must not be accessed using other paths because they +# are not guaranteed to exist on all devices. +ifdef BOARD_USES_VENDOR_DLKMIMAGE + INTERNAL_VENDORIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/lib/modules,/vendor_dlkm/lib/modules,vendor_dlkm.img) +endif + +INSTALLED_FILES_FILE_VENDOR := $(PRODUCT_OUT)/installed-files-vendor.txt +INSTALLED_FILES_JSON_VENDOR := $(INSTALLED_FILES_FILE_VENDOR:.txt=.json) +$(INSTALLED_FILES_FILE_VENDOR): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR) +$(INSTALLED_FILES_FILE_VENDOR) : $(INTERNAL_VENDORIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_VENDOR) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR)) + +vendorimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,vendor) +BUILT_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img +define build-vendorimage-target + $(call pretty,"Target vendor fs image: $(INSTALLED_VENDORIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_VENDOR) + @mkdir -p $(vendorimage_intermediates) && rm -rf $(vendorimage_intermediates)/vendor_image_info.txt + $(call generate-image-prop-dictionary, $(vendorimage_intermediates)/vendor_image_info.txt,vendor,skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt \ + $(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET) $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_VENDORIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET) +$(INSTALLED_VENDORIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_VENDORIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_VENDOR) \ + $(RECOVERY_FROM_BOOT_PATCH) + $(build-vendorimage-target) + +VENDOR_NOTICE_DEPS += $(INSTALLED_VENDORIMAGE_TARGET) + +$(call declare-container-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),legacy_proprietary,proprietary,,"Vendor Image",vendor) +$(call declare-container-license-deps,$(INSTALLED_VENDORIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDORIMAGE_FILES) $(RECOVERY_FROM_BOOT_PATH),$(PRODUCT_OUT)/:/) + +.PHONY: vendorimage-nodeps vnod +vendorimage-nodeps vnod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-vendorimage-target) + +sync: $(INTERNAL_VENDORIMAGE_FILES) + +else ifdef BOARD_PREBUILT_VENDORIMAGE +INSTALLED_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDORIMAGE),$(INSTALLED_VENDORIMAGE_TARGET))) +$(if $(strip $(ALL_TARGETS.$(INSTALLED_VENDORIMAGE_TARGET).META_LIC)),,\ + $(if $(strip $(ALL_TARGETS.$(BOARD_PREBUILT_VENDORIMAGE).META_LIC)),\ + $(eval ALL_TARGETS.$(INSTALLED_VENDORIMAGE_TARGET).META_LIC:=$(ALL_TARGETS.$(BOARD_PREBUILT_VENDORIMAGE).META_LIC)),\ + $(call declare-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),legacy_proprietary,proprietary,,"Vendor Image",vendor))) +endif + +# ----------------------------------------------------------------- +# product partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_PRODUCT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_PRODUCT_IMAGE +INTERNAL_PRODUCTIMAGE_FILES := \ + $(filter $(TARGET_OUT_PRODUCT)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_PRODUCT := $(PRODUCT_OUT)/installed-files-product.txt +INSTALLED_FILES_JSON_PRODUCT := $(INSTALLED_FILES_FILE_PRODUCT:.txt=.json) +$(INSTALLED_FILES_FILE_PRODUCT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_PRODUCT) +$(INSTALLED_FILES_FILE_PRODUCT) : $(INTERNAL_PRODUCTIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_PRODUCT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_PRODUCT)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_PRODUCT)) + +productimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,product) +BUILT_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img +define build-productimage-target + $(call pretty,"Target product fs image: $(INSTALLED_PRODUCTIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_PRODUCT) + @mkdir -p $(productimage_intermediates) && rm -rf $(productimage_intermediates)/product_image_info.txt + $(call generate-image-prop-dictionary, $(productimage_intermediates)/product_image_info.txt,product,skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt \ + $(INSTALLED_PRODUCTIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_PRODUCTIMAGE_TARGET),$(BOARD_PRODUCTIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_PRODUCTIMAGE_TARGET := $(BUILT_PRODUCTIMAGE_TARGET) +$(INSTALLED_PRODUCTIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_PRODUCTIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_PRODUCT) + $(build-productimage-target) + +PRODUCT_NOTICE_DEPS += $(INSTALLED_PRODUCTIMAGE_TARGET) + +$(call declare-1p-container,$(INSTALLED_PRODUCTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_PRODUCTIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_PRODUCTIMAGE_FILES) $(INSTALLED_FILES_FILE_PRODUCT),$(PRODUCT_OUT)/:/) + +.PHONY: productimage-nodeps pnod +productimage-nodeps pnod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-productimage-target) + +sync: $(INTERNAL_PRODUCTIMAGE_FILES) + +else ifdef BOARD_PREBUILT_PRODUCTIMAGE +INSTALLED_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_PRODUCTIMAGE),$(INSTALLED_PRODUCTIMAGE_TARGET))) +endif + +# ----------------------------------------------------------------- +# system_ext partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_EXT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_SYSTEM_EXT_IMAGE +INTERNAL_SYSTEM_EXTIMAGE_FILES := \ + $(filter $(TARGET_OUT_SYSTEM_EXT)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_SYSTEM_EXT := $(PRODUCT_OUT)/installed-files-system_ext.txt +INSTALLED_FILES_JSON_SYSTEM_EXT := $(INSTALLED_FILES_FILE_SYSTEM_EXT:.txt=.json) +$(INSTALLED_FILES_FILE_SYSTEM_EXT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEM_EXT) +$(INSTALLED_FILES_FILE_SYSTEM_EXT) : $(INTERNAL_SYSTEM_EXTIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_SYSTEM_EXT) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEM_EXT)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEM_EXT)) + +system_extimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,system_ext) +BUILT_SYSTEM_EXTIMAGE_TARGET := $(PRODUCT_OUT)/system_ext.img +define build-system_extimage-target + $(call pretty,"Target system_ext fs image: $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_SYSTEM_EXT) + @mkdir -p $(system_extimage_intermediates) && rm -rf $(system_extimage_intermediates)/system_ext_image_info.txt + $(call generate-image-prop-dictionary, $(system_extimage_intermediates)/system_ext_image_info.txt,system_ext, skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_SYSTEM_EXT) \ + $(system_extimage_intermediates)/system_ext_image_info.txt \ + $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ + $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET),$(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_SYSTEM_EXTIMAGE_TARGET := $(BUILT_SYSTEM_EXTIMAGE_TARGET) +$(INSTALLED_SYSTEM_EXTIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_SYSTEM_EXT) + $(build-system_extimage-target) + +SYSTEM_EXT_NOTICE_DEPS += $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) + +$(call declare-1p-container,$(INSTALLED_SYSTEM_EXTIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_SYSTEM_EXTIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEM_EXTIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEM_EXT),$(PRODUCT_OUT)/:/) + +.PHONY: systemextimage-nodeps senod +systemextimage-nodeps senod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-system_extimage-target) + +sync: $(INTERNAL_SYSTEM_EXTIMAGE_FILES) + +else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE +INSTALLED_SYSTEM_EXTIMAGE_TARGET := $(PRODUCT_OUT)/system_ext.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_EXTIMAGE),$(INSTALLED_SYSTEM_EXTIMAGE_TARGET))) +endif + +# ----------------------------------------------------------------- +# odm partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_ODM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_ODM_IMAGE +INTERNAL_ODMIMAGE_FILES := \ + $(filter $(TARGET_OUT_ODM)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +# Create symlinks for odm_dlkm on devices with a odm_dlkm partition: +# /odm/lib/modules -> /odm_dlkm/lib/modules +# +# On devices with a odm_dlkm partition, +# - /odm/lib/modules is a symlink to a directory that stores odm DLKMs. +# - /odm_dlkm/{etc,...} store other odm_dlkm files directly. The odm_dlkm partition is +# mounted at /odm_dlkm at runtime and the symlinks created in system/core/rootdir/Android.mk +# are hidden. +# On devices without a odm_dlkm partition, +# - /odm/lib/modules stores odm DLKMs directly. +# - /odm_dlkm/{etc,...} are symlinks to directories that store other odm_dlkm files. +# See system/core/rootdir/Android.mk for a list of created symlinks. +# The odm DLKMs and other odm_dlkm files must not be accessed using other paths because they +# are not guaranteed to exist on all devices. +ifdef BOARD_USES_ODM_DLKMIMAGE + INTERNAL_ODMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT_ODM)/lib/modules,/odm_dlkm/lib/modules,odm_dlkm.img) +endif + +INSTALLED_FILES_FILE_ODM := $(PRODUCT_OUT)/installed-files-odm.txt +INSTALLED_FILES_JSON_ODM := $(INSTALLED_FILES_FILE_ODM:.txt=.json) +$(INSTALLED_FILES_FILE_ODM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ODM) +$(INSTALLED_FILES_FILE_ODM) : $(INTERNAL_ODMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_ODM) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_ODM)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_ODM)) + +odmimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,odm) +BUILT_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img +define build-odmimage-target + $(call pretty,"Target odm fs image: $(INSTALLED_ODMIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_ODM) + @mkdir -p $(odmimage_intermediates) && rm -rf $(odmimage_intermediates)/odm_image_info.txt + $(call generate-image-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, odm, \ + skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt \ + $(INSTALLED_ODMIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_ODMIMAGE_TARGET),$(BOARD_ODMIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_ODMIMAGE_TARGET := $(BUILT_ODMIMAGE_TARGET) +$(INSTALLED_ODMIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_ODMIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_ODM) + $(build-odmimage-target) + +ODM_NOTICE_DEPS += $(INSTALLED_ODMIMAGE_TARGET) + +$(call declare-1p-container,$(INSTALLED_ODMIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_ODMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_ODMIMAGE_FILES) $(INSTALLED_FILES_FILE_ODM),$(PRODUCT_OUT)/:/) + +.PHONY: odmimage-nodeps onod +odmimage-nodeps onod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-odmimage-target) + +sync: $(INTERNAL_ODMIMAGE_FILES) + +else ifdef BOARD_PREBUILT_ODMIMAGE +INSTALLED_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_ODMIMAGE),$(INSTALLED_ODMIMAGE_TARGET))) +endif + +# ----------------------------------------------------------------- +# vendor_dlkm partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_VENDOR_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_VENDOR_DLKM_IMAGE +INTERNAL_VENDOR_DLKMIMAGE_FILES := \ + $(filter $(TARGET_OUT_VENDOR_DLKM)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_VENDOR_DLKM := $(PRODUCT_OUT)/installed-files-vendor_dlkm.txt +INSTALLED_FILES_JSON_VENDOR_DLKM := $(INSTALLED_FILES_FILE_VENDOR_DLKM:.txt=.json) +$(INSTALLED_FILES_FILE_VENDOR_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DLKM) +$(INSTALLED_FILES_FILE_VENDOR_DLKM) : $(INTERNAL_VENDOR_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_VENDOR_DLKM) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_DLKM)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_DLKM)) + +vendor_dlkmimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,vendor_dlkm) +BUILT_VENDOR_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/vendor_dlkm.img +define build-vendor_dlkmimage-target + $(call pretty,"Target vendor_dlkm fs image: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_VENDOR_DLKM) + @mkdir -p $(vendor_dlkmimage_intermediates) && rm -rf $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt + $(call generate-image-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, \ + vendor_dlkm, skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt \ + $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_VENDOR_DLKMIMAGE_TARGET := $(BUILT_VENDOR_DLKMIMAGE_TARGET) +$(INSTALLED_VENDOR_DLKMIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_VENDOR_DLKMIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_VENDOR_DLKM) + $(build-vendor_dlkmimage-target) + +VENDOR_DLKM_NOTICE_DEPS += $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) + +$(call declare-1p-container,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDOR_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_VENDOR_DLKM),$(PRODUCT_OUT)/:/) + +.PHONY: vendor_dlkmimage-nodeps vdnod +vendor_dlkmimage-nodeps vdnod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-vendor_dlkmimage-target) + +sync: $(INTERNAL_VENDOR_DLKMIMAGE_FILES) + +else ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE +INSTALLED_VENDOR_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/vendor_dlkm.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDOR_DLKMIMAGE),$(INSTALLED_VENDOR_DLKMIMAGE_TARGET))) +endif + +# ----------------------------------------------------------------- +# odm_dlkm partition image +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_ODM_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_ODM_DLKM_IMAGE +INTERNAL_ODM_DLKMIMAGE_FILES := \ + $(filter $(TARGET_OUT_ODM_DLKM)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_ODM_DLKM := $(PRODUCT_OUT)/installed-files-odm_dlkm.txt +INSTALLED_FILES_JSON_ODM_DLKM := $(INSTALLED_FILES_FILE_ODM_DLKM:.txt=.json) +$(INSTALLED_FILES_FILE_ODM_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ODM_DLKM) +$(INSTALLED_FILES_FILE_ODM_DLKM) : $(INTERNAL_ODM_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + rm -f $@ + $(FILESLIST) $(TARGET_OUT_ODM_DLKM) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_ODM_DLKM)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_ODM_DLKM)) + +odm_dlkmimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,odm_dlkm) +BUILT_ODM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/odm_dlkm.img +define build-odm_dlkmimage-target + $(call pretty,"Target odm_dlkm fs image: $(INSTALLED_ODM_DLKMIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_ODM_DLKM) + @mkdir -p $(odm_dlkmimage_intermediates) && rm -rf $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt + $(call generate-image-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, \ + odm_dlkm, skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt \ + $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_ODM_DLKMIMAGE_TARGET := $(BUILT_ODM_DLKMIMAGE_TARGET) +$(INSTALLED_ODM_DLKMIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_ODM_DLKMIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_ODM_DLKM) + $(build-odm_dlkmimage-target) + +ODM_DLKM_NOTICE_DEPS += $(INSTALLED_ODM_DLKMIMAGE_TARGET) + +$(call declare-1p-container,$(INSTALLED_ODM_DLKMIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_ODM_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_ODM_DLKM),$(PRODUCT_OUT)/:/) + +.PHONY: odm_dlkmimage-nodeps odnod +odm_dlkmimage-nodeps odnod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-odm_dlkmimage-target) + +sync: $(INTERNAL_ODM_DLKMIMAGE_FILES) + +else ifdef BOARD_PREBUILT_ODM_DLKMIMAGE +INSTALLED_ODM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/odm_dlkm.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_ODM_DLKMIMAGE),$(INSTALLED_ODM_DLKMIMAGE_TARGET))) +endif + +# ----------------------------------------------------------------- +# system_dlkm partition image + +INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) +ifdef BUILDING_SYSTEM_DLKM_IMAGE + +INTERNAL_SYSTEM_DLKMIMAGE_FILES := \ + $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,\ + $(ALL_DEFAULT_INSTALLED_MODULES)) + +INSTALLED_FILES_FILE_SYSTEM_DLKM := $(PRODUCT_OUT)/installed-files-system_dlkm.txt +INSTALLED_FILES_JSON_SYSTEM_DLKM := $(INSTALLED_FILES_FILE_SYSTEM_DLKM:.txt=.json) +$(INSTALLED_FILES_FILE_SYSTEM_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEM_DLKM) +$(INSTALLED_FILES_FILE_SYSTEM_DLKM): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) + @echo Installed file list: $@ + mkdir -p $(dir $@) + if [ -d "$(BOARD_SYSTEM_DLKM_SRC)" ]; then rsync -rupE $(BOARD_SYSTEM_DLKM_SRC)/ $(TARGET_OUT_SYSTEM_DLKM); fi + rm -f $@ + $(FILESLIST) $(TARGET_OUT_SYSTEM_DLKM) > $(@:.txt=.json) + $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ + +$(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEM_DLKM)) +$(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEM_DLKM)) + +system_dlkmimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,system_dlkm) +BUILT_SYSTEM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/system_dlkm.img +define build-system_dlkmimage-target + $(call pretty,"Target system_dlkm fs image: $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)") + @mkdir -p $(TARGET_OUT_SYSTEM_DLKM) + @mkdir -p $(system_dlkmimage_intermediates) && rm -rf $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt + $(call generate-image-prop-dictionary, $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt, \ + system_dlkm, skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_SYSTEM_DLKM) $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt \ + $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(TARGET_OUT) + $(call assert-max-image-size,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE)) +endef + +# We just build this directly to the install location. +INSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(BUILT_SYSTEM_DLKMIMAGE_TARGET) +$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET): \ + $(INTERNAL_USERIMAGES_DEPS) \ + $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) \ + $(INSTALLED_FILES_FILE_SYSTEM_DLKM) + $(build-system_dlkmimage-target) + +SYSTEM_DLKM_NOTICE_DEPS += $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) + +$(call declare-1p-container,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEM_DLKM),$(PRODUCT_OUT)/:/) + +.PHONY: system_dlkmimage-nodeps sdnod +system_dlkmimage-nodeps sdnod: | $(INTERNAL_USERIMAGES_DEPS) + $(build-system_dlkmimage-target) + +sync: $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) + +else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE +INSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/system_dlkm.img +$(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_DLKMIMAGE),$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET))) +endif + +# ----------------------------------------------------------------- +# dtbo image +ifdef BOARD_PREBUILT_DTBOIMAGE +INSTALLED_DTBOIMAGE_TARGET := $(PRODUCT_OUT)/dtbo.img + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH) + cp $(BOARD_PREBUILT_DTBOIMAGE) $@ + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \ + --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \ + $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS) + +$(call declare-1p-container,$(INSTALLED_DTBOIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_TARGET),$(BOARD_PREBUILT_DTBOIMAGE),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_DTBOIMAGE_TARGET) +else +$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) + cp $(BOARD_PREBUILT_DTBOIMAGE) $@ +endif + +endif # BOARD_PREBUILT_DTBOIMAGE + +# ----------------------------------------------------------------- +# Protected VM firmware image +ifeq ($(BOARD_USES_PVMFWIMAGE),true) +INSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img +INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET := $(PRODUCT_OUT)/pvmfw_embedded.avbpubkey +INTERNAL_PREBUILT_PVMFWIMAGE := packages/modules/Virtualization/pvmfw/pvmfw.img +INTERNAL_PVMFW_EMBEDDED_AVBKEY := external/avb/test/data/testkey_rsa4096_pub.bin + +ifdef BOARD_PREBUILT_PVMFWIMAGE +PREBUILT_PVMFWIMAGE_TARGET := $(BOARD_PREBUILT_PVMFWIMAGE) +else +PREBUILT_PVMFWIMAGE_TARGET := $(INTERNAL_PREBUILT_PVMFWIMAGE) +endif + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_PVMFWIMAGE_TARGET): $(PREBUILT_PVMFWIMAGE_TARGET) $(AVBTOOL) $(BOARD_AVB_PVMFW_KEY_PATH) + cp $< $@ + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_PVMFWIMAGE_PARTITION_SIZE)) \ + --partition_name pvmfw $(INTERNAL_AVB_PVMFW_SIGNING_ARGS) \ + $(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS) + +$(call declare-1p-container,$(INSTALLED_PVMFWIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_PVMFWIMAGE_TARGET),$(PREBUILT_PVMFWIMAGE_TARGET),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_PVMFWIMAGE_TARGET) +else +$(eval $(call copy-one-file,$(PREBUILT_PVMFWIMAGE_TARGET),$(INSTALLED_PVMFWIMAGE_TARGET))) +endif + +$(INSTALLED_PVMFWIMAGE_TARGET): $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) + +$(eval $(call copy-one-file,$(INTERNAL_PVMFW_EMBEDDED_AVBKEY),$(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET))) + +endif # BOARD_USES_PVMFWIMAGE + +# Returns a list of image targets corresponding to the given list of partitions. For example, it +# returns "$(INSTALLED_PRODUCTIMAGE_TARGET)" for "product", or "$(INSTALLED_SYSTEMIMAGE_TARGET) +# $(INSTALLED_VENDORIMAGE_TARGET)" for "system vendor". +# (1): list of partitions like "system", "vendor" or "system product system_ext". +define images-for-partitions +$(strip $(foreach item,$(1),\ + $(if $(filter $(item),system_other),$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),\ + $(if $(filter $(item),init_boot),$(INSTALLED_INIT_BOOT_IMAGE_TARGET),\ + $(INSTALLED_$(call to-upper,$(item))IMAGE_TARGET))))) +endef + +# ----------------------------------------------------------------- +# resource images +ifdef TARGET_PREBUILT_RESOURCE +INSTALLED_RESOURCEIMAGE_TARGET := $(PRODUCT_OUT)/resource.img + +ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_RESOURCEIMAGE_TARGET): $(TARGET_PREBUILT_RESOURCE) $(AVBTOOL) $(BOARD_AVB_RESOURCE_KEY_PATH) + cp $(TARGET_PREBUILT_RESOURCE) $@ + $(AVBTOOL) add_hash_footer \ + --image $@ \ + $(call get-partition-size-argument,$(BOARD_RESOURCEIMG_PARTITION_SIZE)) \ + --partition_name resource $(INTERNAL_AVB_RESOURCE_SIGNING_ARGS) \ + $(BOARD_AVB_RESOURCE_ADD_HASH_FOOTER_ARGS) + +$(call declare-1p-container,$(INSTALLED_RESOURCEIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_RESOURCEIMAGE_TARGET),$(TARGET_PREBUILT_RESOURCE),$(PRODUCT_OUT)/:/) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_RESOURCEIMAGE_TARGET) +else +$(INSTALLED_RESOURCEIMAGE_TARGET): $(TARGET_PREBUILT_RESOURCE) + cp $(TARGET_PREBUILT_RESOURCE) $@ +endif +endif # TARGET_PREBUILT_RESOURCE + +# ----------------------------------------------------------------- +# custom images +INSTALLED_CUSTOMIMAGES_TARGET := + +ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),) +INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS := + +# Sign custom image. +# $(1): the prebuilt custom image. +# $(2): the mount point of the prebuilt custom image. +# $(3): the signed custom image target. +define sign_custom_image +$(3): $(1) $(INTERNAL_USERIMAGES_DEPS) + @echo Target custom image: $(3) + mkdir -p $(dir $(3)) + cp $(1) $(3) +ifeq ($(BOARD_AVB_ENABLE),true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$$$PATH \ + $(AVBTOOL) add_hashtree_footer \ + --image $(3) \ + --key $(BOARD_AVB_$(call to-upper,$(2))_KEY_PATH) \ + --algorithm $(BOARD_AVB_$(call to-upper,$(2))_ALGORITHM) \ + $(call get-partition-size-argument,$(BOARD_AVB_$(call to-upper,$(2))_PARTITION_SIZE)) \ + --partition_name $(2) \ + $(INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS) \ + $(BOARD_AVB_$(call to-upper,$(2))_ADD_HASHTREE_FOOTER_ARGS) +endif +INSTALLED_CUSTOMIMAGES_TARGET += $(3) +endef + +$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ + $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST), \ + $(eval $(call sign_custom_image,$(image),$(partition),$(PRODUCT_OUT)/$(notdir $(image)))))) +endif + +# ----------------------------------------------------------------- +# vbmeta image +ifeq ($(BOARD_AVB_ENABLE),true) + +BUILT_VBMETAIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta.img +AVB_CHAIN_KEY_DIR := $(TARGET_OUT_INTERMEDIATES)/avb_chain_keys + +ifdef BOARD_AVB_KEY_PATH +$(if $(BOARD_AVB_ALGORITHM),,$(error BOARD_AVB_ALGORITHM is not defined)) +else +# If key path isn't specified, use the 4096-bit test key. +BOARD_AVB_ALGORITHM := SHA256_RSA4096 +BOARD_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem +endif + +# AVB signing for system_other.img. +ifdef BUILDING_SYSTEM_OTHER_IMAGE +ifdef BOARD_AVB_SYSTEM_OTHER_KEY_PATH +$(if $(BOARD_AVB_SYSTEM_OTHER_ALGORITHM),,$(error BOARD_AVB_SYSTEM_OTHER_ALGORITHM is not defined)) +else +# If key path isn't specified, use the same key as BOARD_AVB_KEY_PATH. +BOARD_AVB_SYSTEM_OTHER_KEY_PATH := $(BOARD_AVB_KEY_PATH) +BOARD_AVB_SYSTEM_OTHER_ALGORITHM := $(BOARD_AVB_ALGORITHM) +endif + +$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET): $(AVBTOOL) $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH) + @echo Extracting system_other avb key: $@ + @rm -f $@ + @mkdir -p $(dir $@) + $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH) --output $@ + +$(eval $(call declare-0p-target,$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET),)) + +ifndef BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX +BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) +endif + +BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX) +endif # end of AVB for BUILDING_SYSTEM_OTHER_IMAGE + +INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES := \ + $(BOARD_AVB_VBMETA_SYSTEM) \ + $(BOARD_AVB_VBMETA_VENDOR) + +# Not allowing the same partition to appear in multiple groups. +ifneq ($(words $(sort $(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))),$(words $(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))) + $(error BOARD_AVB_VBMETA_SYSTEM and BOARD_AVB_VBMETA_VENDOR cannot have duplicates) +endif + +# When building a standalone recovery image for non-A/B devices, recovery image must be self-signed +# to be verified independently, and cannot be chained into vbmeta.img. See the link below for +# details. +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) +ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) +$(if $(BOARD_AVB_RECOVERY_KEY_PATH),,\ + $(error BOARD_AVB_RECOVERY_KEY_PATH must be defined for if non-A/B is supported. \ + See https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery)) +endif +endif + +# Appends os version as a AVB property descriptor. +SYSTEM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system.os_version:$(SYSTEM_OS_VERSION) + +PRODUCT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.product.os_version:$(PRODUCT_OS_VERSION) + +SYSTEM_EXT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system_ext.os_version:$(SYSTEM_EXT_OS_VERSION) + +INIT_BOOT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.init_boot.os_version:$(INIT_BOOT_OS_VERSION) + +BOOT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.boot.os_version:$(BOOT_OS_VERSION) + +VENDOR_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.vendor.os_version:$(VENDOR_OS_VERSION) + +ODM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.odm.os_version:$(ODM_OS_VERSION) + +VENDOR_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.vendor_dlkm.os_version:$(VENDOR_DLKM_OS_VERSION) + +ODM_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.odm_dlkm.os_version:$(ODM_DLKM_OS_VERSION) + +SYSTEM_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) +BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system_dlkm.os_version:$(SYSTEM_DLKM_OS_VERSION) + +# Appends fingerprint and security patch level as a AVB property descriptor. +BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ + --prop com.android.build.system.security_patch:$(PLATFORM_SECURITY_PATCH) + +BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.product.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ + --prop com.android.build.product.security_patch:$(PLATFORM_SECURITY_PATCH) + +BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system_ext.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ + --prop com.android.build.system_ext.security_patch:$(PLATFORM_SECURITY_PATCH) + +BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.init_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.vendor_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ + +BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.vendor_kernel_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ + +BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.recovery.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.vendor.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.odm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.vendor_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.odm_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.dtbo.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.pvmfw.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) + +# The following vendor- and odm-specific images needs explicit SPL set per board. +# TODO(b/210875415) Is this security_patch property used? Should it be removed from +# boot.img when there is no platform ramdisk included in it? +ifdef BOOT_SECURITY_PATCH +BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.boot.security_patch:$(BOOT_SECURITY_PATCH) +endif + +ifdef INIT_BOOT_SECURITY_PATCH +BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.init_boot.security_patch:$(INIT_BOOT_SECURITY_PATCH) +else ifdef BOOT_SECURITY_PATCH +BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.init_boot.security_patch:$(BOOT_SECURITY_PATCH) +endif + +ifdef VENDOR_SECURITY_PATCH +BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.vendor.security_patch:$(VENDOR_SECURITY_PATCH) +endif + +ifdef ODM_SECURITY_PATCH +BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.odm.security_patch:$(ODM_SECURITY_PATCH) +endif + +ifdef VENDOR_DLKM_SECURITY_PATCH +BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.vendor_dlkm.security_patch:$(VENDOR_DLKM_SECURITY_PATCH) +endif + +ifdef ODM_DLKM_SECURITY_PATCH +BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.odm_dlkm.security_patch:$(ODM_DLKM_SECURITY_PATCH) +endif + +ifdef SYSTEM_DLKM_SECURITY_PATCH +BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ + --prop com.android.build.system_dlkm.security_patch:$(SYSTEM_DLKM_SECURITY_PATCH) +endif + +ifdef PVMFW_SECURITY_PATCH +BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \ + --prop com.android.build.pvmfw.security_patch:$(PVMFW_SECURITY_PATCH) +endif + +BOOT_FOOTER_ARGS := BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS +INIT_BOOT_FOOTER_ARGS := BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS +VENDOR_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS +VENDOR_KERNEL_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS +DTBO_FOOTER_ARGS := BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS +PVMFW_FOOTER_ARGS := BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS +SYSTEM_FOOTER_ARGS := BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS +VENDOR_FOOTER_ARGS := BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS +RECOVERY_FOOTER_ARGS := BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS +PRODUCT_FOOTER_ARGS := BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS +SYSTEM_EXT_FOOTER_ARGS := BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS +ODM_FOOTER_ARGS := BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS +VENDOR_DLKM_FOOTER_ARGS := BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS +ODM_DLKM_FOOTER_ARGS := BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS +SYSTEM_DLKM_FOOTER_ARGS := BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS + +# Helper function that checks and sets required build variables for an AVB chained partition. +# $(1): the partition to enable AVB chain, e.g., boot or system or vbmeta_system. +define _check-and-set-avb-chain-args +$(eval part := $(1)) +$(eval PART=$(call to-upper,$(part))) + +$(eval _key_path := BOARD_AVB_$(PART)_KEY_PATH) +$(eval _signing_algorithm := BOARD_AVB_$(PART)_ALGORITHM) +$(eval _rollback_index := BOARD_AVB_$(PART)_ROLLBACK_INDEX) +$(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION) +$(if $($(_key_path)),,$(error $(_key_path) is not defined)) +$(if $($(_signing_algorithm)),,$(error $(_signing_algorithm) is not defined)) +$(if $($(_rollback_index)),,$(error $(_rollback_index) is not defined)) +$(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined)) + +# Set INTERNAL_AVB_(PART)_SIGNING_ARGS +$(eval _signing_args := INTERNAL_AVB_$(PART)_SIGNING_ARGS) +$(eval $(_signing_args) := \ + --algorithm $($(_signing_algorithm)) --key $($(_key_path))) + +# The recovery partition in non-A/B devices should be verified separately. Skip adding the chain +# partition descriptor for recovery partition into vbmeta.img. +$(if $(or $(filter-out true,$(TARGET_OTA_ALLOW_NON_AB)),$(filter-out recovery,$(part))),\ + $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ + --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey)) + +# Set rollback_index via footer args for non-chained vbmeta image. Chained vbmeta image will pick up +# the index via a separate flag (e.g. BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX). +$(if $(filter $(part),$(part:vbmeta_%=%)),\ + $(eval _footer_args := $(PART)_FOOTER_ARGS) \ + $(eval $($(_footer_args)) += --rollback_index $($(_rollback_index)))) +endef + +# Checks and sets the required build variables for an AVB partition. The partition will be +# configured as a chained partition, if BOARD_AVB__KEY_PATH is defined. Otherwise the +# image descriptor will be included into vbmeta.img, unless it has been already added to any chained +# VBMeta image. +# Multiple boot images can be generated based on BOARD_KERNEL_BINARIES +# but vbmeta would capture the image descriptor of only the first boot +# image specified in BUILT_BOOTIMAGE_TARGET. +# $(1): Partition name, e.g. boot or system. +define check-and-set-avb-args +$(eval _in_chained_vbmeta := $(filter $(1),$(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))) +$(if $(BOARD_AVB_$(call to-upper,$(1))_KEY_PATH),\ + $(if $(_in_chained_vbmeta),\ + $(error Chaining partition "$(1)" in chained VBMeta image is not supported)) \ + $(call _check-and-set-avb-chain-args,$(1)),\ + $(if $(_in_chained_vbmeta),,\ + $(if $(filter boot,$(1)),\ + $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ + --include_descriptors_from_image $(firstword $(call images-for-partitions,$(1)))),\ + $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ + --include_descriptors_from_image $(call images-for-partitions,$(1)))))) +endef + +# Checks and sets build variables for a custom chained partition to include it into vbmeta.img. +# $(1): the custom partition to enable AVB chain. +define check-and-set-custom-avb-chain-args +$(eval part := $(1)) +$(eval PART=$(call to-upper,$(part))) +$(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION) +$(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined)) + +INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ + --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey +endef + +ifdef INSTALLED_BOOTIMAGE_TARGET +$(eval $(call check-and-set-avb-args,boot)) +endif + +ifdef INSTALLED_INIT_BOOT_IMAGE_TARGET +$(eval $(call check-and-set-avb-args,init_boot)) +endif + +ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET +$(eval $(call check-and-set-avb-args,vendor_boot)) +endif + +ifdef INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET +$(eval $(call check-and-set-avb-args,vendor_kernel_boot)) +endif + +ifdef INSTALLED_SYSTEMIMAGE_TARGET +$(eval $(call check-and-set-avb-args,system)) +endif + +ifdef INSTALLED_VENDORIMAGE_TARGET +$(eval $(call check-and-set-avb-args,vendor)) +endif + +ifdef INSTALLED_PRODUCTIMAGE_TARGET +$(eval $(call check-and-set-avb-args,product)) +endif + +ifdef INSTALLED_SYSTEM_EXTIMAGE_TARGET +$(eval $(call check-and-set-avb-args,system_ext)) +endif + +ifdef INSTALLED_ODMIMAGE_TARGET +$(eval $(call check-and-set-avb-args,odm)) +endif + +ifdef INSTALLED_VENDOR_DLKMIMAGE_TARGET +$(eval $(call check-and-set-avb-args,vendor_dlkm)) +endif + +ifdef INSTALLED_ODM_DLKMIMAGE_TARGET +$(eval $(call check-and-set-avb-args,odm_dlkm)) +endif + +ifdef INSTALLED_SYSTEM_DLKMIMAGE_TARGET +$(eval $(call check-and-set-avb-args,system_dlkm)) +endif + +ifdef INSTALLED_DTBOIMAGE_TARGET +$(eval $(call check-and-set-avb-args,dtbo)) +endif + +ifdef INSTALLED_PVMFWIMAGE_TARGET +$(eval $(call check-and-set-avb-args,pvmfw)) +endif + +ifdef INSTALLED_RECOVERYIMAGE_TARGET +$(eval $(call check-and-set-avb-args,recovery)) +endif + +# Not using INSTALLED_VBMETA_SYSTEMIMAGE_TARGET as it won't be set yet. +ifdef BOARD_AVB_VBMETA_SYSTEM +$(eval $(call check-and-set-avb-args,vbmeta_system)) +endif + +ifdef BOARD_AVB_VBMETA_VENDOR +$(eval $(call check-and-set-avb-args,vbmeta_vendor)) +endif + +ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),) +$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ + $(eval $(call check-and-set-custom-avb-chain-args,$(partition)))) +endif + +ifdef TARGET_PREBUILT_RESOURCE +$(eval $(call check-and-set-avb-args,resource)) +endif + +# Add kernel cmdline descriptor for kernel to mount system.img as root with +# dm-verity. This works when system.img is either chained or not-chained: +# - chained: The --setup_as_rootfs_from_kernel option will add dm-verity kernel +# cmdline descriptor to system.img +# - not-chained: The --include_descriptors_from_image option for make_vbmeta_image +# will include the kernel cmdline descriptor from system.img into vbmeta.img +ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) +ifeq ($(filter system, $(BOARD_SUPER_PARTITION_PARTITION_LIST)),) +BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --setup_as_rootfs_from_kernel +endif +endif + +BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --padding_size 4096 +BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += --padding_size 4096 +BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += --padding_size 4096 + +ifeq (eng,$(filter eng, $(TARGET_BUILD_VARIANT))) +# We only need the flag in top-level vbmeta.img. +BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --set_hashtree_disabled_flag +endif + +ifdef BOARD_AVB_ROLLBACK_INDEX +BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --rollback_index $(BOARD_AVB_ROLLBACK_INDEX) +endif + +ifdef BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX +BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += \ + --rollback_index $(BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX) +endif + +ifdef BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX +BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += \ + --rollback_index $(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX) +endif + +# $(1): the directory to extract public keys to +define extract-avb-chain-public-keys + $(if $(BOARD_AVB_BOOT_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_BOOT_KEY_PATH) \ + --output $(1)/boot.avbpubkey) + $(if $(BOARD_AVB_INIT_BOOT_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_INIT_BOOT_KEY_PATH) \ + --output $(1)/init_boot.avbpubkey) + $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),\ + $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_BOOT_KEY_PATH) \ + --output $(1)/vendor_boot.avbpubkey) + $(if $(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH),\ + $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH) \ + --output $(1)/vendor_kernel_boot.avbpubkey) + $(if $(BOARD_AVB_SYSTEM_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_KEY_PATH) \ + --output $(1)/system.avbpubkey) + $(if $(BOARD_AVB_VENDOR_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_KEY_PATH) \ + --output $(1)/vendor.avbpubkey) + $(if $(BOARD_AVB_PRODUCT_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_PRODUCT_KEY_PATH) \ + --output $(1)/product.avbpubkey) + $(if $(BOARD_AVB_SYSTEM_EXT_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_EXT_KEY_PATH) \ + --output $(1)/system_ext.avbpubkey) + $(if $(BOARD_AVB_ODM_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_ODM_KEY_PATH) \ + --output $(1)/odm.avbpubkey) + $(if $(BOARD_AVB_VENDOR_DLKM_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_DLKM_KEY_PATH) \ + --output $(1)/vendor_dlkm.avbpubkey) + $(if $(BOARD_AVB_ODM_DLKM_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_ODM_DLKM_KEY_PATH) \ + --output $(1)/odm_dlkm.avbpubkey) + $(if $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH) \ + --output $(1)/system_dlkm.avbpubkey) + $(if $(BOARD_AVB_DTBO_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_DTBO_KEY_PATH) \ + --output $(1)/dtbo.avbpubkey) + $(if $(BOARD_AVB_PVMFW_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_PVMFW_KEY_PATH) \ + --output $(1)/pvmfw.avbpubkey) + $(if $(BOARD_AVB_RECOVERY_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_RECOVERY_KEY_PATH) \ + --output $(1)/recovery.avbpubkey) + $(if $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) \ + --output $(1)/vbmeta_system.avbpubkey) + $(if $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \ + --output $(1)/vbmeta_vendor.avbpubkey) + $(if $(BOARD_CUSTOMIMAGES_PARTITION_LIST),\ + $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ + $(AVBTOOL) extract_public_key --key $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH) \ + --output $(1)/$(partition).avbpubkey;)) + $(if $(BOARD_AVB_RESOURCE_KEY_PATH),\ + $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_RESOURCE_KEY_PATH) \ + --output $(1)/resource.avbpubkey) +endef + +# Builds a chained VBMeta image. This VBMeta image will contain the descriptors for the partitions +# specified in BOARD_AVB_VBMETA_. The built VBMeta image will be included into the top-level +# vbmeta image as a chained partition. For example, if a target defines `BOARD_AVB_VBMETA_SYSTEM +# := system system_ext`, `vbmeta_system.img` will be created that includes the descriptors for +# `system.img` and `system_ext.img`. `vbmeta_system.img` itself will be included into +# `vbmeta.img` as a chained partition. +# $(1): VBMeta image name, such as "vbmeta_system", "vbmeta_vendor" etc. +# $(2): Output filename. +define build-chained-vbmeta-image + $(call pretty,"Target chained vbmeta image: $@") + $(hide) $(AVBTOOL) make_vbmeta_image \ + $(INTERNAL_AVB_$(call to-upper,$(1))_SIGNING_ARGS) \ + $(BOARD_AVB_MAKE_$(call to-upper,$(1))_IMAGE_ARGS) \ + $(foreach image,$(BOARD_AVB_$(call to-upper,$(1))), \ + --include_descriptors_from_image $(call images-for-partitions,$(image))) \ + --output $@ +endef + +ifdef BUILDING_SYSTEM_IMAGE +ifdef BOARD_AVB_VBMETA_SYSTEM +INSTALLED_VBMETA_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_system.img +$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET): \ + $(AVBTOOL) \ + $(call images-for-partitions,$(BOARD_AVB_VBMETA_SYSTEM)) \ + $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) + $(call build-chained-vbmeta-image,vbmeta_system) + +$(call declare-1p-container,$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET),) + +SYSTEM_NOTICE_DEPS += $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) +endif +endif # BUILDING_SYSTEM_IMAGE + +ifdef BOARD_AVB_VBMETA_VENDOR +INSTALLED_VBMETA_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_vendor.img +$(INSTALLED_VBMETA_VENDORIMAGE_TARGET): \ + $(AVBTOOL) \ + $(call images-for-partitions,$(BOARD_AVB_VBMETA_VENDOR)) \ + $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) + $(call build-chained-vbmeta-image,vbmeta_vendor) + +$(call declare-1p-container,$(INSTALLED_VBMETA_VENDORIMAGE_TARGET),) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) +endif + +define build-vbmetaimage-target + $(call pretty,"Target vbmeta image: $(INSTALLED_VBMETAIMAGE_TARGET)") + $(hide) mkdir -p $(AVB_CHAIN_KEY_DIR) + $(call extract-avb-chain-public-keys, $(AVB_CHAIN_KEY_DIR)) + $(hide) $(AVBTOOL) make_vbmeta_image \ + $(INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS) \ + $(PRIVATE_AVB_VBMETA_SIGNING_ARGS) \ + $(BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS) \ + --output $@ + $(hide) rm -rf $(AVB_CHAIN_KEY_DIR) +endef + +ifdef BUILDING_VBMETA_IMAGE +INSTALLED_VBMETAIMAGE_TARGET := $(BUILT_VBMETAIMAGE_TARGET) +$(INSTALLED_VBMETAIMAGE_TARGET): PRIVATE_AVB_VBMETA_SIGNING_ARGS := \ + --algorithm $(BOARD_AVB_ALGORITHM) --key $(BOARD_AVB_KEY_PATH) + +$(INSTALLED_VBMETAIMAGE_TARGET): \ + $(AVBTOOL) \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ + $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) \ + $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ + $(INSTALLED_ODMIMAGE_TARGET) \ + $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ + $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ + $(INSTALLED_DTBOIMAGE_TARGET) \ + $(INSTALLED_PVMFWIMAGE_TARGET) \ + $(INSTALLED_CUSTOMIMAGES_TARGET) \ + $(INSTALLED_RESOURCEIMAGE_TARGET) \ + $(INSTALLED_RECOVERYIMAGE_TARGET) \ + $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \ + $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) \ + $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \ + $(BOARD_AVB_KEY_PATH) + $(build-vbmetaimage-target) + +$(call declare-1p-container,$(INSTALLED_VBMETAIMAGE_TARGET),) + +UNMOUNTED_NOTICE_DEPS += $(INSTALLED_VBMETAIMAGE_TARGET) + +.PHONY: vbmetaimage-nodeps +vbmetaimage-nodeps: PRIVATE_AVB_VBMETA_SIGNING_ARGS := \ + --algorithm $(BOARD_AVB_ALGORITHM) --key $(BOARD_AVB_KEY_PATH) +vbmetaimage-nodeps: + $(build-vbmetaimage-target) +endif # BUILDING_VBMETA_IMAGE + +endif # BOARD_AVB_ENABLE + +# List of files from all images +INTERNAL_ALLIMAGES_FILES := \ + $(FULL_SYSTEMIMAGE_DEPS) \ + $(INTERNAL_RAMDISK_FILES) \ + $(INTERNAL_USERDATAIMAGE_FILES) \ + $(INTERNAL_VENDORIMAGE_FILES) \ + $(INTERNAL_PRODUCTIMAGE_FILES) \ + $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \ + $(INTERNAL_ODMIMAGE_FILES) \ + $(INTERNAL_VENDOR_DLKMIMAGE_FILES) \ + $(INTERNAL_ODM_DLKMIMAGE_FILES) \ + $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) \ + +# ----------------------------------------------------------------- +# Check VINTF of build + +# Note: vendor_dlkm, odm_dlkm, and system_dlkm does not have VINTF files. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) + +intermediates := $(call intermediates-dir-for,PACKAGING,check_vintf_all) +check_vintf_all_deps := + +# The build system only writes VINTF metadata to */etc/vintf paths. Legacy paths aren't needed here +# because they are only used for prebuilt images. +check_vintf_common_srcs_patterns := \ + $(TARGET_OUT)/etc/vintf/% \ + $(TARGET_OUT_VENDOR)/etc/vintf/% \ + $(TARGET_OUT_ODM)/etc/vintf/% \ + $(TARGET_OUT_PRODUCT)/etc/vintf/% \ + $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \ + +check_vintf_common_srcs := $(sort $(filter $(check_vintf_common_srcs_patterns),$(INTERNAL_ALLIMAGES_FILES))) +check_vintf_common_srcs_patterns := + +check_vintf_has_system := +check_vintf_has_vendor := + +ifneq (,$(filter EMPTY_ODM_SKU_PLACEHOLDER,$(ODM_MANIFEST_SKUS))) +$(error EMPTY_ODM_SKU_PLACEHOLDER is an internal variable and cannot be used for ODM_MANIFEST_SKUS) +endif +ifneq (,$(filter EMPTY_VENDOR_SKU_PLACEHOLDER,$(DEVICE_MANIFEST_SKUS))) +$(error EMPTY_VENDOR_SKU_PLACEHOLDER is an internal variable and cannot be used for DEIVCE_MANIFEST_SKUS) +endif + +# -- Check system manifest / matrix including fragments (excluding other framework manifests / matrices, e.g. product); +check_vintf_system_deps := $(filter $(TARGET_OUT)/etc/vintf/%, $(check_vintf_common_srcs)) +ifneq ($(check_vintf_system_deps),) +check_vintf_has_system := true + +check_vintf_system_log := $(intermediates)/check_vintf_system.log +check_vintf_all_deps += $(check_vintf_system_log) +$(check_vintf_system_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_system_deps) + @( $< --check-one --dirmap /system:$(TARGET_OUT) > $@ 2>&1 ) || ( cat $@ && exit 1 ) +$(call declare-0p-target,$(check_vintf_system_log)) +check_vintf_system_log := + +vintffm_log := $(intermediates)/vintffm.log +check_vintf_all_deps += $(vintffm_log) +$(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps) + @( $< --check --dirmap /system:$(TARGET_OUT) \ + $(VINTF_FRAMEWORK_MANIFEST_FROZEN_DIR) > $@ 2>&1 ) || ( cat $@ && exit 1 ) + +$(call declare-0p-target,$(vintffm_log)) + +endif # check_vintf_system_deps +check_vintf_system_deps := + +# -- Check vendor manifest / matrix including fragments (excluding other device manifests / matrices) +check_vintf_vendor_deps := $(filter $(TARGET_OUT_VENDOR)/etc/vintf/%, $(check_vintf_common_srcs)) +ifneq ($(check_vintf_vendor_deps),) +check_vintf_has_vendor := true +check_vintf_vendor_log := $(intermediates)/check_vintf_vendor.log +check_vintf_all_deps += $(check_vintf_vendor_log) +# Check vendor SKU=(empty) case when: +# - DEVICE_MANIFEST_FILE is not empty; OR +# - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used) +$(check_vintf_vendor_log): PRIVATE_VENDOR_SKUS := \ + $(if $(DEVICE_MANIFEST_FILE),EMPTY_VENDOR_SKU_PLACEHOLDER,\ + $(if $(DEVICE_MANIFEST_SKUS),,EMPTY_VENDOR_SKU_PLACEHOLDER)) \ + $(DEVICE_MANIFEST_SKUS) +$(check_vintf_vendor_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_vendor_deps) + $(foreach vendor_sku,$(PRIVATE_VENDOR_SKUS), \ + ( $< --check-one --dirmap /vendor:$(TARGET_OUT_VENDOR) \ + --property ro.boot.product.vendor.sku=$(filter-out EMPTY_VENDOR_SKU_PLACEHOLDER,$(vendor_sku)) \ + > $@ 2>&1 ) || ( cat $@ && exit 1 ); ) +$(call declare-0p-target,$(check_vintf_vendor_log)) +check_vintf_vendor_log := +endif # check_vintf_vendor_deps +check_vintf_vendor_deps := + +# -- Kernel version and configurations. +ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true) + +BUILT_KERNEL_CONFIGS_FILE := $(intermediates)/kernel_configs.txt +BUILT_KERNEL_VERSION_FILE := $(intermediates)/kernel_version.txt + +my_board_extracted_kernel := + +# BOARD_KERNEL_CONFIG_FILE and BOARD_KERNEL_VERSION can be used to override the values extracted +# from INSTALLED_KERNEL_TARGET. +ifdef BOARD_KERNEL_CONFIG_FILE +ifdef BOARD_KERNEL_VERSION +$(BUILT_KERNEL_CONFIGS_FILE): $(BOARD_KERNEL_CONFIG_FILE) + cp $< $@ +$(BUILT_KERNEL_VERSION_FILE): + echo $(BOARD_KERNEL_VERSION) > $@ + +$(call declare-0p-target,$(BUILT_KERNEL_CONFIGS_FILE)) +$(call declare-0p-target,$(BUILT_KERNEL_VERSION_FILE)) + +my_board_extracted_kernel := true +endif # BOARD_KERNEL_VERSION +endif # BOARD_KERNEL_CONFIG_FILE + +ifneq ($(my_board_extracted_kernel),true) +# Tools for decompression that is not in PATH. +# Check $(EXTRACT_KERNEL) for decompression algorithms supported by the script. +# Algorithms that are in the script but not in this list will be found in PATH. +my_decompress_tools := \ + lz4:$(HOST_OUT_EXECUTABLES)/lz4 \ + +endif # my_board_extracted_kernel + +ifneq ($(my_board_extracted_kernel),true) +ifdef INSTALLED_KERNEL_TARGET +$(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE) +$(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools) +$(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair))) +$(BUILT_KERNEL_CONFIGS_FILE): $(EXTRACT_KERNEL) $(firstword $(INSTALLED_KERNEL_TARGET)) + $< --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(firstword $(INSTALLED_KERNEL_TARGET)) \ + --output-configs $@ \ + --output-release $(BUILT_KERNEL_VERSION_FILE) + +$(call declare-0p-target,$(BUILT_KERNEL_CONFIGS_FILE)) + +my_board_extracted_kernel := true +endif # INSTALLED_KERNEL_TARGET +endif # my_board_extracted_kernel + +ifneq ($(my_board_extracted_kernel),true) +ifdef INSTALLED_BOOTIMAGE_TARGET +$(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE) +$(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools) +$(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair))) +$(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_UNPACKED_BOOTIMG := $(intermediates)/unpacked_bootimage +$(BUILT_KERNEL_CONFIGS_FILE): \ + $(HOST_OUT_EXECUTABLES)/unpack_bootimg \ + $(EXTRACT_KERNEL) \ + $(INSTALLED_BOOTIMAGE_TARGET) + $(HOST_OUT_EXECUTABLES)/unpack_bootimg --boot_img $(INSTALLED_BOOTIMAGE_TARGET) --out $(PRIVATE_UNPACKED_BOOTIMG) + $(EXTRACT_KERNEL) --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(PRIVATE_UNPACKED_BOOTIMG)/kernel \ + --output-configs $@ \ + --output-release $(BUILT_KERNEL_VERSION_FILE) + +$(call declare-0p-target,$(BUILT_KERNEL_CONFIGS_FILE)) + +my_board_extracted_kernel := true +endif # INSTALLED_BOOTIMAGE_TARGET +endif # my_board_extracted_kernel + +ifneq ($(my_board_extracted_kernel),true) +$(warning Neither INSTALLED_KERNEL_TARGET nor INSTALLED_BOOTIMAGE_TARGET is defined when \ + PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS is true. Information about the updated kernel \ + cannot be built into OTA update package. You can fix this by: \ + (1) setting TARGET_NO_KERNEL to false and installing the built kernel to $(PRODUCT_OUT)/kernel,\ + so that kernel information will be extracted from the built kernel; or \ + (2) Add a prebuilt boot image and specify it in BOARD_PREBUILT_BOOTIMAGE; or \ + (3) extracting kernel configuration and defining BOARD_KERNEL_CONFIG_FILE and \ + BOARD_KERNEL_VERSION manually; or \ + (4) unsetting PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS manually.) +# Clear their values to indicate that these two files does not exist. +BUILT_KERNEL_CONFIGS_FILE := +BUILT_KERNEL_VERSION_FILE := +endif + +my_decompress_tools := +my_board_extracted_kernel := + +endif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS + +# -- Check VINTF compatibility of build. +# Skip partial builds; only check full builds. Only check if: +# - PRODUCT_ENFORCE_VINTF_MANIFEST is true +# - system / vendor VINTF metadata exists +# - Building product / system_ext / odm images if board has product / system_ext / odm images +ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true) +ifeq ($(check_vintf_has_system),true) +ifeq ($(check_vintf_has_vendor),true) +ifeq ($(filter true,$(BUILDING_ODM_IMAGE)),$(filter true,$(BOARD_USES_ODMIMAGE))) +ifeq ($(filter true,$(BUILDING_PRODUCT_IMAGE)),$(filter true,$(BOARD_USES_PRODUCTIMAGE))) +ifeq ($(filter true,$(BUILDING_SYSTEM_EXT_IMAGE)),$(filter true,$(BOARD_USES_SYSTEM_EXTIMAGE))) + +check_vintf_compatible_log := $(intermediates)/check_vintf_compatible.log +check_vintf_all_deps += $(check_vintf_compatible_log) + +check_vintf_compatible_args := +check_vintf_compatible_deps := $(check_vintf_common_srcs) + +ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true) +ifneq (,$(BUILT_KERNEL_VERSION_FILE)$(BUILT_KERNEL_CONFIGS_FILE)) +check_vintf_compatible_args += --kernel $(BUILT_KERNEL_VERSION_FILE):$(BUILT_KERNEL_CONFIGS_FILE) +check_vintf_compatible_deps += $(BUILT_KERNEL_CONFIGS_FILE) $(BUILT_KERNEL_VERSION_FILE) +endif # BUILT_KERNEL_VERSION_FILE != "" || BUILT_KERNEL_CONFIGS_FILE != "" +endif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS + +check_vintf_compatible_args += \ + --dirmap /system:$(TARGET_OUT) \ + --dirmap /vendor:$(TARGET_OUT_VENDOR) \ + --dirmap /odm:$(TARGET_OUT_ODM) \ + --dirmap /product:$(TARGET_OUT_PRODUCT) \ + --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \ + +ifdef PRODUCT_SHIPPING_API_LEVEL +check_vintf_compatible_args += --property ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL) +endif # PRODUCT_SHIPPING_API_LEVEL + +$(check_vintf_compatible_log): PRIVATE_CHECK_VINTF_ARGS := $(check_vintf_compatible_args) +$(check_vintf_compatible_log): PRIVATE_CHECK_VINTF_DEPS := $(check_vintf_compatible_deps) +# Check ODM SKU=(empty) case when: +# - ODM_MANIFEST_FILES is not empty; OR +# - ODM_MANIFEST_FILES is empty AND ODM_MANIFEST_SKUS is empty (only ODM manifest fragments are used) +$(check_vintf_compatible_log): PRIVATE_ODM_SKUS := \ + $(if $(ODM_MANIFEST_FILES),EMPTY_ODM_SKU_PLACEHOLDER,\ + $(if $(ODM_MANIFEST_SKUS),,EMPTY_ODM_SKU_PLACEHOLDER)) \ + $(ODM_MANIFEST_SKUS) +# Check vendor SKU=(empty) case when: +# - DEVICE_MANIFEST_FILE is not empty; OR +# - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used) +$(check_vintf_compatible_log): PRIVATE_VENDOR_SKUS := \ + $(if $(DEVICE_MANIFEST_FILE),EMPTY_VENDOR_SKU_PLACEHOLDER,\ + $(if $(DEVICE_MANIFEST_SKUS),,EMPTY_VENDOR_SKU_PLACEHOLDER)) \ + $(DEVICE_MANIFEST_SKUS) +$(check_vintf_compatible_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_compatible_deps) + @echo "PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS=$(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS)" > $@ + @echo -n -e 'Deps: \n ' >> $@ + @sed 's/ /\n /g' <<< "$(PRIVATE_CHECK_VINTF_DEPS)" >> $@ + @echo -n -e 'Args: \n ' >> $@ + @cat <<< "$(PRIVATE_CHECK_VINTF_ARGS)" >> $@ + $(foreach odm_sku,$(PRIVATE_ODM_SKUS), $(foreach vendor_sku,$(PRIVATE_VENDOR_SKUS), \ + echo "For ODM SKU = $(odm_sku), vendor SKU = $(vendor_sku)" >> $@; \ + ( $< --check-compat $(PRIVATE_CHECK_VINTF_ARGS) \ + --property ro.boot.product.hardware.sku=$(filter-out EMPTY_ODM_SKU_PLACEHOLDER,$(odm_sku)) \ + --property ro.boot.product.vendor.sku=$(filter-out EMPTY_VENDOR_SKU_PLACEHOLDER,$(vendor_sku)) \ + >> $@ 2>&1 ) || (cat $@ && exit 1); )) + +$(call declare-0p-target,$(check_vintf_compatible_log)) + +check_vintf_compatible_log := +check_vintf_compatible_args := +check_vintf_compatible_deps := + +endif # BUILDING_SYSTEM_EXT_IMAGE equals BOARD_USES_SYSTEM_EXTIMAGE +endif # BUILDING_PRODUCT_IMAGE equals BOARD_USES_PRODUCTIMAGE +endif # BUILDING_ODM_IMAGE equals BOARD_USES_ODMIMAGE +endif # check_vintf_has_vendor +endif # check_vintf_has_system +endif # PRODUCT_ENFORCE_VINTF_MANIFEST + +# Add all logs of VINTF checks to dist builds +droid_targets: $(check_vintf_all_deps) +$(call dist-for-goals, droid_targets, $(check_vintf_all_deps)) + +# Helper alias to check all VINTF of current build. +.PHONY: check-vintf-all +check-vintf-all: $(check_vintf_all_deps) + $(foreach file,$^,echo "$(file)"; cat "$(file)"; echo;) + +check_vintf_has_vendor := +check_vintf_has_system := +check_vintf_common_srcs := +check_vintf_all_deps := +intermediates := +endif # !TARGET_BUILD_UNBUNDLED + +# ----------------------------------------------------------------- +# Check image sizes <= size of super partition + +ifeq (,$(TARGET_BUILD_UNBUNDLED)) + +ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION)) + +PARTITIONS_AND_OTHER_IN_SUPER := $(BOARD_SUPER_PARTITION_PARTITION_LIST) + +# Add the system other image to the misc_info. Because factory ota may install system_other to the super partition. +ifdef BUILDING_SYSTEM_OTHER_IMAGE +PARTITIONS_AND_OTHER_IN_SUPER += system_other +endif # BUILDING_SYSTEM_OTHER_IMAGE + +# $(1): misc_info.txt +# #(2): optional log file +define check-all-partition-sizes-target + mkdir -p $(dir $(1)) + rm -f $(1) + $(call dump-super-image-info, $(1)) + $(foreach partition,$(PARTITIONS_AND_OTHER_IN_SUPER), \ + echo "$(partition)_image="$(call images-for-partitions,$(partition)) >> $(1);) + $(CHECK_PARTITION_SIZES) $(if $(2),--logfile $(2),-v) $(1) +endef + +check_all_partition_sizes_log := $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/check_all_partition_sizes.log +droid_targets: $(check_all_partition_sizes_log) +$(call dist-for-goals, droid_targets, $(check_all_partition_sizes_log)) + +$(check_all_partition_sizes_log): \ + $(CHECK_PARTITION_SIZES) \ + $(call images-for-partitions,$(PARTITIONS_AND_OTHER_IN_SUPER)) + $(call check-all-partition-sizes-target, \ + $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/misc_info.txt, \ + $@) + +$(call declare-0p-target,$(check_all_partition_sizes_log)) + +.PHONY: check-all-partition-sizes +check-all-partition-sizes: $(check_all_partition_sizes_log) + +.PHONY: check-all-partition-sizes-nodeps +check-all-partition-sizes-nodeps: + $(call check-all-partition-sizes-target, \ + $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes-nodeps)/misc_info.txt) + +endif # PRODUCT_BUILD_SUPER_PARTITION + +endif # !TARGET_BUILD_UNBUNDLED + +# ----------------------------------------------------------------- +# bring in the installer image generation defines if necessary +ifeq ($(TARGET_USE_DISKINSTALLER),true) +include bootable/diskinstaller/config.mk +endif + +# ----------------------------------------------------------------- +# host tools needed to build dist and OTA packages + +ifeq ($(BUILD_OS),darwin) + build_ota_package := false + build_otatools_package := false +else + # Set build_ota_package, and allow opt-out below. + build_ota_package := true + ifeq ($(TARGET_SKIP_OTA_PACKAGE),true) + build_ota_package := false + endif + ifneq (,$(filter address, $(SANITIZE_TARGET))) + build_ota_package := false + endif + ifeq ($(TARGET_PRODUCT),sdk) + build_ota_package := false + endif + # A target without a kernel may be one of the following: + # - A generic target. In this case, the OTA package usually isn't built. + # PRODUCT_BUILD_GENERIC_OTA_PACKAGE may be set to true to force OTA package + # generation. + # - A real device target, with TARGET_NO_KERNEL set to true and + # BOARD_PREBUILT_BOOTIMAGE set. In this case, it is valid to generate + # an OTA package. + ifneq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true) + ifneq ($(filter generic%,$(TARGET_DEVICE)),) + build_ota_package := false + endif + ifeq ($(INSTALLED_BOOTIMAGE_TARGET),) + ifeq ($(TARGET_NO_KERNEL),true) + build_ota_package := false + endif + endif # INSTALLED_BOOTIMAGE_TARGET == "" + ifeq ($(recovery_fstab),) + build_ota_package := false + endif + endif # PRODUCT_BUILD_GENERIC_OTA_PACKAGE + + # Set build_otatools_package, and allow opt-out below. + build_otatools_package := true + ifeq ($(TARGET_SKIP_OTATOOLS_PACKAGE),true) + build_otatools_package := false + endif +endif + +ifeq ($(build_otatools_package),true) + +INTERNAL_OTATOOLS_MODULES := \ + aapt2 \ + add_img_to_target_files \ + apksigner \ + append2simg \ + avbtool \ + blk_alloc_to_base_fs \ + boot_signer \ + brillo_update_payload \ + brotli \ + bsdiff \ + build_image \ + build_super_image \ + build_verity_metadata \ + build_verity_tree \ + care_map_generator \ + check_ota_package_signature \ + check_target_files_signatures \ + check_target_files_vintf \ + checkvintf \ + delta_generator \ + e2fsck \ + e2fsdroid \ + fc_sort \ + fec \ + fsck.erofs \ + fsck.f2fs \ + fs_config \ + generate_gki_certificate \ + generate_verity_key \ + host_init_verifier \ + img2simg \ + img_from_target_files \ + imgdiff \ + libconscrypt_openjdk_jni \ + lpmake \ + lpunpack \ + lz4 \ + make_f2fs \ + make_f2fs_casefold \ + merge_target_files \ + minigzip \ + mk_combined_img \ + mkbootfs \ + mkbootimg \ + mke2fs \ + mke2fs.conf \ + mkfs.erofs \ + mkf2fsuserimg.sh \ + mksquashfs \ + mksquashfsimage.sh \ + mkuserimg_mke2fs \ + ota_extractor \ + ota_from_target_files \ + repack_bootimg \ + secilc \ + sefcontext_compile \ + sgdisk \ + shflags \ + sign_apex \ + sign_target_files_apks \ + sign_virt_apex \ + signapk \ + simg2img \ + sload_f2fs \ + toybox \ + tune2fs \ + unpack_bootimg \ + update_host_simulator \ + validate_target_files \ + verity_signer \ + verity_verifier \ + zipalign \ + zucchini \ + +# Additional tools to unpack and repack the apex file. +INTERNAL_OTATOOLS_MODULES += \ + apexer \ + apex_compression_tool \ + deapexer \ + debugfs_static \ + merge_zips \ + resize2fs \ + soong_zip \ + +ifeq (true,$(PRODUCT_SUPPORTS_VBOOT)) +INTERNAL_OTATOOLS_MODULES += \ + futility \ + vboot_signer +endif + +INTERNAL_OTATOOLS_FILES := \ + $(filter $(HOST_OUT)/%,$(call module-installed-files,$(INTERNAL_OTATOOLS_MODULES))) + +.PHONY: otatools +otatools: $(INTERNAL_OTATOOLS_FILES) + +# For each module, recursively resolve its host shared library dependencies. Then we have a full +# list of modules whose installed files need to be packed. +INTERNAL_OTATOOLS_MODULES_WITH_DEPS := \ + $(sort $(INTERNAL_OTATOOLS_MODULES) \ + $(foreach m,$(INTERNAL_OTATOOLS_MODULES),$(call get-all-shared-libs-deps,$(m)))) + +INTERNAL_OTATOOLS_PACKAGE_FILES := \ + $(filter $(HOST_OUT)/%,$(call module-installed-files,$(INTERNAL_OTATOOLS_MODULES_WITH_DEPS))) + +INTERNAL_OTATOOLS_PACKAGE_FILES += \ + $(sort $(shell find build/make/target/product/security -type f -name "*.x509.pem" -o \ + -name "*.pk8" -o -name verity_key)) + +ifneq (,$(wildcard device)) +INTERNAL_OTATOOLS_PACKAGE_FILES += \ + $(sort $(shell find device $(wildcard vendor) -type f -name "*.pk8" -o -name "verifiedboot*" -o \ + -name "*.pem" -o -name "oem*.prop" -o -name "*.avbpubkey")) +endif +ifneq (,$(wildcard external/avb)) +INTERNAL_OTATOOLS_PACKAGE_FILES += \ + $(sort $(shell find external/avb/test/data -type f -name "testkey_*.pem" -o \ + -name "atx_metadata.bin")) +endif +ifeq (true,$(PRODUCT_SUPPORTS_VBOOT)) +INTERNAL_OTATOOLS_PACKAGE_FILES += \ + $(sort $(shell find external/vboot_reference/tests/devkeys -type f)) +endif + +INTERNAL_OTATOOLS_RELEASETOOLS := \ + $(sort $(shell find build/make/tools/releasetools -name "*.pyc" -prune -o \ + \( -type f -o -type l \) -print)) + +BUILT_OTATOOLS_PACKAGE := $(PRODUCT_OUT)/otatools.zip +$(BUILT_OTATOOLS_PACKAGE): PRIVATE_ZIP_ROOT := $(call intermediates-dir-for,PACKAGING,otatools)/otatools +$(BUILT_OTATOOLS_PACKAGE): PRIVATE_OTATOOLS_PACKAGE_FILES := $(INTERNAL_OTATOOLS_PACKAGE_FILES) +$(BUILT_OTATOOLS_PACKAGE): PRIVATE_OTATOOLS_RELEASETOOLS := $(INTERNAL_OTATOOLS_RELEASETOOLS) +$(BUILT_OTATOOLS_PACKAGE): $(INTERNAL_OTATOOLS_PACKAGE_FILES) $(INTERNAL_OTATOOLS_RELEASETOOLS) +$(BUILT_OTATOOLS_PACKAGE): $(SOONG_ZIP) $(ZIP2ZIP) + @echo "Package OTA tools: $@" + rm -rf $@ $(PRIVATE_ZIP_ROOT) + mkdir -p $(dir $@) + $(call copy-files-with-structure,$(PRIVATE_OTATOOLS_PACKAGE_FILES),$(HOST_OUT)/,$(PRIVATE_ZIP_ROOT)) + $(call copy-files-with-structure,$(PRIVATE_OTATOOLS_RELEASETOOLS),build/make/tools/,$(PRIVATE_ZIP_ROOT)) + cp $(SOONG_ZIP) $(ZIP2ZIP) $(MERGE_ZIPS) $(PRIVATE_ZIP_ROOT)/bin/ + $(SOONG_ZIP) -o $@ -C $(PRIVATE_ZIP_ROOT) -D $(PRIVATE_ZIP_ROOT) + +$(call declare-1p-container,$(BUILT_OTATOOLS_PACKAGE),build) +$(call declare-container-license-deps,$(INTERNAL_OTATOOLS_PACKAGE_FILES) $(INTERNAL_OTATOOLS_RELEASETOOLS),$(BUILT_OTATOOLS_PACKAGE):) + +.PHONY: otatools-package +otatools-package: $(BUILT_OTATOOLS_PACKAGE) + +endif # build_otatools_package + +# ----------------------------------------------------------------- +# misc_info.txt + +INSTALLED_MISC_INFO_TARGET := $(PRODUCT_OUT)/misc_info.txt + +ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),) +# default to common dir for device vendor +tool_extensions := $(TARGET_DEVICE_DIR)/../common +else +tool_extensions := $(TARGET_RELEASETOOLS_EXTENSIONS) +endif +.KATI_READONLY := tool_extensions + +# $1: boot image file name +define misc_boot_size +$(subst .img,_size,$(1))=$(BOARD_KERNEL$(call to-upper,$(subst boot,,$(subst .img,,$(1))))_BOOTIMAGE_PARTITION_SIZE) +endef + +$(INSTALLED_MISC_INFO_TARGET): + rm -f $@ + $(call pretty,"Target misc_info.txt: $@") + $(hide) echo "recovery_api_version=$(RECOVERY_API_VERSION)" >> $@ + $(hide) echo "fstab_version=$(RECOVERY_FSTAB_VERSION)" >> $@ +ifdef BOARD_FLASH_BLOCK_SIZE + $(hide) echo "blocksize=$(BOARD_FLASH_BLOCK_SIZE)" >> $@ +endif +ifneq ($(strip $(BOARD_BOOTIMAGE_PARTITION_SIZE))$(strip $(BOARD_KERNEL_BINARIES)),) + $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),\ + echo "$(call misc_boot_size,$(notdir $(b)))" >> $@;) +endif +ifeq ($(INSTALLED_BOOTIMAGE_TARGET),) + $(hide) echo "no_boot=true" >> $@ +else + echo "boot_images=$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(notdir $(b)))" >> $@ +endif +ifneq ($(INSTALLED_INIT_BOOT_IMAGE_TARGET),) + $(hide) echo "init_boot=true" >> $@ + $(hide) echo "init_boot_size=$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)" >> $@ +endif +ifeq ($(BOARD_RAMDISK_USE_LZ4),true) + echo "lz4_ramdisks=true" >> $@ +endif +ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) + echo "vendor_boot=true" >> $@ + echo "vendor_boot_size=$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)" >> $@ +endif +ifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),) + echo "vendor_kernel_boot=true" >> $@ + echo "vendor_kernel_boot_size=$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)" >> $@ +endif +ifeq ($(INSTALLED_RECOVERYIMAGE_TARGET),) + $(hide) echo "no_recovery=true" >> $@ +endif +ifdef BOARD_INCLUDE_RECOVERY_DTBO + $(hide) echo "include_recovery_dtbo=true" >> $@ +endif +ifdef BOARD_INCLUDE_RECOVERY_ACPIO + $(hide) echo "include_recovery_acpio=true" >> $@ +endif +ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE + $(hide) echo "recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)" >> $@ +endif +ifdef TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS + @# TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS can be empty to indicate that nothing but defaults should be used. + $(hide) echo "recovery_mount_options=$(TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)" >> $@ +else + $(hide) echo "recovery_mount_options=$(DEFAULT_TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)" >> $@ +endif + $(hide) echo "tool_extensions=$(tool_extensions)" >> $@ + $(hide) echo "default_system_dev_certificate=$(DEFAULT_SYSTEM_DEV_CERTIFICATE)" >> $@ +ifdef PRODUCT_EXTRA_OTA_KEYS + $(hide) echo "extra_ota_keys=$(PRODUCT_EXTRA_OTA_KEYS)" >> $@ +endif +ifdef PRODUCT_EXTRA_RECOVERY_KEYS + $(hide) echo "extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)" >> $@ +endif + $(hide) echo 'mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)' >> $@ + $(hide) echo 'recovery_mkbootimg_args=$(BOARD_RECOVERY_MKBOOTIMG_ARGS)' >> $@ + $(hide) echo 'mkbootimg_version_args=$(INTERNAL_MKBOOTIMG_VERSION_ARGS)' >> $@ + $(hide) echo 'mkbootimg_init_args=$(BOARD_MKBOOTIMG_INIT_ARGS)' >> $@ +ifdef BOARD_GKI_SIGNING_KEY_PATH + $(hide) echo 'gki_signing_key_path=$(BOARD_GKI_SIGNING_KEY_PATH)' >> $@ + $(hide) echo 'gki_signing_algorithm=$(BOARD_GKI_SIGNING_ALGORITHM)' >> $@ + $(hide) echo 'gki_signing_signature_args=$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)' >> $@ +endif + $(hide) echo "multistage_support=1" >> $@ + $(hide) echo "blockimgdiff_versions=3,4" >> $@ +ifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true) + $(hide) echo "build_generic_ota_package=true" >> $@ +endif +ifneq ($(OEM_THUMBPRINT_PROPERTIES),) + # OTA scripts are only interested in fingerprint related properties + $(hide) echo "oem_fingerprint_properties=$(OEM_THUMBPRINT_PROPERTIES)" >> $@ +endif +ifneq (,$(filter address, $(SANITIZE_TARGET))) + # We need to create userdata.img with real data because the instrumented libraries are in userdata.img. + $(hide) echo "userdata_img_with_data=true" >> $@ +endif +ifeq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true) + $(hide) echo "full_recovery_image=true" >> $@ +endif +ifdef BOARD_USES_VENDORIMAGE + $(hide) echo "board_uses_vendorimage=true" >> $@ +endif +ifeq ($(BOARD_AVB_ENABLE),true) +ifeq ($(BUILDING_VBMETA_IMAGE),true) + $(hide) echo "avb_building_vbmeta_image=true" >> $@ +endif # BUILDING_VBMETA_IMAGE + $(hide) echo "avb_enable=true" >> $@ + $(hide) echo "avb_vbmeta_key_path=$(BOARD_AVB_KEY_PATH)" >> $@ + $(hide) echo "avb_vbmeta_algorithm=$(BOARD_AVB_ALGORITHM)" >> $@ + $(hide) echo "avb_vbmeta_args=$(BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS)" >> $@ + $(hide) echo "avb_boot_add_hash_footer_args=$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_BOOT_KEY_PATH + $(hide) echo "avb_boot_key_path=$(BOARD_AVB_BOOT_KEY_PATH)" >> $@ + $(hide) echo "avb_boot_algorithm=$(BOARD_AVB_BOOT_ALGORITHM)" >> $@ + $(hide) echo "avb_boot_rollback_index_location=$(BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_BOOT_KEY_PATH + $(hide) echo "avb_init_boot_add_hash_footer_args=$(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_INIT_BOOT_KEY_PATH + $(hide) echo "avb_init_boot_key_path=$(BOARD_AVB_INIT_BOOT_KEY_PATH)" >> $@ + $(hide) echo "avb_init_boot_algorithm=$(BOARD_AVB_INIT_BOOT_ALGORITHM)" >> $@ + $(hide) echo "avb_init_boot_rollback_index_location=$(BOARD_AVB_INIT_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_INIT_BOOT_KEY_PATH + echo "avb_vendor_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_VENDOR_BOOT_KEY_PATH + echo "avb_vendor_boot_key_path=$(BOARD_AVB_VENDOR_BOOT_KEY_PATH)" >> $@ + echo "avb_vendor_boot_algorithm=$(BOARD_AVB_VENDOR_BOOT_ALGORITHM)" >> $@ + echo "avb_vendor_boot_rollback_index_location=$(BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_VENDOR_BOOT_KEY_PATH + echo "avb_vendor_kernel_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH + echo "avb_vendor_kernel_boot_key_path=$(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH)" >> $@ + echo "avb_vendor_kernel_boot_algorithm=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ALGORITHM)" >> $@ + echo "avb_vendor_kernel_boot_rollback_index_location=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH + $(hide) echo "avb_recovery_add_hash_footer_args=$(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_RECOVERY_KEY_PATH + $(hide) echo "avb_recovery_key_path=$(BOARD_AVB_RECOVERY_KEY_PATH)" >> $@ + $(hide) echo "avb_recovery_algorithm=$(BOARD_AVB_RECOVERY_ALGORITHM)" >> $@ + $(hide) echo "avb_recovery_rollback_index_location=$(BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_RECOVERY_KEY_PATH +ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST))) + $(hide) echo "avb_custom_images_partition_list=$(BOARD_CUSTOMIMAGES_PARTITION_LIST)" >> $@ + $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ + echo "avb_$(partition)_key_path=$(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH)" >> $@; \ + echo "avb_$(partition)_algorithm=$(BOARD_AVB_$(call to-upper,$(partition))_ALGORITHM)" >> $@; \ + echo "avb_$(partition)_add_hashtree_footer_args=$(BOARD_AVB_$(call to-upper,$(partition))_ADD_HASHTREE_FOOTER_ARGS)" >> $@; \ + echo "avb_$(partition)_rollback_index_location=$(BOARD_AVB_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)" >> $@; \ + echo "avb_$(partition)_partition_size=$(BOARD_AVB_$(call to-upper,$(partition))_PARTITION_SIZE)" >> $@; \ + echo "avb_$(partition)_image_list=$(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))" >> $@;) +endif # BOARD_CUSTOMIMAGES_PARTITION_LIST +ifdef TARGET_PREBUILT_RESOURCE + $(hide) echo "avb_resource_add_hash_footer_args=$(BOARD_AVB_RESOURCE_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_RESOURCE_KEY_PATH + $(hide) echo "avb_resource_key_path=$(BOARD_AVB_RESOURCE_KEY_PATH)" >> $@ + $(hide) echo "avb_resource_algorithm=$(BOARD_AVB_RESOURCE_ALGORITHM)" >> $@ + $(hide) echo "avb_resource_rollback_index_location=$(BOARD_AVB_RESOURCE_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_RESOURCE_KEY_PATH +endif # TARGET_PREBUILT_RESOURCE +ifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM))) + $(hide) echo "avb_vbmeta_system=$(BOARD_AVB_VBMETA_SYSTEM)" >> $@ + $(hide) echo "avb_vbmeta_system_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)" >> $@ + $(hide) echo "avb_vbmeta_system_key_path=$(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH)" >> $@ + $(hide) echo "avb_vbmeta_system_algorithm=$(BOARD_AVB_VBMETA_SYSTEM_ALGORITHM)" >> $@ + $(hide) echo "avb_vbmeta_system_rollback_index_location=$(BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_VBMETA_SYSTEM +ifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR))) + $(hide) echo "avb_vbmeta_vendor=$(BOARD_AVB_VBMETA_VENDOR)" >> $@ + $(hide) echo "avb_vbmeta_vendor_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)" >> $@ + $(hide) echo "avb_vbmeta_vendor_key_path=$(BOARD_AVB_VBMETA_VENDOR_KEY_PATH)" >> $@ + $(hide) echo "avb_vbmeta_vendor_algorithm=$(BOARD_AVB_VBMETA_VENDOR_ALGORITHM)" >> $@ + $(hide) echo "avb_vbmeta_vendor_rollback_index_location=$(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_VBMETA_VENDOR_KEY_PATH +endif # BOARD_AVB_ENABLE +ifdef BOARD_BPT_INPUT_FILES + $(hide) echo "board_bpt_enable=true" >> $@ + $(hide) echo "board_bpt_make_table_args=$(BOARD_BPT_MAKE_TABLE_ARGS)" >> $@ + $(hide) echo "board_bpt_input_files=$(BOARD_BPT_INPUT_FILES)" >> $@ +endif +ifdef BOARD_BPT_DISK_SIZE + $(hide) echo "board_bpt_disk_size=$(BOARD_BPT_DISK_SIZE)" >> $@ +endif + $(call generate-userimage-prop-dictionary, $@) +ifeq ($(AB_OTA_UPDATER),true) + @# Include the build type in META/misc_info.txt so the server can easily differentiate production builds. + $(hide) echo "build_type=$(TARGET_BUILD_VARIANT)" >> $@ + $(hide) echo "ab_update=true" >> $@ +endif +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) + $(hide) echo "allow_non_ab=true" >> $@ +endif +ifdef BOARD_PREBUILT_DTBOIMAGE + $(hide) echo "has_dtbo=true" >> $@ +ifeq ($(BOARD_AVB_ENABLE),true) + $(hide) echo "dtbo_size=$(BOARD_DTBOIMG_PARTITION_SIZE)" >> $@ + $(hide) echo "avb_dtbo_add_hash_footer_args=$(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_DTBO_KEY_PATH + $(hide) echo "avb_dtbo_key_path=$(BOARD_AVB_DTBO_KEY_PATH)" >> $@ + $(hide) echo "avb_dtbo_algorithm=$(BOARD_AVB_DTBO_ALGORITHM)" >> $@ + $(hide) echo "avb_dtbo_rollback_index_location=$(BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_DTBO_KEY_PATH +endif # BOARD_AVB_ENABLE +endif # BOARD_PREBUILT_DTBOIMAGE +ifeq ($(BOARD_USES_PVMFWIMAGE),true) + $(hide) echo "has_pvmfw=true" >> $@ +ifeq ($(BOARD_AVB_ENABLE),true) + $(hide) echo "pvmfw_size=$(BOARD_PVMFWIMAGE_PARTITION_SIZE)" >> $@ + $(hide) echo "avb_pvmfw_add_hash_footer_args=$(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)" >> $@ +ifdef BOARD_AVB_PVMFW_KEY_PATH + $(hide) echo "avb_pvmfw_key_path=$(BOARD_AVB_PVMFW_KEY_PATH)" >> $@ + $(hide) echo "avb_pvmfw_algorithm=$(BOARD_AVB_PVMFW_ALGORITHM)" >> $@ + $(hide) echo "avb_pvmfw_rollback_index_location=$(BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION)" >> $@ +endif # BOARD_AVB_PVMFW_KEY_PATH +endif # BOARD_AVB_ENABLE +endif # BOARD_USES_PVMFWIMAGE + $(call dump-dynamic-partitions-info,$@) + @# VINTF checks +ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true) + $(hide) echo "vintf_enforce=true" >> $@ +endif +ifdef ODM_MANIFEST_SKUS + $(hide) echo "vintf_odm_manifest_skus=$(ODM_MANIFEST_SKUS)" >> $@ +endif +ifdef ODM_MANIFEST_FILES + $(hide) echo "vintf_include_empty_odm_sku=true" >> $@ +endif +ifdef DEVICE_MANIFEST_SKUS + $(hide) echo "vintf_vendor_manifest_skus=$(DEVICE_MANIFEST_SKUS)" >> $@ +endif +ifdef DEVICE_MANIFEST_FILE + $(hide) echo "vintf_include_empty_vendor_sku=true" >> $@ +endif +ifeq ($(BOARD_BOOTLOADER_IN_UPDATE_PACKAGE),true) + $(hide) echo "bootloader_in_update_package=true" >> $@ +endif +ifeq ($(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE),true) + $(hide) echo "exclude_kernel_from_recovery_image=true" >> $@ +endif +ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),) + $(hide) echo "partial_ota_update_partitions_list=$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)" >> $@ +endif + $(hide) echo "booot_header_version=$(BOARD_BOOT_HEADER_VERSION)" >> $@ + +ifeq ($(BUILDING_WITH_VSDK),true) + $(hide) echo "building_with_vsdk=true" >> $@ +endif +ifeq ($(TARGET_FLATTEN_APEX),false) + $(hide) echo "target_flatten_apex=false" >> $@ +endif + +$(call declare-0p-target,$(INSTALLED_MISC_INFO_TARGET)) + +.PHONY: misc_info +misc_info: $(INSTALLED_MISC_INFO_TARGET) + +droidcore-unbundled: $(INSTALLED_MISC_INFO_TARGET) + +# ----------------------------------------------------------------- +# A zip of the directories that map to the target filesystem. +# This zip can be used to create an OTA package or filesystem image +# as a post-build step. +# +name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + name := $(name)_debug +endif +name := $(name)-target_files-$(FILE_NAME_TAG) + +intermediates := $(call intermediates-dir-for,PACKAGING,target_files) +BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip +$(BUILT_TARGET_FILES_PACKAGE): intermediates := $(intermediates) +$(BUILT_TARGET_FILES_PACKAGE): \ + zip_root := $(intermediates)/$(name) + +# $(1): Directory to copy +# $(2): Location to copy it to +# The "ls -A" is to prevent "acp s/* d" from failing if s is empty. +define package_files-copy-root + if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \ + mkdir -p $(2) && \ + $(ACP) -rd $(strip $(1))/* $(2); \ + fi +endef + +built_ota_tools := + +# We can't build static executables when SANITIZE_TARGET=address +ifeq (,$(filter address, $(SANITIZE_TARGET))) +built_ota_tools += \ + $(call intermediates-dir-for,EXECUTABLES,updater)/updater +endif + +$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools) + +tool_extension := $(wildcard $(tool_extensions)/releasetools.py) +$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_TOOL_EXTENSION := $(tool_extension) + +updaer_dep := +ifeq ($(AB_OTA_UPDATER),true) +updater_dep += system/update_engine/update_engine.conf +$(call declare-1p-target,system/update_engine/update_engine.conf,system/update_engine) +updater_dep += external/zucchini/version_info.h +$(call declare-license-metadata,external/zucchini/version_info.h,legacy_notice,notice,external/zucchini/LICENSE,external/zucchini) +updater_dep += $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so +endif + +# Build OTA tools if non-A/B is allowed +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) +updater_dep += $(built_ota_tools) +endif + +$(BUILT_TARGET_FILES_PACKAGE): $(updater_dep) + +# If we are using recovery as boot, output recovery files to BOOT/. +# If we are moving recovery resources to vendor_boot, output recovery files to VENDOR_BOOT/. +ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) +$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_OUT := BOOT +else ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) +$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_OUT := VENDOR_BOOT +else +$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_OUT := RECOVERY +endif + +ifeq ($(AB_OTA_UPDATER),true) + ifdef OSRELEASED_DIRECTORY + $(BUILT_TARGET_FILES_PACKAGE): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id + $(BUILT_TARGET_FILES_PACKAGE): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version + $(BUILT_TARGET_FILES_PACKAGE): $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version + endif + + # Not checking in board_config.mk, since AB_OTA_PARTITIONS may be updated in Android.mk (e.g. to + # additionally include radio or bootloader partitions). + ifeq ($(AB_OTA_PARTITIONS),) + $(error AB_OTA_PARTITIONS must be defined when using AB_OTA_UPDATER) + endif +endif + +ifneq ($(AB_OTA_PARTITIONS),) + ifneq ($(AB_OTA_UPDATER),true) + $(error AB_OTA_UPDATER must be true when defining AB_OTA_PARTITIONS) + endif +endif + +# Run fs_config while creating the target files package +# $1: root directory +# $2: add prefix +define fs_config +(cd $(1); find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,$(2),' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) -R "$(2)" +endef + +define filter-out-missing-vendor +$(if $(INSTALLED_VENDORIMAGE_TARGET),$(1),$(filter-out vendor,$(1))) +endef +define filter-out-missing-vendor_dlkm +$(if $(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(1),$(filter-out vendor_dlkm,$(1))) +endef +define filter-out-missing-odm +$(if $(INSTALLED_ODMIMAGE_TARGET),$(1),$(filter-out odm,$(1))) +endef +define filter-out-missing-odm_dlkm +$(if $(INSTALLED_ODM_DLKMIMAGE_TARGET),$(1),$(filter-out odm_dlkm,$(1))) +endef +define filter-out-missing-system_dlkm +$(if $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(1),$(filter-out system_dlkm,$(1))) +endef +# Filter out vendor,vendor_dlkm,odm,odm_dlkm,system_dlkm from the list for AOSP targets. +# $(1): list +define filter-out-missing-partitions +$(call filter-out-missing-vendor,\ + $(call filter-out-missing-vendor_dlkm,\ + $(call filter-out-missing-odm,\ + $(call filter-out-missing-odm_dlkm,\ + $(call filter-out-missing-system_dlkm,$(1)))))) +endef + +# Information related to dynamic partitions and virtual A/B. This information +# is needed for building the super image (see dump-super-image-info) and +# building OTA packages. +# $(1): file +define dump-dynamic-partitions-info + $(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)), \ + echo "use_dynamic_partitions=true" >> $(1)) + $(if $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)), \ + echo "dynamic_partition_retrofit=true" >> $(1)) + echo "lpmake=$(notdir $(LPMAKE))" >> $(1) + $(if $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION)), $(if $(BOARD_SUPER_PARTITION_SIZE), \ + echo "build_super_partition=true" >> $(1))) + $(if $(BUILDING_SUPER_EMPTY_IMAGE), \ + echo "build_super_empty_partition=true" >> $(1)) + $(if $(filter true,$(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE)), \ + echo "build_retrofit_dynamic_partitions_ota_package=true" >> $(1)) + echo "super_metadata_device=$(BOARD_SUPER_PARTITION_METADATA_DEVICE)" >> $(1) + $(if $(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \ + echo "super_block_devices=$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)" >> $(1)) + $(foreach device,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \ + echo "super_$(device)_device_size=$(BOARD_SUPER_PARTITION_$(call to-upper,$(device))_DEVICE_SIZE)" >> $(1);) + $(if $(BOARD_SUPER_PARTITION_PARTITION_LIST), \ + echo "dynamic_partition_list=$(call filter-out-missing-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST))" >> $(1)) + $(if $(BOARD_SUPER_PARTITION_GROUPS), + echo "super_partition_groups=$(BOARD_SUPER_PARTITION_GROUPS)" >> $(1)) + $(foreach group,$(BOARD_SUPER_PARTITION_GROUPS), \ + echo "super_$(group)_group_size=$(BOARD_$(call to-upper,$(group))_SIZE)" >> $(1); \ + $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \ + echo "super_$(group)_partition_list=$(call filter-out-missing-partitions,$(BOARD_$(call to-upper,$(group))_PARTITION_LIST))" >> $(1);)) + $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)), \ + echo "build_non_sparse_super_partition=true" >> $(1)) + $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)), \ + echo "build_non_sparse_super_partition=true" >> $(1)) + $(if $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)), \ + echo "super_image_in_update_package=true" >> $(1)) + $(if $(BOARD_SUPER_PARTITION_SIZE), \ + echo "super_partition_size=$(BOARD_SUPER_PARTITION_SIZE)" >> $(1)) + $(if $(BOARD_SUPER_PARTITION_ALIGNMENT), \ + echo "super_partition_alignment=$(BOARD_SUPER_PARTITION_ALIGNMENT)" >> $(1)) + $(if $(BOARD_SUPER_PARTITION_WARN_LIMIT), \ + echo "super_partition_warn_limit=$(BOARD_SUPER_PARTITION_WARN_LIMIT)" >> $(1)) + $(if $(BOARD_SUPER_PARTITION_ERROR_LIMIT), \ + echo "super_partition_error_limit=$(BOARD_SUPER_PARTITION_ERROR_LIMIT)" >> $(1)) + $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA)), \ + echo "virtual_ab=true" >> $(1)) + $(if $(filter true,$(PRODUCT_VIRTUAL_AB_COMPRESSION)), \ + echo "virtual_ab_compression=true" >> $(1)) +# This value controls the compression algorithm used for VABC +# valid options are defined in system/core/fs_mgr/libsnapshot/cow_writer.cpp +# e.g. "none", "gz", "brotli" + $(if $(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD), \ + echo "virtual_ab_compression_method=$(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD)" >> $(1)) + $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT)), \ + echo "virtual_ab_retrofit=true" >> $(1)) +endef + +# By conditionally including the dependency of the target files package on the +# full system image deps, we speed up builds that do not build the system +# image. +ifdef BUILDING_SYSTEM_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(FULL_SYSTEMIMAGE_DEPS) +else + # releasetools may need the system build.prop even when building a + # system-image-less product. + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BUILD_PROP_TARGET) +endif + +ifdef BUILDING_USERDATA_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_USERDATAIMAGE_FILES) +endif + +ifdef BUILDING_SYSTEM_OTHER_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_SYSTEMOTHERIMAGE_FILES) +endif + +ifdef BUILDING_VENDOR_BOOT_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_RAMDISK_FILES) + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) + # The vendor ramdisk may be built from the recovery ramdisk. + ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) + endif +endif + +ifdef BUILDING_RECOVERY_IMAGE + # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other + # BUILT_TARGET_FILES_PACKAGE dependencies because currently there're cp/rsync/rm + # commands in build-recoveryimage-target, which would touch the files under + # TARGET_RECOVERY_OUT and race with packaging target-files.zip. + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BOOTIMAGE_TARGET) + else + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_RECOVERYIMAGE_TARGET) + endif + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RECOVERYIMAGE_FILES) +endif + +# Conditionally depend on the image files if the image is being built so the +# target-files.zip rule doesn't wait on the image creation rule, or the image +# if it is coming from a prebuilt. + +ifdef BUILDING_VENDOR_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDORIMAGE_FILES) +else ifdef BOARD_PREBUILT_VENDORIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_VENDORIMAGE_TARGET) +endif + +ifdef BUILDING_PRODUCT_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_PRODUCTIMAGE_FILES) +else ifdef BOARD_PREBUILT_PRODUCTIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_PRODUCTIMAGE_TARGET) +endif + +ifdef BUILDING_SYSTEM_EXT_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_SYSTEM_EXTIMAGE_FILES) +else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) +endif + +ifneq (,$(BUILDING_BOOT_IMAGE)$(BUILDING_INIT_BOOT_IMAGE)) + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RAMDISK_FILES) +endif # BUILDING_BOOT_IMAGE != "" || BUILDING_INIT_BOOT_IMAGE != "" + +ifneq (,$(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES))) + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BOOTIMAGE_TARGET) +endif + +ifdef BUILDING_ODM_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_ODMIMAGE_FILES) +else ifdef BOARD_PREBUILT_ODMIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_ODMIMAGE_TARGET) +endif + +ifdef BUILDING_VENDOR_DLKM_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_DLKMIMAGE_FILES) +else ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) +endif + +ifdef BUILDING_ODM_DLKM_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_ODM_DLKMIMAGE_FILES) +else ifdef BOARD_PREBUILT_ODM_DLKMIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_ODM_DLKMIMAGE_TARGET) +endif + +ifdef BUILDING_SYSTEM_DLKM_IMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) +else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE + $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) +endif + +ifeq ($(BUILD_QEMU_IMAGES),true) + MK_VBMETA_BOOT_KERNEL_CMDLINE_SH := device/generic/goldfish/tools/mk_vbmeta_boot_params.sh + $(BUILT_TARGET_FILES_PACKAGE): $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) +endif + +ifdef BOARD_PREBUILT_BOOTLOADER +$(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BOOTLOADER_MODULE) +droidcore-unbundled: $(INSTALLED_BOOTLOADER_MODULE) +endif + +# Depending on the various images guarantees that the underlying +# directories are up-to-date. +$(BUILT_TARGET_FILES_PACKAGE): \ + $(INSTALLED_RADIOIMAGE_TARGET) \ + $(INSTALLED_RECOVERYIMAGE_TARGET) \ + $(INSTALLED_CACHEIMAGE_TARGET) \ + $(INSTALLED_DTBOIMAGE_TARGET) \ + $(INSTALLED_PVMFWIMAGE_TARGET) \ + $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) \ + $(INSTALLED_CUSTOMIMAGES_TARGET) \ + $(INSTALLED_RESOURCEIMAGE_TARGET) \ + $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ + $(INSTALLED_KERNEL_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_DTBIMAGE_TARGET) \ + $(INSTALLED_2NDBOOTLOADER_TARGET) \ + $(BOARD_PREBUILT_DTBOIMAGE) \ + $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \ + $(BOARD_RECOVERY_ACPIO) \ + $(PRODUCT_SYSTEM_BASE_FS_PATH) \ + $(PRODUCT_VENDOR_BASE_FS_PATH) \ + $(PRODUCT_PRODUCT_BASE_FS_PATH) \ + $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \ + $(PRODUCT_ODM_BASE_FS_PATH) \ + $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \ + $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \ + $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \ + $(LPMAKE) \ + $(SELINUX_FC) \ + $(INSTALLED_MISC_INFO_TARGET) \ + $(APKCERTS_FILE) \ + $(SOONG_APEX_KEYS_FILE) \ + $(SOONG_ZIP) \ + $(HOST_OUT_EXECUTABLES)/fs_config \ + $(ADD_IMG_TO_TARGET_FILES) \ + $(MAKE_RECOVERY_PATCH) \ + $(BUILT_KERNEL_CONFIGS_FILE) \ + $(BUILT_KERNEL_VERSION_FILE) \ + | $(ACP) + @echo "Package target files: $@" + $(hide) rm -rf $@ $@.list $(zip_root) + $(hide) mkdir -p $(dir $@) $(zip_root) +ifneq (,$(INSTALLED_RECOVERYIMAGE_TARGET)$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))$(filter true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))) + @# Components of the recovery image + $(hide) mkdir -p $(zip_root)/$(PRIVATE_RECOVERY_OUT) +# Exclude recovery files in the default vendor ramdisk if including a standalone +# recovery ramdisk in vendor_boot. +ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) + $(hide) $(call package_files-copy-root, \ + $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/$(PRIVATE_RECOVERY_OUT)/RAMDISK) +endif +ifdef INSTALLED_KERNEL_TARGET +ifneq (,$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))) + cp $(INSTALLED_KERNEL_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/ +else ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) + cp $(firstword $(INSTALLED_KERNEL_TARGET)) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/kernel +endif +endif +ifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT))) +ifdef INSTALLED_2NDBOOTLOADER_TARGET + cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/second +endif +ifdef BOARD_INCLUDE_RECOVERY_DTBO +ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE + cp $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_dtbo +else + cp $(BOARD_PREBUILT_DTBOIMAGE) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_dtbo +endif +endif # BOARD_INCLUDE_RECOVERY_DTBO +ifdef BOARD_INCLUDE_RECOVERY_ACPIO + cp $(BOARD_RECOVERY_ACPIO) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_acpio +endif +ifdef INSTALLED_DTBIMAGE_TARGET + cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/dtb +endif +ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) +ifdef INTERNAL_KERNEL_CMDLINE + echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/cmdline +endif # INTERNAL_KERNEL_CMDLINE != "" +endif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true +ifdef BOARD_KERNEL_BASE + echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/base +endif +ifdef BOARD_KERNEL_PAGESIZE + echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/pagesize +endif +endif # not (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT) +endif # INSTALLED_RECOVERYIMAGE_TARGET defined or BOARD_USES_RECOVERY_AS_BOOT is true + @# Components of the boot image + $(hide) mkdir -p $(zip_root)/BOOT + $(hide) mkdir -p $(zip_root)/ROOT + $(hide) $(call package_files-copy-root, \ + $(TARGET_ROOT_OUT),$(zip_root)/ROOT) + @# If we are using recovery as boot, this is already done when processing recovery. +ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true) +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + $(hide) $(call package_files-copy-root, \ + $(TARGET_RAMDISK_OUT),$(zip_root)/BOOT/RAMDISK) +endif +ifdef INSTALLED_KERNEL_TARGET + $(hide) cp $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/ +endif +ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) + echo "$(GENERIC_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline +else ifndef INSTALLED_VENDOR_BOOTIMAGE_TARGET # && BOARD_USES_GENERIC_KERNEL_IMAGE != true + echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline +ifdef INSTALLED_2NDBOOTLOADER_TARGET + cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second +endif +ifdef INSTALLED_DTBIMAGE_TARGET + cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/BOOT/dtb +endif +ifdef BOARD_KERNEL_BASE + echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base +endif +ifdef BOARD_KERNEL_PAGESIZE + echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/BOOT/pagesize +endif +endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET == "" && BOARD_USES_GENERIC_KERNEL_IMAGE != true +endif # BOARD_USES_RECOVERY_AS_BOOT not true + $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\ + mkdir -p $(zip_root)/RADIO; \ + cp $(t) $(zip_root)/RADIO/$(notdir $(t));) +ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET + mkdir -p $(zip_root)/VENDOR_BOOT + $(call package_files-copy-root, \ + $(TARGET_VENDOR_RAMDISK_OUT),$(zip_root)/VENDOR_BOOT/RAMDISK) +ifdef INSTALLED_DTBIMAGE_TARGET +ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true) + cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/VENDOR_BOOT/dtb +endif +endif # end of INSTALLED_DTBIMAGE_TARGET +ifdef INTERNAL_VENDOR_BOOTCONFIG_TARGET + cp $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) $(zip_root)/VENDOR_BOOT/vendor_bootconfig +endif +ifdef BOARD_KERNEL_BASE + echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/VENDOR_BOOT/base +endif +ifdef BOARD_KERNEL_PAGESIZE + echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/VENDOR_BOOT/pagesize +endif + echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/VENDOR_BOOT/vendor_cmdline +ifdef INTERNAL_VENDOR_RAMDISK_FRAGMENTS + echo "$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS)" > "$(zip_root)/VENDOR_BOOT/vendor_ramdisk_fragments" + $(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \ + mkdir -p $(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment); \ + echo "$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)" > "$(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/mkbootimg_args"; \ + $(eval prebuilt_ramdisk := $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)) \ + $(if $(prebuilt_ramdisk), \ + cp "$(prebuilt_ramdisk)" "$(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/prebuilt_ramdisk";, \ + $(call package_files-copy-root, \ + $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR), \ + $(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/RAMDISK); \ + )) +endif # INTERNAL_VENDOR_RAMDISK_FRAGMENTS != "" +endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET +ifdef INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET + mkdir -p $(zip_root)/VENDOR_KERNEL_BOOT + $(call package_files-copy-root, \ + $(TARGET_VENDOR_KERNEL_RAMDISK_OUT),$(zip_root)/VENDOR_KERNEL_BOOT/RAMDISK) +ifdef INSTALLED_DTBIMAGE_TARGET + cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/VENDOR_KERNEL_BOOT/dtb +endif +ifdef BOARD_KERNEL_PAGESIZE + echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/VENDOR_KERNEL_BOOT/pagesize +endif +endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET +ifdef BUILDING_SYSTEM_IMAGE + @# Contents of the system image + $(hide) $(call package_files-copy-root, \ + $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM) +else ifdef INSTALLED_BUILD_PROP_TARGET + @# Copy the system build.prop even if not building a system image + @# because add_img_to_target_files may need it to build other partition + @# images. + $(hide) mkdir -p "$(zip_root)/SYSTEM" + $(hide) cp "$(INSTALLED_BUILD_PROP_TARGET)" "$(patsubst $(TARGET_OUT)/%,$(zip_root)/SYSTEM/%,$(INSTALLED_BUILD_PROP_TARGET))" +endif +ifdef BUILDING_USERDATA_IMAGE + @# Contents of the data image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_DATA),$(zip_root)/DATA) +endif +ifdef BUILDING_VENDOR_IMAGE + @# Contents of the vendor image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_VENDOR),$(zip_root)/VENDOR) +endif +ifdef BUILDING_PRODUCT_IMAGE + @# Contents of the product image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_PRODUCT),$(zip_root)/PRODUCT) +endif +ifdef BUILDING_SYSTEM_EXT_IMAGE + @# Contents of the system_ext image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_SYSTEM_EXT),$(zip_root)/SYSTEM_EXT) +endif +ifdef BUILDING_ODM_IMAGE + @# Contents of the odm image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_ODM),$(zip_root)/ODM) +endif +ifdef BUILDING_VENDOR_DLKM_IMAGE + @# Contents of the vendor_dlkm image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_VENDOR_DLKM),$(zip_root)/VENDOR_DLKM) +endif +ifdef BUILDING_ODM_DLKM_IMAGE + @# Contents of the odm_dlkm image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_ODM_DLKM),$(zip_root)/ODM_DLKM) +endif +ifdef BUILDING_SYSTEM_DLKM_IMAGE + @# Contents of the system_dlkm image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_SYSTEM_DLKM),$(zip_root)/SYSTEM_DLKM) +endif +ifdef BUILDING_SYSTEM_OTHER_IMAGE + @# Contents of the system_other image + $(hide) $(call package_files-copy-root, \ + $(TARGET_OUT_SYSTEM_OTHER),$(zip_root)/SYSTEM_OTHER) +endif + @# Extra contents of the OTA package + $(hide) mkdir -p $(zip_root)/OTA + $(hide) cp $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/ +ifdef BUILDING_RAMDISK_IMAGE +ifeq (true,$(BOARD_IMG_USE_RAMDISK)) + @# Contents of the ramdisk image + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_RAMDISK_TARGET) $(zip_root)/IMAGES/ +endif +endif +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) +ifneq ($(built_ota_tools),) + $(hide) mkdir -p $(zip_root)/OTA/bin + $(hide) cp $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/ +endif +endif + @# Files that do not end up in any images, but are necessary to + @# build them. + $(hide) mkdir -p $(zip_root)/META + $(hide) cp $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt + $(hide) cp $(SOONG_APEX_KEYS_FILE) $(zip_root)/META/apexkeys.txt +ifneq ($(tool_extension),) + $(hide) cp $(PRIVATE_TOOL_EXTENSION) $(zip_root)/META/ +endif + $(hide) echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt + $(hide) cp $(SELINUX_FC) $(zip_root)/META/file_contexts.bin + $(hide) cp $(INSTALLED_MISC_INFO_TARGET) $(zip_root)/META/misc_info.txt +ifeq ($(INSTALLED_LOADER_TARGET),) + $(info No RK Loader for TARGET_DEVICE $(TARGET_DEVICE) to otapackage) +else + $(info cp RK Loader:$(TARGET_DEVICE) to otapackage) + $(hide) cp $(INSTALLED_LOADER_TARGET) $(zip_root)/RKLoader.bin +endif +ifeq ($(INSTALLED_UBOOT_TARGET),) + $(info No uboot for uboot/uboot.img to otapackage) +else +ifeq ($(AB_OTA_UPDATER),true) + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_UBOOT_TARGET) $(zip_root)/IMAGES/uboot.img +else + $(hide) cp $(INSTALLED_UBOOT_TARGET) $(zip_root)/uboot.img +endif +endif +ifeq ($(INSTALLED_TRUST_TARGET),) + $(info No trust for uboot/trust.img to otapackage) +else +ifeq ($(AB_OTA_UPDATER),true) + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_TRUST_TARGET) $(zip_root)/IMAGES/trust.img +else + $(hide) cp $(INSTALLED_TRUST_TARGET) $(zip_root)/trust.img +endif +endif +ifdef TARGET_PREBUILT_RESOURCE + $(info package add resource.img to BOOT and RECOVERY) + $(hide) cp $(TARGET_PREBUILT_RESOURCE) $(zip_root)/BOOT/resource.img +ifneq ($(TARGET_NO_RECOVERY),true) + $(hide) cp $(TARGET_PREBUILT_RESOURCE) $(zip_root)/RECOVERY/resource.img +endif +ifeq (1,$(strip $(shell expr $(BOARD_BOOT_HEADER_VERSION) \>= 3))) + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(TARGET_PREBUILT_RESOURCE) $(zip_root)/IMAGES/resource.img +endif +endif + +ifneq ($(PRODUCT_SYSTEM_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_SYSTEM_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_VENDOR_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_VENDOR_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_VENDOR_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_PRODUCT_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_PRODUCT_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_PRODUCT_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_SYSTEM_EXT_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_ODM_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_ODM_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_ODM_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_VENDOR_DLKM_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_ODM_DLKM_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_ODM_DLKM_BASE_FS_PATH)) +endif +ifneq ($(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH),) + $(hide) cp $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \ + $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH)) +endif +ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) +ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) + $(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \ + $(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root) +endif +endif +ifeq ($(AB_OTA_UPDATER),true) + @# When using the A/B updater, include the updater config files in the zip. + $(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt + $(hide) cp $(TOPDIR)external/zucchini/version_info.h $(zip_root)/META/zucchini_config.txt + $(hide) cp $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so $(zip_root)/META/liblz4.so + $(hide) for part in $(sort $(AB_OTA_PARTITIONS)); do \ + echo "$${part}" >> $(zip_root)/META/ab_partitions.txt; \ + done + $(hide) for conf in $(strip $(AB_OTA_POSTINSTALL_CONFIG)); do \ + echo "$${conf}" >> $(zip_root)/META/postinstall_config.txt; \ + done +ifdef OSRELEASED_DIRECTORY + $(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id $(zip_root)/META/product_id.txt + $(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version $(zip_root)/META/product_version.txt + $(hide) cp $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version $(zip_root)/META/system_version.txt +endif +endif +ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) + @# If breakpad symbols have been generated, add them to the zip. + $(hide) cp -R $(TARGET_OUT_BREAKPAD) $(zip_root)/BREAKPAD +endif +ifdef BOARD_PREBUILT_VENDORIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_VENDORIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_PRODUCTIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_PRODUCTIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE + $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES + $(hide) cp $(INSTALLED_INIT_BOOT_IMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ +endif + +ifndef BOARD_PREBUILT_BOOTIMAGE +ifneq (,$(strip $(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES)))) +ifdef INSTALLED_BOOTIMAGE_TARGET + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/ +endif # INSTALLED_BOOTIMAGE_TARGET +endif # INTERNAL_PREBUILT_BOOTIMAGE != "" || BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES == true +else # BOARD_PREBUILT_BOOTIMAGE is defined + $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES + $(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ +endif # BOARD_PREBUILT_BOOTIMAGE +ifdef BOARD_PREBUILT_ODMIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_ODMIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_ODM_DLKMIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/ +endif +ifdef BOARD_PREBUILT_DTBOIMAGE + $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES + $(hide) cp $(INSTALLED_DTBOIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ +endif # BOARD_PREBUILT_DTBOIMAGE +ifeq ($(BOARD_USES_PVMFWIMAGE),true) + $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES + $(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ + $(hide) cp $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) $(zip_root)/PREBUILT_IMAGES/ +endif +ifdef BOARD_PREBUILT_BOOTLOADER + $(hide) mkdir -p $(zip_root)/IMAGES + $(hide) cp $(INSTALLED_BOOTLOADER_MODULE) $(zip_root)/IMAGES/ +endif +ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),) + $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES + $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ + $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),cp $(image) $(zip_root)/PREBUILT_IMAGES/;)) +endif # BOARD_CUSTOMIMAGES_PARTITION_LIST +ifdef TARGET_PREBUILT_RESOURCE + $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES + $(hide) cp $(INSTALLED_RESOURCEIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ +endif # BOARD_RESOURCEIMAGES_PARTITION_LIST + @# The radio images in BOARD_PACK_RADIOIMAGES will be additionally copied from RADIO/ into + @# IMAGES/, which then will be added into -img.zip. Such images must be listed in + @# INSTALLED_RADIOIMAGE_TARGET. + $(hide) $(foreach part,$(BOARD_PACK_RADIOIMAGES), \ + echo $(part) >> $(zip_root)/META/pack_radioimages.txt;) + @# Run fs_config on all the system, vendor, boot ramdisk, + @# and recovery ramdisk files in the zip, and save the output +ifdef BUILDING_SYSTEM_IMAGE + $(hide) $(call fs_config,$(zip_root)/SYSTEM,system/) > $(zip_root)/META/filesystem_config.txt +endif +ifdef BUILDING_VENDOR_IMAGE + $(hide) $(call fs_config,$(zip_root)/VENDOR,vendor/) > $(zip_root)/META/vendor_filesystem_config.txt +endif +ifdef BUILDING_PRODUCT_IMAGE + $(hide) $(call fs_config,$(zip_root)/PRODUCT,product/) > $(zip_root)/META/product_filesystem_config.txt +endif +ifdef BUILDING_SYSTEM_EXT_IMAGE + $(hide) $(call fs_config,$(zip_root)/SYSTEM_EXT,system_ext/) > $(zip_root)/META/system_ext_filesystem_config.txt +endif +ifdef BUILDING_ODM_IMAGE + $(hide) $(call fs_config,$(zip_root)/ODM,odm/) > $(zip_root)/META/odm_filesystem_config.txt +endif +ifdef BUILDING_VENDOR_DLKM_IMAGE + $(hide) $(call fs_config,$(zip_root)/VENDOR_DLKM,vendor_dlkm/) > $(zip_root)/META/vendor_dlkm_filesystem_config.txt +endif +ifdef BUILDING_ODM_DLKM_IMAGE + $(hide) $(call fs_config,$(zip_root)/ODM_DLKM,odm_dlkm/) > $(zip_root)/META/odm_dlkm_filesystem_config.txt +endif +ifdef BUILDING_SYSTEM_DLKM_IMAGE + $(hide) $(call fs_config,$(zip_root)/SYSTEM_DLKM,system_dlkm/) > $(zip_root)/META/system_dlkm_filesystem_config.txt +endif + @# ROOT always contains the files for the root under normal boot. + $(hide) $(call fs_config,$(zip_root)/ROOT,) > $(zip_root)/META/root_filesystem_config.txt +ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + @# BOOT/RAMDISK exists and contains the ramdisk for recovery if using BOARD_USES_RECOVERY_AS_BOOT. + $(hide) $(call fs_config,$(zip_root)/BOOT/RAMDISK,) > $(zip_root)/META/boot_filesystem_config.txt +endif +ifdef BUILDING_INIT_BOOT_IMAGE + $(hide) $(call package_files-copy-root, $(TARGET_RAMDISK_OUT),$(zip_root)/INIT_BOOT/RAMDISK) + $(hide) $(call fs_config,$(zip_root)/INIT_BOOT/RAMDISK,) > $(zip_root)/META/init_boot_filesystem_config.txt +ifdef BOARD_KERNEL_PAGESIZE + $(hide) echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/INIT_BOOT/pagesize +endif # BOARD_KERNEL_PAGESIZE +endif # BUILDING_INIT_BOOT_IMAGE +ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) + $(call fs_config,$(zip_root)/VENDOR_BOOT/RAMDISK,) > $(zip_root)/META/vendor_boot_filesystem_config.txt +endif +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + @# BOOT/RAMDISK also exists and contains the first stage ramdisk if not using BOARD_BUILD_SYSTEM_ROOT_IMAGE. + $(hide) $(call fs_config,$(zip_root)/BOOT/RAMDISK,) > $(zip_root)/META/boot_filesystem_config.txt +endif +ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) + $(hide) $(call fs_config,$(zip_root)/RECOVERY/RAMDISK,) > $(zip_root)/META/recovery_filesystem_config.txt +endif +ifdef BUILDING_SYSTEM_OTHER_IMAGE + $(hide) $(call fs_config,$(zip_root)/SYSTEM_OTHER,system/) > $(zip_root)/META/system_other_filesystem_config.txt +endif + @# Metadata for compatibility verification. +ifdef BUILT_KERNEL_CONFIGS_FILE + $(hide) cp $(BUILT_KERNEL_CONFIGS_FILE) $(zip_root)/META/kernel_configs.txt +endif +ifdef BUILT_KERNEL_VERSION_FILE + $(hide) cp $(BUILT_KERNEL_VERSION_FILE) $(zip_root)/META/kernel_version.txt +endif + rm -rf $(zip_root)/META/dynamic_partitions_info.txt +ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) + $(call dump-dynamic-partitions-info, $(zip_root)/META/dynamic_partitions_info.txt) +endif + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \ + $(ADD_IMG_TO_TARGET_FILES) -a -v -p $(HOST_OUT) $(zip_root) +ifeq ($(BUILD_QEMU_IMAGES),true) + $(hide) AVBTOOL=$(AVBTOOL) $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(zip_root)/IMAGES/vbmeta.img \ + $(zip_root)/IMAGES/system.img $(zip_root)/IMAGES/VerifiedBootParams.textproto +endif + @# Zip everything up, preserving symlinks and placing META/ files first to + @# help early validation of the .zip file while uploading it. + $(hide) find $(zip_root)/META | sort >$@.list + $(hide) find $(zip_root) -path $(zip_root)/META -prune -o -print | sort >>$@.list + $(hide) $(SOONG_ZIP) -d -o $@ -C $(zip_root) -r $@.list + +.PHONY: target-files-package +target-files-package: $(BUILT_TARGET_FILES_PACKAGE) + +$(call declare-1p-container,$(BUILT_TARGET_FILES_PACKAGE),) +$(call declare-container-license-deps,$(BUILT_TARGET_FILES_PACKAGE), $(INSTALLED_RADIOIMAGE_TARGET) \ + $(INSTALLED_RECOVERYIMAGE_TARGET) \ + $(INSTALLED_CACHEIMAGE_TARGET) \ + $(INSTALLED_DTBOIMAGE_TARGET) \ + $(INSTALLED_PVMFWIMAGE_TARGET) \ + $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) \ + $(INSTALLED_CUSTOMIMAGES_TARGET) \ + $(INSTALLED_RESOURCEIMAGE_TARGET) \ + $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ + $(INSTALLED_KERNEL_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_DTBIMAGE_TARGET) \ + $(INSTALLED_2NDBOOTLOADER_TARGET) \ + $(BOARD_PREBUILT_DTBOIMAGE) \ + $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \ + $(BOARD_RECOVERY_ACPIO) \ + $(PRODUCT_SYSTEM_BASE_FS_PATH) \ + $(PRODUCT_VENDOR_BASE_FS_PATH) \ + $(PRODUCT_PRODUCT_BASE_FS_PATH) \ + $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \ + $(PRODUCT_ODM_BASE_FS_PATH) \ + $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \ + $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \ + $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \ + $(LPMAKE) \ + $(SELINUX_FC) \ + $(INSTALLED_MISC_INFO_TARGET) \ + $(APKCERTS_FILE) \ + $(SOONG_APEX_KEYS_FILE) \ + $(HOST_OUT_EXECUTABLES)/fs_config \ + $(ADD_IMG_TO_TARGET_FILES) \ + $(MAKE_RECOVERY_PATCH) \ + $(BUILT_KERNEL_CONFIGS_FILE) \ + $(BUILT_KERNEL_VERSION_FILE),$(BUILT_TARGET_FILES_PACKAGE):) + +$(call dist-for-goals, target-files-package, $(BUILT_TARGET_FILES_PACKAGE)) + +# ----------------------------------------------------------------- +# NDK Sysroot Package +NDK_SYSROOT_TARGET := $(PRODUCT_OUT)/ndk_sysroot.tar.bz2 +$(NDK_SYSROOT_TARGET): $(SOONG_OUT_DIR)/ndk.timestamp + @echo Package NDK sysroot... + $(hide) tar cjf $@ -C $(SOONG_OUT_DIR) ndk + +ifeq ($(HOST_OS),linux) +$(call dist-for-goals,sdk,$(NDK_SYSROOT_TARGET)) +endif + +ifeq ($(build_ota_package),true) +# ----------------------------------------------------------------- +# OTA update package + +# $(1): output file +# $(2): additional args +define build-ota-package-target +PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \ + $(OTA_FROM_TARGET_FILES) \ + --verbose \ + --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \ + --path $(HOST_OUT) \ + $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \ + $(2) \ + $(BUILT_TARGET_FILES_PACKAGE) $(1) +endef + +product_name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + product_name := $(product_name)_debug +endif +name := $(product_name)-ota-$(FILE_NAME_TAG) + +INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip +INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata + +$(call declare-0p-target,$(INTERNAL_OTA_METADATA)) + +$(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) +$(INTERNAL_OTA_PACKAGE_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_OTA_METADATA) +$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES) + @echo "Package OTA: $@" + $(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --output_metadata_path $(INTERNAL_OTA_METADATA)) + +$(call declare-1p-container,$(INTERNAL_OTA_PACKAGE_TARGET),) +$(call declare-container-license-deps,$(INTERNAL_OTA_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/) + +.PHONY: otapackage +otapackage: $(INTERNAL_OTA_PACKAGE_TARGET) + +ifeq ($(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE),true) +name := $(product_name)-ota-retrofit-$(FILE_NAME_TAG) + +INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip +$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) +$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): \ + $(BUILT_TARGET_FILES_PACKAGE) \ + $(OTA_FROM_TARGET_FILES) \ + $(INTERNAL_OTATOOLS_FILES) + @echo "Package OTA (retrofit dynamic partitions): $@" + $(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --retrofit_dynamic_partitions) + +$(call declare-1p-container,$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET),) +$(call declare-container-license-deps,$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/) + +.PHONY: otardppackage + +otapackage otardppackage: $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) + +endif # BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE + +ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),) +name := $(product_name)-partial-ota-$(FILE_NAME_TAG) + +INTERNAL_OTA_PARTIAL_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip +$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) +$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES) + @echo "Package partial OTA: $@" + $(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --partial "$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)") + +$(call declare-1p-container,$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET),) +$(call declare-container-license-deps,$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/) + + +.PHONY: partialotapackage +partialotapackage: $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) + +endif # BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST + +endif # build_ota_package + +# ----------------------------------------------------------------- +# A zip of the appcompat directory containing logs +APPCOMPAT_ZIP := $(PRODUCT_OUT)/appcompat.zip +# For apps_only build we'll establish the dependency later in build/make/core/main.mk. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(APPCOMPAT_ZIP): $(FULL_SYSTEMIMAGE_DEPS) \ + $(INTERNAL_RAMDISK_FILES) \ + $(INTERNAL_USERDATAIMAGE_FILES) \ + $(INTERNAL_VENDORIMAGE_FILES) \ + $(INTERNAL_PRODUCTIMAGE_FILES) \ + $(INTERNAL_SYSTEM_EXTIMAGE_FILES) +endif +$(APPCOMPAT_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,appcompat)/filelist +$(APPCOMPAT_ZIP): $(SOONG_ZIP) + @echo "appcompat logs: $@" + $(hide) rm -rf $@ $(PRIVATE_LIST_FILE) + $(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/appcompat $(dir $(PRIVATE_LIST_FILE)) + $(hide) find $(PRODUCT_OUT)/appcompat | sort >$(PRIVATE_LIST_FILE) + $(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/appcompat -l $(PRIVATE_LIST_FILE) + +# The mac build doesn't build dex2oat, so create the zip file only if the build OS is linux. +ifeq ($(BUILD_OS),linux) +ifneq ($(DEX2OAT),) +dexpreopt_tools_deps := $(DEXPREOPT_GEN_DEPS) $(DEXPREOPT_GEN) $(AAPT2) +dexpreopt_tools_deps += $(HOST_OUT_EXECUTABLES)/dexdump +dexpreopt_tools_deps += $(HOST_OUT_EXECUTABLES)/oatdump +DEXPREOPT_TOOLS_ZIP := $(PRODUCT_OUT)/dexpreopt_tools.zip +$(DEXPREOPT_TOOLS_ZIP): $(dexpreopt_tools_deps) +$(DEXPREOPT_TOOLS_ZIP): PRIVATE_DEXPREOPT_TOOLS_DEPS := $(dexpreopt_tools_deps) +$(DEXPREOPT_TOOLS_ZIP): $(SOONG_ZIP) + $(hide) mkdir -p $(dir $@) + $(hide) $(SOONG_ZIP) -d -o $@ -j $(addprefix -f ,$(PRIVATE_DEXPREOPT_TOOLS_DEPS)) -f $$(realpath $(DEX2OAT)) +$(call declare-1p-target,$(DEXPREOPT_TOOLS_ZIP),) +endif # DEX2OAT is set +endif # BUILD_OS == linux + +DEXPREOPT_CONFIG_ZIP := $(PRODUCT_OUT)/dexpreopt_config.zip + +$(DEXPREOPT_CONFIG_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_ODMIMAGE_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(DEXPREOPT_CONFIG_ZIP): $(DEX_PREOPT_CONFIG_FOR_MAKE) \ + $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) \ + +endif + +$(DEXPREOPT_CONFIG_ZIP): $(SOONG_ZIP) + $(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/dexpreopt_config + +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +ifneq (,$(DEX_PREOPT_CONFIG_FOR_MAKE)) + $(hide) cp $(DEX_PREOPT_CONFIG_FOR_MAKE) $(PRODUCT_OUT)/dexpreopt_config +endif +ifneq (,$(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)) + $(hide) cp $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(PRODUCT_OUT)/dexpreopt_config +endif +endif #!TARGET_BUILD_UNBUNDLED + $(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/dexpreopt_config -D $(PRODUCT_OUT)/dexpreopt_config + +.PHONY: dexpreopt_config_zip +dexpreopt_config_zip: $(DEXPREOPT_CONFIG_ZIP) + +$(call declare-1p-target,$(DEXPREOPT_CONFIG_ZIP),) + +# ----------------------------------------------------------------- +# A zip of the symbols directory. Keep the full paths to make it +# more obvious where these files came from. +# Also produces a textproto containing mappings from elf IDs to symbols +# filename, which will allow finding the appropriate symbols to deobfuscate +# a stack trace frame. +# + +name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + name := $(name)_debug +endif + +# The path to the zip file containing binaries with symbols. +SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name)-symbols-$(FILE_NAME_TAG).zip +# The path to a file containing mappings from elf IDs to filenames. +SYMBOLS_MAPPING := $(PRODUCT_OUT)/$(name)-symbols-mapping-$(FILE_NAME_TAG).textproto +.KATI_READONLY := SYMBOLS_ZIP SYMBOLS_MAPPING +# For apps_only build we'll establish the dependency later in build/make/core/main.mk. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(SYMBOLS_ZIP): $(INTERNAL_ALLIMAGES_FILES) $(updater_dep) +endif +$(SYMBOLS_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,symbols)/filelist +$(SYMBOLS_ZIP): PRIVATE_MAPPING_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,elf_symbol_mapping) +$(SYMBOLS_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP) + @echo "Package symbols: $@" + $(hide) rm -rf $@ $(PRIVATE_LIST_FILE) + $(hide) mkdir -p $(TARGET_OUT_UNSTRIPPED) $(dir $(PRIVATE_LIST_FILE)) $(PRIVATE_MAPPING_PACKAGING_DIR) + # Find all of the files in the symbols directory and zip them into the symbols zip. + $(hide) find -L $(TARGET_OUT_UNSTRIPPED) -type f | sort >$(PRIVATE_LIST_FILE) + $(hide) $(SOONG_ZIP) --ignore_missing_files -d -o $@ -C $(OUT_DIR)/.. -l $(PRIVATE_LIST_FILE) + # Find all of the files in the symbols mapping directory and merge them into the symbols mapping textproto. + $(hide) find -L $(PRIVATE_MAPPING_PACKAGING_DIR) -type f | sort >$(PRIVATE_LIST_FILE) + $(hide) $(SYMBOLS_MAP) -merge $(SYMBOLS_MAPPING) -ignore_missing_files @$(PRIVATE_LIST_FILE) +$(SYMBOLS_ZIP): .KATI_IMPLICIT_OUTPUTS := $(SYMBOLS_MAPPING) + +$(call declare-1p-container,$(SYMBOLS_ZIP),) +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(call declare-container-license-deps,$(SYMBOLS_ZIP),$(INTERNAL_ALLIMAGES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/) +endif + +# ----------------------------------------------------------------- +# A zip of the coverage directory. +# +name := gcov-report-files-all +ifeq ($(TARGET_BUILD_TYPE),debug) +name := $(name)_debug +endif +COVERAGE_ZIP := $(PRODUCT_OUT)/$(name).zip +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(COVERAGE_ZIP): $(INTERNAL_ALLIMAGES_FILES) +endif +$(COVERAGE_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,coverage)/filelist +$(COVERAGE_ZIP): $(SOONG_ZIP) + @echo "Package coverage: $@" + $(hide) rm -rf $@ $(PRIVATE_LIST_FILE) + $(hide) mkdir -p $(dir $@) $(TARGET_OUT_COVERAGE) $(dir $(PRIVATE_LIST_FILE)) + $(hide) find $(TARGET_OUT_COVERAGE) | sort >$(PRIVATE_LIST_FILE) + $(hide) $(SOONG_ZIP) -d -o $@ -C $(TARGET_OUT_COVERAGE) -l $(PRIVATE_LIST_FILE) + +$(call declare-1p-container,$(COVERAGE_ZIP),) +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(call declare-container-license-deps,$(COVERAGE_ZIP),$(INTERNAL_ALLIMAGE_FILES),$(PRODUCT_OUT)/:/) +endif + +SYSTEM_NOTICE_DEPS += $(COVERAGE_ZIP) + +#------------------------------------------------------------------ +# Export the LLVM profile data tool and dependencies for Clang coverage processing +# +ifeq (true,$(CLANG_COVERAGE)) + LLVM_PROFDATA := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-profdata + LLVM_COV := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-cov + LIBCXX := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib64/libc++.so.1 + # Use llvm-profdata.zip for backwards compatibility with tradefed code. + LLVM_COVERAGE_TOOLS_ZIP := $(PRODUCT_OUT)/llvm-profdata.zip + + $(LLVM_COVERAGE_TOOLS_ZIP): $(SOONG_ZIP) + $(hide) $(SOONG_ZIP) -d -o $@ -C $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION) -f $(LLVM_PROFDATA) -f $(LIBCXX) -f $(LLVM_COV) + + $(call dist-for-goals,droidcore-unbundled apps_only,$(LLVM_COVERAGE_TOOLS_ZIP)) +endif + +# ----------------------------------------------------------------- +# A zip of the Android Apps. Not keeping full path so that we don't +# include product names when distributing +# +name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + name := $(name)_debug +endif +name := $(name)-apps-$(FILE_NAME_TAG) + +APPS_ZIP := $(PRODUCT_OUT)/$(name).zip +$(APPS_ZIP): $(FULL_SYSTEMIMAGE_DEPS) + @echo "Package apps: $@" + $(hide) rm -rf $@ + $(hide) mkdir -p $(dir $@) + $(hide) apps_to_zip=`find $(TARGET_OUT_APPS) $(TARGET_OUT_APPS_PRIVILEGED) -mindepth 2 -maxdepth 3 -name "*.apk"`; \ + if [ -z "$$apps_to_zip" ]; then \ + echo "No apps to zip up. Generating empty apps archive." ; \ + a=$$(mktemp /tmp/XXXXXXX) && touch $$a && zip $@ $$a && zip -d $@ $$a; \ + else \ + zip -qjX $@ $$apps_to_zip; \ + fi + +ifeq (true,$(EMMA_INSTRUMENT)) +#------------------------------------------------------------------ +# An archive of classes for use in generating code-coverage reports +# These are the uninstrumented versions of any classes that were +# to be instrumented. +# Any dependencies are set up later in build/make/core/main.mk. + +JACOCO_REPORT_CLASSES_ALL := $(PRODUCT_OUT)/jacoco-report-classes-all.jar +$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_TARGET_JACOCO_DIR := $(call intermediates-dir-for,PACKAGING,jacoco) +$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_HOST_JACOCO_DIR := $(call intermediates-dir-for,PACKAGING,jacoco,HOST) +$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_TARGET_PROGUARD_USAGE_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage) +$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_HOST_PROGUARD_USAGE_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage,HOST) +$(JACOCO_REPORT_CLASSES_ALL) : + @echo "Collecting uninstrumented classes" + mkdir -p $(PRIVATE_TARGET_JACOCO_DIR) $(PRIVATE_HOST_JACOCO_DIR) $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) $(PRIVATE_HOST_PROGUARD_USAGE_DIR) + $(SOONG_ZIP) -o $@ -L 0 \ + -C $(PRIVATE_TARGET_JACOCO_DIR) -P out/target/common/obj -D $(PRIVATE_TARGET_JACOCO_DIR) \ + -C $(PRIVATE_HOST_JACOCO_DIR) -P out/target/common/obj -D $(PRIVATE_HOST_JACOCO_DIR) \ + -C $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) -P out/target/common/obj -D $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) \ + -C $(PRIVATE_HOST_PROGUARD_USAGE_DIR) -P out/target/common/obj -D $(PRIVATE_HOST_PROGUARD_USAGE_DIR) + +ifeq (,$(TARGET_BUILD_UNBUNDLED)) + $(JACOCO_REPORT_CLASSES_ALL): $(INTERNAL_ALLIMAGES_FILES) +endif +endif # EMMA_INSTRUMENT=true + + +#------------------------------------------------------------------ +# A zip of Proguard obfuscation dictionary files. +# Also produces a textproto containing mappings from the hashes of the +# dictionary contents (which are also stored in the dex files on the +# devices) to the filename of the proguard dictionary, which will allow +# finding the appropriate dictionary to deobfuscate a stack trace frame. +# + +# The path to the zip file containing proguard dictionaries. +PROGUARD_DICT_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-$(FILE_NAME_TAG).zip +# The path to the zip file containing mappings from dictionary hashes to filenames. +PROGUARD_DICT_MAPPING := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-mapping-$(FILE_NAME_TAG).textproto +.KATI_READONLY := PROGUARD_DICT_ZIP PROGUARD_DICT_MAPPING +# For apps_only build we'll establish the dependency later in build/make/core/main.mk. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(PROGUARD_DICT_ZIP): $(INTERNAL_ALLIMAGES_FILES) $(updater_dep) +endif +$(PROGUARD_DICT_ZIP): PRIVATE_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,proguard_dictionary) +$(PROGUARD_DICT_ZIP): PRIVATE_MAPPING_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,proguard_dictionary_mapping) +$(PROGUARD_DICT_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,proguard_dictionary_filelist)/filelist +$(PROGUARD_DICT_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP) + @echo "Packaging Proguard obfuscation dictionary files." + rm -rf $@ $(PRIVATE_LIST_FILE) + mkdir -p $(PRIVATE_PACKAGING_DIR) $(PRIVATE_MAPPING_PACKAGING_DIR) $(dir $(PRIVATE_LIST_FILE)) + # Zip all of the files in the proguard dictionary directory. + $(SOONG_ZIP) --ignore_missing_files -d -o $@ -C $(PRIVATE_PACKAGING_DIR) -D $(PRIVATE_PACKAGING_DIR) + # Find all of the files in the proguard dictionary mapping directory and merge them into the mapping textproto. + # Strip the PRIVATE_PACKAGING_DIR off the filenames to match soong_zip's -C argument. + $(hide) find -L $(PRIVATE_MAPPING_PACKAGING_DIR) -type f | sort >$(PRIVATE_LIST_FILE) + $(SYMBOLS_MAP) -merge $(PROGUARD_DICT_MAPPING) -strip_prefix $(PRIVATE_PACKAGING_DIR)/ -ignore_missing_files @$(PRIVATE_LIST_FILE) +$(PROGUARD_DICT_ZIP): .KATI_IMPLICIT_OUTPUTS := $(PROGUARD_DICT_MAPPING) + +$(call declare-1p-container,$(PROGUARD_DICT_ZIP),) +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(INTERNAL_ALLIMAGES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/) +endif + +#------------------------------------------------------------------ +# A zip of Proguard usage files. +# +PROGUARD_USAGE_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-usage-$(FILE_NAME_TAG).zip +# For apps_only build we'll establish the dependency later in build/make/core/main.mk. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(PROGUARD_USAGE_ZIP): \ + $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ + $(INSTALLED_USERDATAIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ + $(INSTALLED_ODMIMAGE_TARGET) \ + $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ + $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ + $(updater_dep) +endif +$(PROGUARD_USAGE_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,proguard_usage.zip)/filelist +$(PROGUARD_USAGE_ZIP): PRIVATE_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage) +$(PROGUARD_USAGE_ZIP): $(MERGE_ZIPS) + @echo "Packaging Proguard usage files." + mkdir -p $(dir $@) $(PRIVATE_PACKAGING_DIR) $(dir $(PRIVATE_LIST_FILE)) + find $(PRIVATE_PACKAGING_DIR) -name proguard_usage.zip > $(PRIVATE_LIST_FILE) + $(MERGE_ZIPS) $@ @$(PRIVATE_LIST_FILE) + +$(call declare-1p-container,$(PROGUARD_USAGE_ZIP),) +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +$(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ + $(INSTALLED_USERDATAIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ + $(INSTALLED_ODMIMAGE_TARGET) \ + $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ + $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ + $(updater_dep),$(PROGUARD_USAGE_ZIP):/) +endif + +ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) + +# Dump variables used by build_super_image.py (for building super.img and super_empty.img). +# $(1): output file +define dump-super-image-info + $(call dump-dynamic-partitions-info,$(1)) + $(if $(filter true,$(AB_OTA_UPDATER)), \ + echo "ab_update=true" >> $(1)) +endef + +endif # PRODUCT_USE_DYNAMIC_PARTITIONS + +# ----------------------------------------------------------------- +# super partition image (dist) + +ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION)) + +# BOARD_SUPER_PARTITION_SIZE must be defined to build super image. +ifneq ($(BOARD_SUPER_PARTITION_SIZE),) + +ifneq (true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)) + +# For real devices and for dist builds, build super image from target files to an intermediate directory. +INTERNAL_SUPERIMAGE_DIST_TARGET := $(call intermediates-dir-for,PACKAGING,super.img)/super.img +$(INTERNAL_SUPERIMAGE_DIST_TARGET): extracted_input_target_files := $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) +$(INTERNAL_SUPERIMAGE_DIST_TARGET): $(LPMAKE) $(BUILT_TARGET_FILES_PACKAGE) $(BUILD_SUPER_IMAGE) + $(call pretty,"Target super fs image from target files: $@") + PATH=$(dir $(LPMAKE)):$$PATH \ + $(BUILD_SUPER_IMAGE) -v $(extracted_input_target_files) $@ + +# Skip packing it in dist package because it is in update package. +ifneq (true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)) +$(call dist-for-goals,dist_files,$(INTERNAL_SUPERIMAGE_DIST_TARGET)) +endif + +.PHONY: superimage_dist +superimage_dist: $(INTERNAL_SUPERIMAGE_DIST_TARGET) + +endif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS != "true" +endif # BOARD_SUPER_PARTITION_SIZE != "" +endif # PRODUCT_BUILD_SUPER_PARTITION == "true" + +# ----------------------------------------------------------------- +# super partition image for development + +ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION)) +ifneq ($(BOARD_SUPER_PARTITION_SIZE),) +ifneq (true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)) + +# Build super.img by using $(INSTALLED_*IMAGE_TARGET) to $(1) +# $(1): built image path +# $(2): misc_info.txt path; its contents should match expectation of build_super_image.py +define build-superimage-target + mkdir -p $(dir $(2)) + rm -rf $(2) + $(call dump-super-image-info,$(2)) + $(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \ + echo "$(p)_image=$(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)" >> $(2);) + $(if $(BUILDING_SYSTEM_OTHER_IMAGE), $(if $(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)), \ + echo "system_other_image=$(INSTALLED_SYSTEMOTHERIMAGE_TARGET)" >> $(2);)) + mkdir -p $(dir $(1)) + PATH=$(dir $(LPMAKE)):$$PATH \ + $(BUILD_SUPER_IMAGE) -v $(2) $(1) +endef + +INSTALLED_SUPERIMAGE_TARGET := $(PRODUCT_OUT)/super.img +INSTALLED_SUPERIMAGE_DEPENDENCIES := $(LPMAKE) $(BUILD_SUPER_IMAGE) \ + $(foreach p, $(BOARD_SUPER_PARTITION_PARTITION_LIST), $(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)) + +ifdef BUILDING_SYSTEM_OTHER_IMAGE +ifneq ($(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)),) +INSTALLED_SUPERIMAGE_DEPENDENCIES += $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) +endif +endif + +# If BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT is set, super.img is built from images in the +# $(PRODUCT_OUT) directory, and is built to $(PRODUCT_OUT)/super.img. Also, it will +# be built for non-dist builds. This is useful for devices that uses super.img directly, e.g. +# virtual devices. +ifeq (true,$(BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT)) +$(INSTALLED_SUPERIMAGE_TARGET): $(INSTALLED_SUPERIMAGE_DEPENDENCIES) + $(call pretty,"Target super fs image for debug: $@") + $(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\ + $(call intermediates-dir-for,PACKAGING,superimage_debug)/misc_info.txt) + +droidcore-unbundled: $(INSTALLED_SUPERIMAGE_TARGET) + +# For devices that uses super image directly, the superimage target points to the file in $(PRODUCT_OUT). +.PHONY: superimage +superimage: $(INSTALLED_SUPERIMAGE_TARGET) + +$(call dist-for-goals,dist_files,$(INSTALLED_MISC_INFO_TARGET):super_misc_info.txt) +endif # BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT + +# Build $(PRODUCT_OUT)/super.img without dependencies. +.PHONY: superimage-nodeps supernod +superimage-nodeps supernod: intermediates := +superimage-nodeps supernod: | $(INSTALLED_SUPERIMAGE_DEPENDENCIES) + $(call pretty,"make $(INSTALLED_SUPERIMAGE_TARGET): ignoring dependencies") + $(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\ + $(call intermediates-dir-for,PACKAGING,superimage-nodeps)/misc_info.txt) + +endif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS != "true" +endif # BOARD_SUPER_PARTITION_SIZE != "" +endif # PRODUCT_BUILD_SUPER_PARTITION == "true" + +# ----------------------------------------------------------------- +# super empty image +ifdef BUILDING_SUPER_EMPTY_IMAGE + +INSTALLED_SUPERIMAGE_EMPTY_TARGET := $(PRODUCT_OUT)/super_empty.img +$(INSTALLED_SUPERIMAGE_EMPTY_TARGET): intermediates := $(call intermediates-dir-for,PACKAGING,super_empty) +$(INSTALLED_SUPERIMAGE_EMPTY_TARGET): $(LPMAKE) $(BUILD_SUPER_IMAGE) + $(call pretty,"Target empty super fs image: $@") + mkdir -p $(intermediates) + rm -rf $(intermediates)/misc_info.txt + $(call dump-super-image-info,$(intermediates)/misc_info.txt) + PATH=$(dir $(LPMAKE)):$$PATH \ + $(BUILD_SUPER_IMAGE) -v $(intermediates)/misc_info.txt $@ + +$(call dist-for-goals,dist_files,$(INSTALLED_SUPERIMAGE_EMPTY_TARGET)) + +$(call declare-0p-target,$(INSTALLED_SUPERIMAGE_EMPTY_TARGET)) + +endif # BUILDING_SUPER_EMPTY_IMAGE + + +# ----------------------------------------------------------------- +# The update package + +name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + name := $(name)_debug +endif +name := $(name)-img-$(FILE_NAME_TAG) + +INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip + +$(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(IMG_FROM_TARGET_FILES) + $(call pretty,"Package: $@") + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \ + $(IMG_FROM_TARGET_FILES) \ + --additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto \ + $(BUILT_TARGET_FILES_PACKAGE) $@ + +$(call declare-1p-container,$(INTERNAL_UPDATE_PACKAGE_TARGET),) +$(call declare-container-license-deps,$(INTERNAL_UPDATE_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(IMG_FROM_TARGET_FILES),$(PRODUCT_OUT)/:/) + +.PHONY: updatepackage +updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET) +$(call dist-for-goals,updatepackage,$(INTERNAL_UPDATE_PACKAGE_TARGET)) + + +# ----------------------------------------------------------------- +# dalvik something +.PHONY: dalvikfiles +dalvikfiles: $(INTERNAL_DALVIK_MODULES) + +ifeq ($(BUILD_QEMU_IMAGES),true) +MK_QEMU_IMAGE_SH := device/generic/goldfish/tools/mk_qemu_image.sh +MK_COMBINE_QEMU_IMAGE := $(HOST_OUT_EXECUTABLES)/mk_combined_img +SGDISK_HOST := $(HOST_OUT_EXECUTABLES)/sgdisk + +ifdef INSTALLED_SYSTEMIMAGE_TARGET +INSTALLED_QEMU_SYSTEMIMAGE := $(PRODUCT_OUT)/system-qemu.img +INSTALLED_SYSTEM_QEMU_CONFIG := $(PRODUCT_OUT)/system-qemu-config.txt +$(INSTALLED_SYSTEM_QEMU_CONFIG): $(INSTALLED_SUPERIMAGE_TARGET) $(INSTALLED_VBMETAIMAGE_TARGET) + @echo "$(PRODUCT_OUT)/vbmeta.img vbmeta 1" > $@ + @echo "$(INSTALLED_SUPERIMAGE_TARGET) super 2" >> $@ +$(INSTALLED_QEMU_SYSTEMIMAGE): $(INSTALLED_VBMETAIMAGE_TARGET) $(MK_COMBINE_QEMU_IMAGE) $(SGDISK_HOST) $(SIMG2IMG) \ + $(INSTALLED_SUPERIMAGE_TARGET) $(INSTALLED_SYSTEM_QEMU_CONFIG) + @echo Create system-qemu.img now + (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); \ + $(MK_COMBINE_QEMU_IMAGE) -i $(INSTALLED_SYSTEM_QEMU_CONFIG) -o $@) + +systemimage: $(INSTALLED_QEMU_SYSTEMIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_SYSTEMIMAGE) +endif +ifdef INSTALLED_VENDORIMAGE_TARGET +INSTALLED_QEMU_VENDORIMAGE := $(PRODUCT_OUT)/vendor-qemu.img +$(INSTALLED_QEMU_VENDORIMAGE): $(INSTALLED_VENDORIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG) + @echo Create vendor-qemu.img + (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_VENDORIMAGE_TARGET)) + +vendorimage: $(INSTALLED_QEMU_VENDORIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_VENDORIMAGE) +endif + +ifdef INSTALLED_RAMDISK_TARGET +ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET +ifdef INTERNAL_VENDOR_RAMDISK_TARGET +INSTALLED_QEMU_RAMDISKIMAGE := $(PRODUCT_OUT)/ramdisk-qemu.img +$(INSTALLED_QEMU_RAMDISKIMAGE): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_RAMDISK_TARGET) + @echo Create ramdisk-qemu.img + (cat $(INSTALLED_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_TARGET) > $(INSTALLED_QEMU_RAMDISKIMAGE)) + +droidcore-unbundled: $(INSTALLED_QEMU_RAMDISKIMAGE) +endif +endif +endif + +ifdef INSTALLED_PRODUCTIMAGE_TARGET +INSTALLED_QEMU_PRODUCTIMAGE := $(PRODUCT_OUT)/product-qemu.img +$(INSTALLED_QEMU_PRODUCTIMAGE): $(INSTALLED_PRODUCTIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG) + @echo Create product-qemu.img + (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_PRODUCTIMAGE_TARGET)) + +productimage: $(INSTALLED_QEMU_PRODUCTIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_PRODUCTIMAGE) +endif +ifdef INSTALLED_SYSTEM_EXTIMAGE_TARGET +INSTALLED_QEMU_SYSTEM_EXTIMAGE := $(PRODUCT_OUT)/system_ext-qemu.img +$(INSTALLED_QEMU_SYSTEM_EXTIMAGE): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG) + @echo Create system_ext-qemu.img + (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)) + +systemextimage: $(INSTALLED_QEMU_SYSTEM_EXTIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_SYSTEM_EXTIMAGE) +endif +ifdef INSTALLED_ODMIMAGE_TARGET +INSTALLED_QEMU_ODMIMAGE := $(PRODUCT_OUT)/odm-qemu.img +$(INSTALLED_QEMU_ODMIMAGE): $(INSTALLED_ODMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) + @echo Create odm-qemu.img + (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_ODMIMAGE_TARGET)) + +odmimage: $(INSTALLED_QEMU_ODMIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_ODMIMAGE) +endif + +ifdef INSTALLED_VENDOR_DLKMIMAGE_TARGET +INSTALLED_QEMU_VENDOR_DLKMIMAGE := $(PRODUCT_OUT)/vendor_dlkm-qemu.img +$(INSTALLED_QEMU_VENDOR_DLKMIMAGE): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) + @echo Create vendor_dlkm-qemu.img + (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)) + +vendor_dlkmimage: $(INSTALLED_QEMU_VENDOR_DLKMIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_VENDOR_DLKMIMAGE) +endif + +ifdef INSTALLED_ODM_DLKMIMAGE_TARGET +INSTALLED_QEMU_ODM_DLKMIMAGE := $(PRODUCT_OUT)/odm_dlkm-qemu.img +$(INSTALLED_QEMU_ODM_DLKMIMAGE): $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) + @echo Create odm_dlkm-qemu.img + (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_ODM_DLKMIMAGE_TARGET)) + +odm_dlkmimage: $(INSTALLED_QEMU_ODM_DLKMIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_ODM_DLKMIMAGE) +endif + +ifdef INSTALLED_SYSTEM_DLKMIMAGE_TARGET +INSTALLED_QEMU_SYSTEM_DLKMIMAGE := $(PRODUCT_OUT)/system_dlkm-qemu.img +$(INSTALLED_QEMU_SYSTEM_DLKMIMAGE): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) + @echo Create system_dlkm-qemu.img + (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)) + +system_dlkmimage: $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE) +droidcore-unbundled: $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE) +endif + +QEMU_VERIFIED_BOOT_PARAMS := $(PRODUCT_OUT)/VerifiedBootParams.textproto +$(QEMU_VERIFIED_BOOT_PARAMS): $(INSTALLED_VBMETAIMAGE_TARGET) $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(AVBTOOL) + @echo Creating $@ + (export AVBTOOL=$(AVBTOOL); $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(INSTALLED_VBMETAIMAGE_TARGET) \ + $(INSTALLED_SYSTEMIMAGE_TARGET) $(QEMU_VERIFIED_BOOT_PARAMS)) + +systemimage: $(QEMU_VERIFIED_BOOT_PARAMS) +droidcore-unbundled: $(QEMU_VERIFIED_BOOT_PARAMS) + +endif +# ----------------------------------------------------------------- +# The emulator package +ifeq ($(BUILD_EMULATOR),true) +INTERNAL_EMULATOR_PACKAGE_FILES += \ + $(HOST_OUT_EXECUTABLES)/emulator$(HOST_EXECUTABLE_SUFFIX) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_USERDATAIMAGE_TARGET) + +name := $(TARGET_PRODUCT)-emulator-$(FILE_NAME_TAG) + +INTERNAL_EMULATOR_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip + +$(INTERNAL_EMULATOR_PACKAGE_TARGET): $(INTERNAL_EMULATOR_PACKAGE_FILES) + @echo "Package: $@" + $(hide) zip -qjX $@ $(INTERNAL_EMULATOR_PACKAGE_FILES) + +endif + + +# ----------------------------------------------------------------- +# The SDK + +ifneq ($(filter sdk,$(MAKECMDGOALS)),) + +# The SDK includes host-specific components, so it belongs under HOST_OUT. +sdk_dir := $(HOST_OUT)/sdk/$(TARGET_PRODUCT) + +# Build a name that looks like: +# +# linux-x86 --> android-sdk_12345_linux-x86 +# darwin-x86 --> android-sdk_12345_mac-x86 +# windows-x86 --> android-sdk_12345_windows +# +ifneq ($(HOST_OS),linux) + $(error Building the monolithic SDK is only supported on Linux) +endif +sdk_name := android-sdk_$(FILE_NAME_TAG) +INTERNAL_SDK_HOST_OS_NAME := linux-$(SDK_HOST_ARCH) +sdk_name := $(sdk_name)_$(INTERNAL_SDK_HOST_OS_NAME) + +sdk_dep_file := $(sdk_dir)/sdk_deps.mk + +ATREE_FILES := +-include $(sdk_dep_file) + +# if we don't have a real list, then use "everything" +ifeq ($(strip $(ATREE_FILES)),) +ATREE_FILES := \ + $(ALL_DOCS) \ + $(ALL_SDK_FILES) +endif + +atree_dir := development/build + + +sdk_atree_files := $(atree_dir)/sdk.exclude.atree + +# development/build/sdk-android-.atree is used to differentiate +# between architecture models (e.g. ARMv5TE versus ARMv7) when copying +# files like the kernel image. We use TARGET_CPU_ABI because we don't +# have a better way to distinguish between CPU models. +ifneq (,$(strip $(wildcard $(atree_dir)/sdk-android-$(TARGET_CPU_ABI).atree))) + sdk_atree_files += $(atree_dir)/sdk-android-$(TARGET_CPU_ABI).atree +endif + +ifneq ($(PRODUCT_SDK_ATREE_FILES),) +sdk_atree_files += $(PRODUCT_SDK_ATREE_FILES) +else +sdk_atree_files += $(atree_dir)/sdk.atree +endif + +deps := \ + $(OUT_DOCS)/offline-sdk-timestamp \ + $(SDK_METADATA_FILES) \ + $(INSTALLED_SDK_BUILD_PROP_TARGET) \ + $(ATREE_FILES) \ + $(sdk_atree_files) \ + $(HOST_OUT_EXECUTABLES)/atree \ + $(HOST_OUT_EXECUTABLES)/line_endings + +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) + +# Set SDK_GNU_ERROR to non-empty to fail when a GNU target is built. +# +#SDK_GNU_ERROR := true + +$(INTERNAL_SDK_TARGET): $(deps) + @echo "Package SDK: $@" + $(hide) rm -rf $(PRIVATE_DIR) $@ + $(hide) for f in $(strip $(target_gnu_MODULES)); do \ + if [ -f $$f ]; then \ + echo SDK: $(if $(SDK_GNU_ERROR),ERROR:,warning:) \ + including GNU target $$f >&2; \ + FAIL=$(SDK_GNU_ERROR); \ + fi; \ + done; \ + if [ $$FAIL ]; then exit 1; fi + $(hide) ( \ + ATREE_STRIP="$(HOST_STRIP) -x" \ + $(HOST_OUT_EXECUTABLES)/atree \ + $(addprefix -f ,$(PRIVATE_INPUT_FILES)) \ + -m $(PRIVATE_DEP_FILE) \ + -I . \ + -I $(PRODUCT_OUT) \ + -I $(HOST_OUT) \ + -I $(TARGET_COMMON_OUT_ROOT) \ + -v "PLATFORM_NAME=android-$(PLATFORM_VERSION)" \ + -v "OUT_DIR=$(OUT_DIR)" \ + -v "HOST_OUT=$(HOST_OUT)" \ + -v "TARGET_ARCH=$(TARGET_ARCH)" \ + -v "TARGET_CPU_ABI=$(TARGET_CPU_ABI)" \ + -v "DLL_EXTENSION=$(HOST_SHLIB_SUFFIX)" \ + -o $(PRIVATE_DIR) && \ + HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \ + development/build/tools/sdk_clean.sh $(PRIVATE_DIR) && \ + chmod -R ug+rwX $(PRIVATE_DIR) && \ + cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) \ + ) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 ) + +MAIN_SDK_DIR := $(sdk_dir) +MAIN_SDK_ZIP := $(INTERNAL_SDK_TARGET) + +endif # sdk in MAKECMDGOALS + +# ----------------------------------------------------------------- +# Findbugs +INTERNAL_FINDBUGS_XML_TARGET := $(PRODUCT_OUT)/findbugs.xml +INTERNAL_FINDBUGS_HTML_TARGET := $(PRODUCT_OUT)/findbugs.html +$(INTERNAL_FINDBUGS_XML_TARGET): $(ALL_FINDBUGS_FILES) + @echo UnionBugs: $@ + $(hide) $(FINDBUGS_DIR)/unionBugs $(ALL_FINDBUGS_FILES) \ + > $@ +$(INTERNAL_FINDBUGS_HTML_TARGET): $(INTERNAL_FINDBUGS_XML_TARGET) + @echo ConvertXmlToText: $@ + $(hide) $(FINDBUGS_DIR)/convertXmlToText -html:fancy.xsl \ + $(INTERNAL_FINDBUGS_XML_TARGET) > $@ + +# ----------------------------------------------------------------- +# Findbugs + +# ----------------------------------------------------------------- +# These are some additional build tasks that need to be run. +ifneq ($(dont_bother),true) +include $(sort $(wildcard $(BUILD_SYSTEM)/tasks/*.mk)) +-include $(sort $(wildcard vendor/*/build/tasks/*.mk)) +-include $(sort $(wildcard device/*/build/tasks/*.mk)) +-include $(sort $(wildcard product/*/build/tasks/*.mk)) +# Also the project-specific tasks +-include $(sort $(wildcard vendor/*/*/build/tasks/*.mk)) +-include $(sort $(wildcard device/*/*/build/tasks/*.mk)) +-include $(sort $(wildcard product/*/*/build/tasks/*.mk)) +# Also add test specifc tasks +include $(sort $(wildcard platform_testing/build/tasks/*.mk)) +include $(sort $(wildcard test/vts/tools/build/tasks/*.mk)) +endif + +include $(BUILD_SYSTEM)/product-graph.mk + +# ----------------------------------------------------------------- +# Create SDK repository packages. Must be done after tasks/* since +# we need the addon rules defined. +ifneq ($(sdk_repo_goal),) +include $(TOPDIR)development/build/tools/sdk_repo.mk +endif + +# ----------------------------------------------------------------- +# Soong generates the list of all shared libraries that are depended on by fuzz +# targets. It saves this list as a source:destination pair to +# FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS, where the source is the path to the +# build of the unstripped shared library, and the destination is the +# /data/fuzz/$ARCH/lib (for device) or /fuzz/$ARCH/lib (for host) directory +# where fuzz target shared libraries are to be "reinstalled". The +# copy-many-files below generates the rules to copy the unstripped shared +# libraries to the device or host "reinstallation" directory. These rules are +# depended on by each module in soong_cc_prebuilt.mk, where the module will have +# a dependency on each shared library that it needs to be "reinstalled". +FUZZ_SHARED_DEPS := $(call copy-many-files,$(strip $(FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS))) + +# ----------------------------------------------------------------- +# The rule to build all fuzz targets for C++ and Rust, and package them. +# Note: The packages are created in Soong, and in a perfect world, +# we'd be able to create the phony rule there. But, if we want to +# have dist goals for the fuzz target, we need to have the PHONY +# target defined in make. MakeVarsContext.DistForGoal doesn't take +# into account that a PHONY rule create by Soong won't be available +# during make, and such will fail with `writing to readonly +# directory`, because kati will see 'haiku' as being a file, not a +# phony target. +.PHONY: haiku +haiku: $(SOONG_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_FUZZ_TARGETS) +$(call dist-for-goals,haiku,$(SOONG_FUZZ_PACKAGING_ARCH_MODULES)) + +.PHONY: haiku-java +haiku-java: $(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_JAVA_FUZZ_TARGETS) +$(call dist-for-goals,haiku-java,$(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES)) + +.PHONY: haiku-rust +haiku-rust: $(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_RUST_FUZZ_TARGETS) +$(call dist-for-goals,haiku-rust,$(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES)) + +# ----------------------------------------------------------------- +# Extract platform fonts used in Layoutlib +include $(BUILD_SYSTEM)/layoutlib_fonts.mk + + +# ----------------------------------------------------------------- +# OS Licensing + +include $(BUILD_SYSTEM)/os_licensing.mk + +# When appending new code to this file, please insert above OS Licensing diff --git a/make/core/OWNERS b/make/core/OWNERS new file mode 100644 index 0000000..8d612e0 --- /dev/null +++ b/make/core/OWNERS @@ -0,0 +1,6 @@ +per-file dex_preopt*.mk = ngeoffray@google.com,calin@google.com,mathewi@google.com,skvadrik@google.com +per-file verify_uses_libraries.sh = ngeoffray@google.com,calin@google.com,skvadrik@google.com + +# For version updates +per-file version_defaults.mk = aseaton@google.com,lubomir@google.com,pscovanner@google.com,bkhalife@google.com,jainne@google.com + diff --git a/make/core/WINPTHREADS_COPYING b/make/core/WINPTHREADS_COPYING new file mode 100644 index 0000000..3507701 --- /dev/null +++ b/make/core/WINPTHREADS_COPYING @@ -0,0 +1,57 @@ +Copyright (c) 2011 mingw-w64 project + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/make/core/aapt2.mk b/make/core/aapt2.mk new file mode 100644 index 0000000..7b17df4 --- /dev/null +++ b/make/core/aapt2.mk @@ -0,0 +1,146 @@ +###################################### +# Compile resource with AAPT2 +# Input variables: +# - full_android_manifest +# - my_res_resources +# - my_overlay_resources +# - my_compiled_res_base_dir +# - my_asset_dirs +# - my_full_asset_paths +# - my_res_package +# - R_file_stamp +# - proguard_options_file +# - my_generated_res_dirs: Resources generated during the build process and we have to compile them in a single run of aapt2. +# - my_generated_res_dirs_deps: the dependency to use for my_generated_res_dirs. +# - my_generated_res_zips: Zip files containing resources +# - my_apk_split_configs: The configurations for which to generate splits. +# - built_apk_splits: The paths where AAPT should generate the splits. +# +# Output variables: +# - my_res_resources_flat +# - my_overlay_resources_flat +# - my_generated_resources_flata +# +###################################### + +# Compile all the resource files. +my_res_resources_flat := \ + $(foreach r, $(my_res_resources),\ + $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\ + $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\ + $(o)) + +my_overlay_resources_flat := \ + $(foreach r, $(my_overlay_resources),\ + $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\ + $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\ + $(o)) + +my_resources_flata := +# Compile generated resources +ifneq ($(my_generated_res_dirs),) +my_generated_resources_flata := $(my_compiled_res_base_dir)/gen_res.flata +$(my_generated_resources_flata): PRIVATE_SOURCE_RES_DIRS := $(my_generated_res_dirs) +$(my_generated_resources_flata) : $(my_generated_res_dirs_deps) $(AAPT2) + @echo "AAPT2 compile $@ <- $(PRIVATE_SOURCE_RES_DIRS)" + $(call aapt2-compile-resource-dirs) + +my_resources_flata += $(my_generated_resources_flata) +endif + +# Compile zipped resources +ifneq ($(my_generated_res_zips),) +my_zipped_resources_flata := $(my_compiled_res_base_dir)/zip_res.flata +$(my_zipped_resources_flata): PRIVATE_SOURCE_RES_ZIPS := $(my_generated_res_zips) +$(my_zipped_resources_flata) : $(my_generated_res_zips) $(AAPT2) $(ZIPSYNC) + @echo "AAPT2 compile $@ <- $(PRIVATE_SOURCE_RES_ZIPS)" + $(call aapt2-compile-resource-zips) + +my_resources_flata += $(my_zipped_resources_flata) +endif + +# Always set --pseudo-localize, it will be stripped out later for release +# builds that don't want it. +$(my_res_resources_flat) $(my_overlay_resources_flat) $(my_resources_flata) $(my_generated_resources_flata) $(my_zippped_resources_flata): \ + PRIVATE_AAPT2_CFLAGS := --pseudo-localize $(filter --legacy,$(LOCAL_AAPT_FLAGS)) + +# TODO(b/78447299): Forbid LOCAL_STATIC_JAVA_AAR_LIBRARIES in aapt2 and remove +# support for it. +my_static_library_resources := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk) +my_static_library_extra_packages := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/extra_packages) +my_shared_library_resources := $(foreach l, $(LOCAL_SHARED_ANDROID_LIBRARIES),\ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk) + +ifneq ($(my_static_library_resources),) +$(my_res_package): PRIVATE_AAPT_FLAGS += --auto-add-overlay +endif + +ifneq ($(my_apk_split_configs),) +# Join the Split APK paths with their configuration, separated by a ':'. +$(my_res_package): PRIVATE_AAPT_FLAGS += $(addprefix --split ,$(join $(built_apk_splits),$(addprefix :,$(my_apk_split_configs)))) +endif + +my_srcjar := $(intermediates.COMMON)/aapt2.srcjar +LOCAL_SRCJARS += $(my_srcjar) + +aapt_extra_packages := $(intermediates.COMMON)/extra_packages + +$(my_res_package): PRIVATE_RES_FLAT := $(my_res_resources_flat) +$(my_res_package): PRIVATE_OVERLAY_FLAT := $(my_static_library_resources) $(my_resources_flata) $(my_overlay_resources_flat) +$(my_res_package): PRIVATE_SHARED_ANDROID_LIBRARIES := $(my_shared_library_resources) +$(my_res_package): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file) +$(my_res_package): PRIVATE_ASSET_DIRS := $(my_asset_dirs) +$(my_res_package): PRIVATE_JAVA_GEN_DIR := $(intermediates.COMMON)/aapt2 +$(my_res_package): PRIVATE_SRCJAR := $(my_srcjar) +$(my_res_package): PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES := $(my_static_library_extra_packages) +$(my_res_package): PRIVATE_AAPT_EXTRA_PACKAGES := $(aapt_extra_packages) +$(my_res_package): .KATI_IMPLICIT_OUTPUTS := $(my_srcjar) $(aapt_extra_packages) + +ifdef R_file_stamp +$(my_res_package): PRIVATE_R_FILE_STAMP := $(R_file_stamp) +$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(R_file_stamp) +endif + +resource_export_package := +ifdef LOCAL_EXPORT_PACKAGE_RESOURCES +# Put this module's resources into a PRODUCT-agnositc package that +# other packages can use to build their own PRODUCT-agnostic R.java (etc.) +# files. +resource_export_package := $(intermediates.COMMON)/package-export.apk +$(my_res_package): PRIVATE_RESOURCE_EXPORT_PACKAGE := $(resource_export_package) +$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(resource_export_package) +endif + +ifdef proguard_options_file +$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(proguard_options_file) +endif + +$(my_res_package): $(full_android_manifest) $(my_static_library_resources) $(my_shared_library_resources) +$(my_res_package): $(my_full_asset_paths) +$(my_res_package): $(my_res_resources_flat) $(my_overlay_resources_flat) \ + $(my_resources_flata) $(my_static_library_resources) $(my_static_library_extra_packages) \ + $(AAPT2) $(SOONG_ZIP) $(EXTRACT_JAR_PACKAGES) + @echo "AAPT2 link $@" + $(call aapt2-link) +ifdef R_file_stamp + @rm -f $(PRIVATE_R_FILE_STAMP) + $(call find-generated-R.java,$(PRIVATE_JAVA_GEN_DIR),$(PRIVATE_R_FILE_STAMP)) +endif +ifdef LOCAL_EXPORT_PACKAGE_RESOURCES + @rm -f $(PRIVATE_RESOURCE_EXPORT_PACKAGE) + + cp $@ $(PRIVATE_RESOURCE_EXPORT_PACKAGE) +endif + +# Clear inputs only used in this file, so that they're not re-used during the next build +my_res_resources := +my_overlay_resources := +my_compiled_res_base_dir := +my_asset_dirs := +my_full_asset_paths := +my_apk_split_configs := +my_generated_res_dirs := +my_generated_res_dirs_deps := +my_generated_res_zips := diff --git a/make/core/aapt_flags.mk b/make/core/aapt_flags.mk new file mode 100644 index 0000000..13d4817 --- /dev/null +++ b/make/core/aapt_flags.mk @@ -0,0 +1,15 @@ +## AAPT Flags +# aapt doesn't accept multiple --extra-packages flags. +# We have to collapse them into a single --extra-packages flag here. +LOCAL_AAPT_FLAGS := $(strip $(LOCAL_AAPT_FLAGS)) +ifdef LOCAL_AAPT_FLAGS + ifeq ($(filter 0 1,$(words $(filter --extra-packages,$(LOCAL_AAPT_FLAGS)))),) + aapt_flags := $(subst --extra-packages$(space),--extra-packages@,$(LOCAL_AAPT_FLAGS)) + aapt_flags_extra_packages := $(patsubst --extra-packages@%,%,$(filter --extra-packages@%,$(aapt_flags))) + aapt_flags_extra_packages := $(sort $(subst :,$(space),$(aapt_flags_extra_packages))) + LOCAL_AAPT_FLAGS := $(filter-out --extra-packages@%,$(aapt_flags)) \ + --extra-packages $(subst $(space),:,$(aapt_flags_extra_packages)) + aapt_flags_extra_packages := + aapt_flags := + endif +endif diff --git a/make/core/allowed_ndk_types.mk b/make/core/allowed_ndk_types.mk new file mode 100644 index 0000000..b88b9e8 --- /dev/null +++ b/make/core/allowed_ndk_types.mk @@ -0,0 +1,84 @@ +# Determines the types of NDK modules the current module is allowed to link to. +# Input variables: +# LOCAL_MODULE +# LOCAL_MODULE_CLASS +# LOCAL_NDK_STL_VARIANT +# LOCAL_SDK_VERSION +# Output variables: +# my_ndk_stl_family: Family of the NDK STL. +# my_ndk_stl_link_type: STL link type, static or shared. +# my_allowed_ndk_types: Types of NDK modules that may be linked. +# my_warn_ndk_types: Types of NDK modules that shouldn't be linked, but are. + +my_allowed_ndk_types := +my_warn_ndk_types := +my_ndk_stl_family := +my_ndk_stl_link_type := + +ifdef LOCAL_SDK_VERSION + ifeq ($(LOCAL_NDK_STL_VARIANT),) + my_ndk_stl_family := system + my_ndk_stl_link_type := shared + else ifeq ($(LOCAL_NDK_STL_VARIANT),system) + my_ndk_stl_family := system + my_ndk_stl_link_type := shared + else ifeq ($(LOCAL_NDK_STL_VARIANT),c++_shared) + my_ndk_stl_family := libc++ + my_ndk_stl_link_type := shared + else ifeq ($(LOCAL_NDK_STL_VARIANT),c++_static) + my_ndk_stl_family := libc++ + my_ndk_stl_link_type := static + else ifeq ($(LOCAL_NDK_STL_VARIANT),none) + my_ndk_stl_family := none + my_ndk_stl_link_type := none + else + $(call pretty-error,invalid LOCAL_NDK_STL_VARIANT: $(LOCAL_NDK_STL_VARIANT)) + endif + + ifeq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES) + # The "none" link type indicates that nothing is actually linked. Since + # this is a static library, it's still up to the final use of the + # library whether a static or shared STL should be used. + my_ndk_stl_link_type := none + endif + + # The system STL is only the C++ ABI layer, so it's compatible with any STL. + my_allowed_ndk_types += native:ndk:system:shared + my_allowed_ndk_types += native:ndk:system:none + + # Libaries that don't use the STL can be linked to anything. + my_allowed_ndk_types += native:ndk:none:none + + # And it's always okay to link a static library that uses your own STL type. + # Since nothing was actually linked for the static library, it is up to the + # first linked library in the dependency chain which gets used. + my_allowed_ndk_types += native:ndk:$(my_ndk_stl_family):none + + ifeq ($(LOCAL_MODULE_CLASS),APPS) + # For an app package, it's actually okay to depend on any set of STLs. + # If any of the individual libraries depend on each other they've + # already been checked for consistency, and if they don't they'll be + # kept isolated by RTLD_LOCAL anyway. + my_allowed_ndk_types += \ + native:ndk:libc++:shared native:ndk:libc++:static + + # The "none" link type that used by static libraries is intentionally + # omitted here. We should only be dealing with shared libraries in + # LOCAL_JNI_SHARED_LIBRARIES. + else ifeq ($(my_ndk_stl_link_type),shared) + # Modules linked to a shared STL can only use another shared STL. + my_allowed_ndk_types += native:ndk:$(my_ndk_stl_family):shared + endif + # Else we are a non-static library that uses a static STL, and are + # incompatible with all other shared libraries that use an STL. +else + my_allowed_ndk_types := \ + native:ndk:none:none \ + native:ndk:system:none \ + native:ndk:system:shared \ + + ifeq ($(LOCAL_MODULE_CLASS),APPS) + # CTS is bad and it should feel bad: http://b/13249737 + my_warn_ndk_types += native:ndk:libc++:static + endif +endif diff --git a/make/core/android_manifest.mk b/make/core/android_manifest.mk new file mode 100644 index 0000000..254e09b --- /dev/null +++ b/make/core/android_manifest.mk @@ -0,0 +1,111 @@ +# Handle AndroidManifest.xmls +# Input: LOCAL_MANIFEST_FILE, LOCAL_FULL_MANIFEST_FILE, LOCAL_FULL_LIBS_MANIFEST_FILES, +# LOCAL_USE_EMBEDDED_NATIVE_LIBS +# Output: full_android_manifest + +ifeq ($(strip $(LOCAL_MANIFEST_FILE)),) + LOCAL_MANIFEST_FILE := AndroidManifest.xml +endif +ifdef LOCAL_FULL_MANIFEST_FILE + main_android_manifest := $(LOCAL_FULL_MANIFEST_FILE) +else + main_android_manifest := $(LOCAL_PATH)/$(LOCAL_MANIFEST_FILE) +endif + +LOCAL_STATIC_JAVA_AAR_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)) + +my_full_libs_manifest_files := + +ifndef LOCAL_DONT_MERGE_MANIFESTS + my_full_libs_manifest_files += $(LOCAL_FULL_LIBS_MANIFEST_FILES) + + my_full_libs_manifest_files += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES),\ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/manifest/AndroidManifest.xml) +endif + +full_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml + +ifneq (,$(strip $(my_full_libs_manifest_files))) + # Set up rules to merge library manifest files + fixed_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml.fixed + + $(full_android_manifest): PRIVATE_LIBS_MANIFESTS := $(my_full_libs_manifest_files) + $(full_android_manifest): $(ANDROID_MANIFEST_MERGER) + $(full_android_manifest) : $(fixed_android_manifest) $(my_full_libs_manifest_files) + @echo "Merge android manifest files: $@ <-- $< $(PRIVATE_LIBS_MANIFESTS)" + @mkdir -p $(dir $@) + $(hide) $(ANDROID_MANIFEST_MERGER) --main $< \ + --libs $(call normalize-path-list,$(PRIVATE_LIBS_MANIFESTS)) \ + --out $@ +else + fixed_android_manifest := $(full_android_manifest) +endif + +my_target_sdk_version := $(call module-target-sdk-version) +my_min_sdk_version := $(call module-min-sdk-version) + +ifdef TARGET_BUILD_APPS + ifndef TARGET_BUILD_USE_PREBUILT_SDKS + ifeq ($(my_target_sdk_version),$(PLATFORM_VERSION_CODENAME)) + ifdef UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT + my_target_sdk_version := $(my_target_sdk_version).$$(cat $(API_FINGERPRINT)) + my_min_sdk_version := $(my_min_sdk_version).$$(cat $(API_FINGERPRINT)) + $(fixed_android_manifest): $(API_FINGERPRINT) + endif + endif + endif +endif + +$(fixed_android_manifest): PRIVATE_MIN_SDK_VERSION := $(my_min_sdk_version) +$(fixed_android_manifest): PRIVATE_TARGET_SDK_VERSION := $(my_target_sdk_version) + +my_exported_sdk_libs_file := $(call local-intermediates-dir,COMMON)/exported-sdk-libs +$(fixed_android_manifest): PRIVATE_EXPORTED_SDK_LIBS_FILE := $(my_exported_sdk_libs_file) +$(fixed_android_manifest): $(my_exported_sdk_libs_file) + +my_manifest_fixer_flags := +ifneq ($(LOCAL_MODULE_CLASS),APPS) + my_manifest_fixer_flags += --library +endif +ifeq ($(LOCAL_PRIVATE_PLATFORM_APIS),true) + my_manifest_fixer_flags += --uses-non-sdk-api +endif + +ifeq (true,$(LOCAL_USE_EMBEDDED_DEX)) + my_manifest_fixer_flags += --use-embedded-dex +endif + +ifeq ($(LOCAL_MODULE_CLASS),APPS) + ifeq (true,$(call math_gt_or_eq,$(patsubst $(PLATFORM_VERSION_CODENAME),100,$(call module-min-sdk-version)),23)) + ifeq (true,$(LOCAL_USE_EMBEDDED_NATIVE_LIBS)) + my_manifest_fixer_flags += --extract-native-libs=false + else + my_manifest_fixer_flags += --extract-native-libs=true + endif + else ifeq (true,$(LOCAL_USE_EMBEDDED_NATIVE_LIBS)) + $(call pretty-error,LOCAL_USE_EMBEDDED_NATIVE_LIBS is set but minSdkVersion $(call module-min-sdk-version) does not support it) + endif +endif + +$(fixed_android_manifest): PRIVATE_MANIFEST_FIXER_FLAGS := $(my_manifest_fixer_flags) +# These two libs are added as optional dependencies ( with +# android:required set to false). This is because they haven't existed in pre-P +# devices, but classes in them were in bootclasspath jars, etc. So making them +# hard dependencies (andriod:required=true) would prevent apps from being +# installed to such legacy devices. +$(fixed_android_manifest): PRIVATE_OPTIONAL_SDK_LIB_NAMES := android.test.base android.test.mock +$(fixed_android_manifest): $(MANIFEST_FIXER) +$(fixed_android_manifest): $(main_android_manifest) + echo $(PRIVATE_OPTIONAL_SDK_LIB_NAMES) | tr ' ' '\n' > $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional + @echo "Fix manifest: $@" + $(MANIFEST_FIXER) \ + --minSdkVersion $(PRIVATE_MIN_SDK_VERSION) \ + --targetSdkVersion $(PRIVATE_TARGET_SDK_VERSION) \ + --raise-min-sdk-version \ + $(PRIVATE_MANIFEST_FIXER_FLAGS) \ + $(if (PRIVATE_EXPORTED_SDK_LIBS_FILE),\ + $$(cat $(PRIVATE_EXPORTED_SDK_LIBS_FILE) | grep -v -f $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional | sort -u | sed -e 's/^/\ --uses-library\ /' | tr '\n' ' ') \ + $$(cat $(PRIVATE_EXPORTED_SDK_LIBS_FILE) | grep -f $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional | sort -u | sed -e 's/^/\ --optional-uses-library\ /' | tr '\n' ' ') \ + ) \ + $< $@ + rm $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional diff --git a/make/core/android_soong_config_vars.mk b/make/core/android_soong_config_vars.mk new file mode 100644 index 0000000..cba0e03 --- /dev/null +++ b/make/core/android_soong_config_vars.mk @@ -0,0 +1,186 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file defines the Soong Config Variable namespace ANDROID, and also any +# variables in that namespace. + +# The expectation is that no vendor should be using the ANDROID namespace. This +# check ensures that we don't collide with any existing vendor usage. + +ifdef SOONG_CONFIG_ANDROID +$(error The Soong config namespace ANDROID is reserved.) +endif + +$(call add_soong_config_namespace,ANDROID) + +# Add variables to the namespace below: + +$(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64) +$(call add_soong_config_var,ANDROID,IS_TARGET_MIXED_SEPOLICY) +ifeq ($(IS_TARGET_MIXED_SEPOLICY),true) +$(call add_soong_config_var_value,ANDROID,MIXED_SEPOLICY_VERSION,$(BOARD_SEPOLICY_VERS)) +endif +$(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE) +$(call add_soong_config_var,ANDROID,BOARD_USES_RECOVERY_AS_BOOT) +$(call add_soong_config_var,ANDROID,BOARD_BUILD_SYSTEM_ROOT_IMAGE) +$(call add_soong_config_var,ANDROID,PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT) + +# Default behavior for the tree wrt building modules or using prebuilts. This +# can always be overridden by setting the environment variable +# MODULE_BUILD_FROM_SOURCE. +BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := false + +ifneq ($(SANITIZE_TARGET)$(EMMA_INSTRUMENT_FRAMEWORK),) + # Always use sources when building the framework with Java coverage or + # sanitized builds as they both require purpose built prebuilts which we do + # not provide. + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +ifneq ($(CLANG_COVERAGE)$(NATIVE_COVERAGE_PATHS),) + # Always use sources when building with clang coverage and native coverage. + # It is possible that there are certain situations when building with coverage + # would work with prebuilts, e.g. when the coverage is not being applied to + # modules for which we provide prebuilts. Unfortunately, determining that + # would require embedding knowledge of which coverage paths affect which + # modules here. That would duplicate a lot of information, add yet another + # location module authors have to update and complicate the logic here. + # For nowe we will just always build from sources when doing coverage builds. + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +# TODO(b/172063604): Remove once products no longer use dex2oat(d)s. +# If the product uses dex2oats and/or dex2oatds then build from sources as +# ART does not currently provide prebuilts of those tools. +ifneq (,$(filter dex2oats dex2oatds,$(PRODUCT_HOST_PACKAGES))) + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +# ART does not provide linux_bionic variants needed for products that +# set HOST_CROSS_OS=linux_bionic. +ifeq (linux_bionic,${HOST_CROSS_OS}) + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +# ART does not provide host side arm64 variants needed for products that +# set HOST_CROSS_ARCH=arm64. +ifeq (arm64,${HOST_CROSS_ARCH}) + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +# TV based devices do not seem to work with prebuilts, so build from source +# for now and fix in a follow up. +ifneq (,$(filter tv,$(subst $(comma),$(space),${PRODUCT_CHARACTERISTICS}))) + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +# ATV based devices do not seem to work with prebuilts, so build from source +# for now and fix in a follow up. +ifneq (,${PRODUCT_IS_ATV}) + BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true +endif + +ifneq (,$(MODULE_BUILD_FROM_SOURCE)) + # Keep an explicit setting. +else ifeq (,$(filter docs sdk win_sdk sdk_addon,$(MAKECMDGOALS))$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES))) + # Prebuilt module SDKs require prebuilt modules to work, and currently + # prebuilt modules are only provided for com.google.android.xxx. If we can't + # find one of them in PRODUCT_PACKAGES then assume com.android.xxx are in use, + # and disable prebuilt SDKs. In particular this applies to AOSP builds. + # + # However, docs/sdk/win_sdk/sdk_addon builds might not include com.google.android.xxx + # packages, so for those we respect the default behavior. + MODULE_BUILD_FROM_SOURCE := true +else ifneq (,$(PRODUCT_MODULE_BUILD_FROM_SOURCE)) + # Let products override the branch default. + MODULE_BUILD_FROM_SOURCE := $(PRODUCT_MODULE_BUILD_FROM_SOURCE) +else + MODULE_BUILD_FROM_SOURCE := $(BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE) +endif + +ifneq (,$(ART_MODULE_BUILD_FROM_SOURCE)) + # Keep an explicit setting. +else ifneq (,$(findstring .android.art,$(TARGET_BUILD_APPS))) + # Build ART modules from source if they are listed in TARGET_BUILD_APPS. + ART_MODULE_BUILD_FROM_SOURCE := true +else + # Do the same as other modules by default. + ART_MODULE_BUILD_FROM_SOURCE := $(MODULE_BUILD_FROM_SOURCE) +endif + +$(call soong_config_set,art_module,source_build,$(ART_MODULE_BUILD_FROM_SOURCE)) + +ifdef TARGET_BOARD_AUTO + $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO)) +endif + +# Ensure that those mainline modules who have individually toggleable prebuilts +# are controlled by the MODULE_BUILD_FROM_SOURCE environment variable by +# default. +INDIVIDUALLY_TOGGLEABLE_PREBUILT_MODULES := \ + btservices \ + permission \ + uwb \ + wifi \ + +$(foreach m, $(INDIVIDUALLY_TOGGLEABLE_PREBUILT_MODULES),\ + $(if $(call soong_config_get,$(m)_module,source_build),,\ + $(call soong_config_set,$(m)_module,source_build,$(MODULE_BUILD_FROM_SOURCE)))) + +# Apex build mode variables +ifdef APEX_BUILD_FOR_PRE_S_DEVICES +$(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static) +endif + +ifeq (true,$(MODULE_BUILD_FROM_SOURCE)) +$(call add_soong_config_var_value,ANDROID,module_build_from_source,true) +endif + +# Messaging app vars +ifeq (eng,$(TARGET_BUILD_VARIANT)) +$(call soong_config_set,messaging,build_variant_eng,true) +endif + +# Enable SystemUI optimizations by default unless explicitly set. +SYSTEMUI_OPTIMIZE_JAVA ?= true +$(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA) + +# Disable Compose in SystemUI by default. +SYSTEMUI_USE_COMPOSE ?= false +$(call add_soong_config_var,ANDROID,SYSTEMUI_USE_COMPOSE) + +# Enable system_server optimizations by default unless explicitly set or if +# there may be dependent runtime jars. +# TODO(b/240588226): Remove the off-by-default exceptions after handling +# system_server jars automatically w/ R8. +ifeq (true,$(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS)) + # If system_server jar ordering is broken, don't assume services.jar can be + # safely optimized in isolation, as there may be dependent jars. + SYSTEM_OPTIMIZE_JAVA ?= false +else ifneq (platform:services,$(lastword $(PRODUCT_SYSTEM_SERVER_JARS))) + # If services is not the final jar in the dependency ordering, don't assume + # it can be safely optimized in isolation, as there may be dependent jars. + SYSTEM_OPTIMIZE_JAVA ?= false +else + SYSTEM_OPTIMIZE_JAVA ?= true +endif +$(call add_soong_config_var,ANDROID,SYSTEM_OPTIMIZE_JAVA) + +# Check for SupplementalApi module. +ifeq ($(wildcard packages/modules/SupplementalApi),) +$(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,false) +else +$(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,true) +endif + diff --git a/make/core/app_certificate_validate.mk b/make/core/app_certificate_validate.mk new file mode 100644 index 0000000..1ccacfb --- /dev/null +++ b/make/core/app_certificate_validate.mk @@ -0,0 +1,10 @@ + +ifeq (true,$(non_system_module)) + ifneq (,$(filter $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))%,$(LOCAL_CERTIFICATE))) + CERTIFICATE_VIOLATION_MODULES += $(LOCAL_MODULE) + ifeq (true,$(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)) + $(if $(filter $(LOCAL_MODULE),$(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)),,\ + $(call pretty-error,The module in product partition cannot be signed with certificate in system.)) + endif + endif +endif diff --git a/make/core/app_prebuilt_internal.mk b/make/core/app_prebuilt_internal.mk new file mode 100644 index 0000000..eb429cd --- /dev/null +++ b/make/core/app_prebuilt_internal.mk @@ -0,0 +1,304 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################ +# Internal build rules for APPS prebuilt modules +############################################################ + +ifneq (APPS,$(LOCAL_MODULE_CLASS)) +$(call pretty-error,app_prebuilt_internal.mk is for APPS modules only) +endif + +ifdef LOCAL_COMPRESSED_MODULE + ifneq (true,$(LOCAL_COMPRESSED_MODULE)) + $(call pretty-error, Unknown value for LOCAL_COMPRESSED_MODULE $(LOCAL_COMPRESSED_MODULE)) + endif + LOCAL_BUILT_MODULE_STEM := package.apk.gz + ifndef LOCAL_INSTALLED_MODULE_STEM + PACKAGES.$(LOCAL_MODULE).COMPRESSED := gz + LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk.gz + endif +else # LOCAL_COMPRESSED_MODULE + LOCAL_BUILT_MODULE_STEM := package.apk + ifndef LOCAL_INSTALLED_MODULE_STEM + LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk + endif +endif # LOCAL_COMPRESSED_MODULE + +include $(BUILD_SYSTEM)/base_rules.mk +built_module := $(LOCAL_BUILT_MODULE) + +# Run veridex on product, system_ext and vendor modules. +# We skip it for unbundled app builds where we cannot build veridex. +module_run_appcompat := +ifeq (true,$(non_system_module)) +ifeq (,$(TARGET_BUILD_APPS)) # ! unbundled app build +ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) + module_run_appcompat := true +endif +endif +endif + +PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) + +my_extract_apk := $(strip $(LOCAL_EXTRACT_APK)) + +# Select dpi-specific source +ifdef LOCAL_DPI_VARIANTS +my_dpi := $(firstword $(filter $(LOCAL_DPI_VARIANTS),$(PRODUCT_AAPT_PREF_CONFIG) $(PRODUCT_AAPT_PREBUILT_DPI))) +ifdef my_dpi +ifdef LOCAL_DPI_FILE_STEM +my_prebuilt_dpi_file_stem := $(LOCAL_DPI_FILE_STEM) +else +my_prebuilt_dpi_file_stem := $(LOCAL_MODULE)_%.apk +endif +my_prebuilt_src_file := $(dir $(my_prebuilt_src_file))$(subst %,$(my_dpi),$(my_prebuilt_dpi_file_stem)) + +ifneq ($(strip $(LOCAL_EXTRACT_DPI_APK)),) +my_extract_apk := $(subst %,$(my_dpi),$(LOCAL_EXTRACT_DPI_APK)) +endif # LOCAL_EXTRACT_DPI_APK +endif # my_dpi +endif # LOCAL_DPI_VARIANTS + +ifdef my_extract_apk +my_extracted_apk := $(intermediates)/extracted.apk + +$(my_extracted_apk): PRIVATE_EXTRACT := $(my_extract_apk) +$(my_extracted_apk): $(my_prebuilt_src_file) + @echo Extract APK: $@ + $(hide) mkdir -p $(dir $@) && rm -f $@ + $(hide) unzip -p $< $(PRIVATE_EXTRACT) >$@ + +my_prebuilt_src_file := $(my_extracted_apk) +my_extracted_apk := +my_extract_apk := +ifeq ($(PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK),true) +# If the product property is set, always preopt for extracted modules to prevent executing out of +# the APK. +my_preopt_for_extracted_apk := true +endif +endif + +rs_compatibility_jni_libs := +include $(BUILD_SYSTEM)/install_jni_libs.mk + +ifeq ($(LOCAL_CERTIFICATE),EXTERNAL) + # The magic string "EXTERNAL" means this package will be signed with + # the default dev key throughout the build process, but we expect + # the final package to be signed with a different key. + # + # This can be used for packages where we don't have access to the + # keys, but want the package to be predexopt'ed. + LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) + PACKAGES.$(LOCAL_MODULE).EXTERNAL_KEY := 1 + + $(built_module) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem + $(built_module) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 + $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem +endif +ifeq ($(LOCAL_CERTIFICATE),) + # It is now a build error to add a prebuilt .apk without + # specifying a key for it. + $(error No LOCAL_CERTIFICATE specified for prebuilt "$(my_prebuilt_src_file)") +else ifeq ($(LOCAL_CERTIFICATE),PRESIGNED) + # The magic string "PRESIGNED" means this package is already checked + # signed with its release key. + # + # By setting .CERTIFICATE but not .PRIVATE_KEY, this package will be + # mentioned in apkcerts.txt (with certificate set to "PRESIGNED") + # but the dexpreopt process will not try to re-sign the app. + PACKAGES.$(LOCAL_MODULE).CERTIFICATE := PRESIGNED + PACKAGES := $(PACKAGES) $(LOCAL_MODULE) +else + # If this is not an absolute certificate, assign it to a generic one. + ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./) + LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE) + endif + + # NOTE(ruperts): Consider moving the logic below out of a conditional, + # to avoid the possibility of silently ignoring user settings. + + PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 + PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem + PACKAGES := $(PACKAGES) $(LOCAL_MODULE) + + $(built_module) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem + $(built_module) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 + $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem + + additional_certificates := $(foreach c,$(LOCAL_ADDITIONAL_CERTIFICATES), $(c).x509.pem $(c).pk8) + $(built_module): $(additional_certificates) + $(built_module): PRIVATE_ADDITIONAL_CERTIFICATES := $(additional_certificates) + + $(built_module): $(LOCAL_CERTIFICATE_LINEAGE) + $(built_module): PRIVATE_CERTIFICATE_LINEAGE := $(LOCAL_CERTIFICATE_LINEAGE) + + $(built_module): PRIVATE_ROTATION_MIN_SDK_VERSION := $(LOCAL_ROTATION_MIN_SDK_VERSION) +endif + +ifneq ($(LOCAL_MODULE_STEM),) + PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM) +else + PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE) +endif + +include $(BUILD_SYSTEM)/app_certificate_validate.mk + +# Set a actual_partition_tag (calculated in base_rules.mk) for the package. +PACKAGES.$(LOCAL_MODULE).PARTITION := $(actual_partition_tag) + +# Disable dex-preopt of prebuilts to save space, if requested. +ifndef LOCAL_DEX_PREOPT +ifeq ($(DONT_DEXPREOPT_PREBUILTS),true) +LOCAL_DEX_PREOPT := false +endif +endif + +# If the module is a compressed module, we don't pre-opt it because its final +# installation location will be the data partition. +ifdef LOCAL_COMPRESSED_MODULE +LOCAL_DEX_PREOPT := false +endif + +my_dex_jar := $(my_prebuilt_src_file) +dex_preopt_profile_src_file := $(my_prebuilt_src_file) + +####################################### +# defines built_odex along with rule to install odex +my_manifest_or_apk := $(my_prebuilt_src_file) +include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk +my_manifest_or_apk := +####################################### +ifneq ($(LOCAL_REPLACE_PREBUILT_APK_INSTALLED),) +# There is a replacement for the prebuilt .apk we can install without any processing. +$(built_module) : $(LOCAL_REPLACE_PREBUILT_APK_INSTALLED) + $(transform-prebuilt-to-target) + +else # ! LOCAL_REPLACE_PREBUILT_APK_INSTALLED + +# If the SDK version is 30 or higher, the apk is signed with a v2+ scheme. +# Altering it will invalidate the signature. Just do error checks instead. +do_not_alter_apk := +ifeq (PRESIGNED,$(LOCAL_CERTIFICATE)) + ifneq (,$(LOCAL_SDK_VERSION)) + ifeq ($(call math_is_number,$(LOCAL_SDK_VERSION)),true) + ifeq ($(call math_gt,$(LOCAL_SDK_VERSION),29),true) + do_not_alter_apk := true + endif + endif + # TODO: Add system_current after fixing the existing modules. + ifneq ($(filter current test_current core_current,$(LOCAL_SDK_VERSION)),) + do_not_alter_apk := true + endif + endif +endif + +ifeq ($(do_not_alter_apk),true) +$(built_module) : $(my_prebuilt_src_file) | $(ZIPALIGN) + $(transform-prebuilt-to-target) + $(check-jni-dex-compression) + $(check-package-alignment) +else +# Sign and align non-presigned .apks. +# The embedded prebuilt jni to uncompress. +ifeq ($(LOCAL_CERTIFICATE),PRESIGNED) +# For PRESIGNED apks we must uncompress every .so file: +# even if the .so file isn't for the current TARGET_ARCH, +# we can't strip the file. +embedded_prebuilt_jni_libs := +endif +ifndef embedded_prebuilt_jni_libs +# No LOCAL_PREBUILT_JNI_LIBS, uncompress all. +embedded_prebuilt_jni_libs := +endif +$(built_module): PRIVATE_EMBEDDED_JNI_LIBS := $(embedded_prebuilt_jni_libs) + +ifdef LOCAL_COMPRESSED_MODULE +$(built_module) : $(MINIGZIP) +endif + +ifeq ($(module_run_appcompat),true) +$(built_module) : $(appcompat-files) +$(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE) +endif + +ifeq ($(module_run_appcompat),true) +$(built_module) : $(AAPT2) +endif +$(built_module) : $(my_prebuilt_src_file) | $(ZIPALIGN) $(ZIP2ZIP) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH) + $(transform-prebuilt-to-target) + $(uncompress-prebuilt-embedded-jni-libs) + $(remove-unwanted-prebuilt-embedded-jni-libs) +ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) + $(uncompress-dexs) +endif # LOCAL_UNCOMPRESS_DEX +ifneq ($(LOCAL_CERTIFICATE),PRESIGNED) +ifeq ($(module_run_appcompat),true) + $(call appcompat-header, aapt2) + $(run-appcompat) +endif # module_run_appcompat + $(sign-package) + # No need for align-package because sign-package takes care of alignment +else # LOCAL_CERTIFICATE == PRESIGNED + $(align-package) +endif # LOCAL_CERTIFICATE +ifdef LOCAL_COMPRESSED_MODULE + $(compress-package) +endif # LOCAL_COMPRESSED_MODULE +endif # ! do_not_alter_apk +endif # ! LOCAL_REPLACE_PREBUILT_APK_INSTALLED + + +############################### +## Install split apks. +ifdef LOCAL_PACKAGE_SPLITS +ifdef LOCAL_COMPRESSED_MODULE +$(error $(LOCAL_MODULE): LOCAL_COMPRESSED_MODULE is not currently supported for split installs) +endif # LOCAL_COMPRESSED_MODULE + +# LOCAL_PACKAGE_SPLITS is a list of apks to be installed. +built_apk_splits := $(addprefix $(intermediates)/,$(notdir $(LOCAL_PACKAGE_SPLITS))) +installed_apk_splits := $(addprefix $(my_module_path)/,$(notdir $(LOCAL_PACKAGE_SPLITS))) + +# Rules to sign the split apks. +my_src_dir := $(sort $(dir $(LOCAL_PACKAGE_SPLITS))) +ifneq (1,$(words $(my_src_dir))) +$(error You must put all the split source apks in the same folder: $(LOCAL_PACKAGE_SPLITS)) +endif +my_src_dir := $(LOCAL_PATH)/$(my_src_dir) + +$(built_apk_splits) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem | $(ZIPALIGN) $(ZIP2ZIP) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH) +$(built_apk_splits) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 +$(built_apk_splits) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem +$(built_apk_splits) : $(intermediates)/%.apk : $(my_src_dir)/%.apk + $(copy-file-to-new-target) + $(sign-package) + +# Rules to install the split apks. +$(installed_apk_splits) : $(my_module_path)/%.apk : $(intermediates)/%.apk + @echo "Install: $@" + $(copy-file-to-new-target) + +# Register the additional built and installed files. +ALL_MODULES.$(my_register_name).INSTALLED += $(installed_apk_splits) +ALL_MODULES.$(my_register_name).BUILT_INSTALLED += \ + $(foreach s,$(LOCAL_PACKAGE_SPLITS),$(intermediates)/$(notdir $(s)):$(my_module_path)/$(notdir $(s))) + +# Make sure to install the splits when you run "make ". +$(my_all_targets): $(installed_apk_splits) + +endif # LOCAL_PACKAGE_SPLITS + diff --git a/make/core/artifact_path_requirements.mk b/make/core/artifact_path_requirements.mk new file mode 100644 index 0000000..566b9f7 --- /dev/null +++ b/make/core/artifact_path_requirements.mk @@ -0,0 +1,64 @@ +# This file contains logic to enforce artifact path requirements +# defined in product makefiles. + +# Fakes don't get installed, and NDK stubs aren't installed to device. +static_allowed_patterns := $(TARGET_OUT_FAKE)/% $(SOONG_OUT_DIR)/ndk/% +# RROs become REQUIRED by the source module, but are always placed on the vendor partition. +static_allowed_patterns += %__auto_generated_rro_product.apk +static_allowed_patterns += %__auto_generated_rro_vendor.apk +# Auto-included targets are not considered +static_allowed_patterns += $(call product-installed-files,) +# $(PRODUCT_OUT)/apex is where shared libraries in APEXes get installed. +# The path can be considered as a fake path, as the shared libraries +# are installed there just to have symbols files for them under +# $(PRODUCT_OUT)/symbols/apex for debugging purpose. The /apex directory +# is never compiled into a filesystem image. +static_allowed_patterns += $(PRODUCT_OUT)/apex/% +ifeq (true,$(BOARD_USES_SYSTEM_OTHER_ODEX)) + # Allow system_other odex space optimization. + static_allowed_patterns += \ + $(TARGET_OUT_SYSTEM_OTHER)/%.odex \ + $(TARGET_OUT_SYSTEM_OTHER)/%.vdex \ + $(TARGET_OUT_SYSTEM_OTHER)/%.art +endif + +ifneq (,$(filter-out true false relaxed strict,$(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS))$(filter-out 1 0,$(words $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)))) + $(error PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS must be one of [true, false, relaxed, strict], found: $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)) +endif + +all_offending_files := +$(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\ + $(eval requirements := $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENTS)) \ + $(eval ### Verify that the product only produces files inside its path requirements.) \ + $(eval allowed := $(PRODUCTS.$(makefile).ARTIFACT_PATH_ALLOWED_LIST)) \ + $(eval path_patterns := $(call resolve-product-relative-paths,$(requirements),%)) \ + $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \ + $(eval files := $(call product-installed-files, $(makefile))) \ + $(eval offending_files := $(filter-out $(path_patterns) $(allowed_patterns) $(static_allowed_patterns),$(files))) \ + $(call maybe-print-list-and-error,$(offending_files),\ + $(makefile) produces files outside its artifact path requirement. \ + Allowed paths are $(subst $(space),$(comma)$(space),$(addsuffix *,$(requirements)))) \ + $(eval unused_allowed := $(filter-out $(files),$(allowed_patterns))) \ + $(if $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENT_IS_RELAXED),, \ + $(call maybe-print-list-and-error,$(unused_allowed),$(makefile) includes redundant allowed entries in its artifact path requirement.) \ + ) \ + $(eval ### Optionally verify that nothing else produces files inside this artifact path requirement.) \ + $(eval extra_files := $(filter-out $(files) $(HOST_OUT)/%,$(product_target_FILES))) \ + $(eval files_in_requirement := $(filter $(path_patterns),$(extra_files))) \ + $(eval all_offending_files += $(files_in_requirement)) \ + $(eval allowed := $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST)) \ + $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \ + $(eval offending_files := $(filter-out $(allowed_patterns),$(files_in_requirement))) \ + $(eval enforcement := $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)) \ + $(if $(filter-out false,$(enforcement)),\ + $(call maybe-print-list-and-error,$(offending_files),\ + $(INTERNAL_PRODUCT) produces files inside $(makefile)s artifact path requirement. \ + $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT)) \ + $(eval unused_allowed := $(if $(filter true strict,$(enforcement)),\ + $(foreach p,$(allowed_patterns),$(if $(filter $(p),$(extra_files)),,$(p))))) \ + $(call maybe-print-list-and-error,$(unused_allowed),$(INTERNAL_PRODUCT) includes redundant artifact path requirement allowed list entries.) \ + ) \ +) +$(PRODUCT_OUT)/offending_artifacts.txt: + rm -f $@ + $(foreach f,$(sort $(all_offending_files)),echo $(f) >> $@;) diff --git a/make/core/autogen_test_config.mk b/make/core/autogen_test_config.mk new file mode 100644 index 0000000..137b118 --- /dev/null +++ b/make/core/autogen_test_config.mk @@ -0,0 +1,77 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This build rule allows TradeFed test config file to be created based on +# following inputs: +# is_native: If the test is a native test. +# full_android_manifest: Name of the AndroidManifest file for the test. +# Output: +# autogen_test_config_file: Path to the test config file generated. + +autogen_test_config_file := $(dir $(LOCAL_BUILT_MODULE))$(LOCAL_MODULE).config +# TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base. +autogen_test_install_base := /data/local/tmp +# Automatically setup test root for native test. +ifeq (true,$(is_native)) + ifeq (true,$(LOCAL_VENDOR_MODULE)) + autogen_test_install_base = /data/local/tests/vendor + endif + ifeq (true,$(LOCAL_USE_VNDK)) + autogen_test_install_base = /data/local/tests/vendor + endif +endif +ifeq (true,$(is_native)) +ifeq ($(LOCAL_NATIVE_BENCHMARK),true) +autogen_test_config_template := $(NATIVE_BENCHMARK_TEST_CONFIG_TEMPLATE) +else + ifeq ($(LOCAL_IS_HOST_MODULE),true) + autogen_test_config_template := $(NATIVE_HOST_TEST_CONFIG_TEMPLATE) + else + autogen_test_config_template := $(NATIVE_TEST_CONFIG_TEMPLATE) + endif +endif +# Auto generating test config file for native test +$(autogen_test_config_file): PRIVATE_TEST_INSTALL_BASE := $(autogen_test_install_base) +$(autogen_test_config_file): PRIVATE_MODULE_NAME := $(LOCAL_MODULE) +$(autogen_test_config_file) : $(autogen_test_config_template) + @echo "Auto generating test config $(notdir $@)" + $(hide) sed 's&{MODULE}&$(PRIVATE_MODULE_NAME)&g;s&{TEST_INSTALL_BASE}&$(PRIVATE_TEST_INSTALL_BASE)&g;s&{EXTRA_CONFIGS}&&g' $< > $@ +my_auto_generate_config := true +else +# Auto generating test config file for instrumentation test +ifneq (,$(full_android_manifest)) +$(autogen_test_config_file): PRIVATE_AUTOGEN_TEST_CONFIG_SCRIPT := $(AUTOGEN_TEST_CONFIG_SCRIPT) +$(autogen_test_config_file): PRIVATE_TEST_CONFIG_ANDROID_MANIFEST := $(full_android_manifest) +$(autogen_test_config_file): PRIVATE_EMPTY_TEST_CONFIG := $(EMPTY_TEST_CONFIG) +$(autogen_test_config_file): PRIVATE_TEMPLATE := $(INSTRUMENTATION_TEST_CONFIG_TEMPLATE) +$(autogen_test_config_file) : $(full_android_manifest) $(EMPTY_TEST_CONFIG) $(INSTRUMENTATION_TEST_CONFIG_TEMPLATE) $(AUTOGEN_TEST_CONFIG_SCRIPT) + @echo "Auto generating test config $(notdir $@)" + @rm -f $@ + $(hide) $(PRIVATE_AUTOGEN_TEST_CONFIG_SCRIPT) $@ $(PRIVATE_TEST_CONFIG_ANDROID_MANIFEST) $(PRIVATE_EMPTY_TEST_CONFIG) $(PRIVATE_TEMPLATE) +my_auto_generate_config := true +endif # ifneq (,$(full_android_manifest)) +endif # ifneq (true,$(is_native)) + +ifeq (true,$(my_auto_generate_config)) + LOCAL_INTERMEDIATE_TARGETS += $(autogen_test_config_file) + $(LOCAL_BUILT_MODULE): $(autogen_test_config_file) + ALL_MODULES.$(my_register_name).auto_test_config := true + $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_autogen := true +else + autogen_test_config_file := +endif + +my_auto_generate_config := diff --git a/make/core/base_rules.mk b/make/core/base_rules.mk new file mode 100644 index 0000000..7ea9b52 --- /dev/null +++ b/make/core/base_rules.mk @@ -0,0 +1,1206 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Catch users that directly include base_rules.mk +$(call record-module-type,base_rules) + +# Users can define base-rules-hook in their buildspec.mk to perform +# arbitrary operations as each module is included. +ifdef base-rules-hook +$(if $(base-rules-hook),) +endif + +########################################################### +## Common instructions for a generic module. +########################################################### + +LOCAL_MODULE := $(strip $(LOCAL_MODULE)) +ifeq ($(LOCAL_MODULE),) + $(error $(LOCAL_PATH): LOCAL_MODULE is not defined) +endif +$(call verify-module-name) + +my_test_data := +my_test_config := + +LOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE)) +ifdef LOCAL_IS_HOST_MODULE + ifneq ($(LOCAL_IS_HOST_MODULE),true) + $(error $(LOCAL_PATH): LOCAL_IS_HOST_MODULE must be "true" or empty, not "$(LOCAL_IS_HOST_MODULE)") + endif + ifeq ($(LOCAL_HOST_PREFIX),) + my_prefix := HOST_ + else + my_prefix := $(LOCAL_HOST_PREFIX) + endif + my_host := host- + my_kind := HOST +else + my_prefix := TARGET_ + my_kind := + my_host := +endif + +ifeq ($(my_prefix),HOST_CROSS_) + my_host_cross := true +else + my_host_cross := +endif + +ifeq (true, $(LOCAL_PRODUCT_MODULE)) +ifneq (,$(filter $(LOCAL_MODULE),$(PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION))) + LOCAL_PRODUCT_MODULE := +endif +endif + +_path := $(LOCAL_MODULE_PATH) $(LOCAL_MODULE_PATH_32) $(LOCAL_MODULE_PATH_64) +ifneq ($(filter $(TARGET_OUT_VENDOR)%,$(_path)),) +LOCAL_VENDOR_MODULE := true +else ifneq ($(filter $(TARGET_OUT_OEM)/%,$(_path)),) +LOCAL_OEM_MODULE := true +else ifneq ($(filter $(TARGET_OUT_ODM)/%,$(_path)),) +LOCAL_ODM_MODULE := true +else ifneq ($(filter $(TARGET_OUT_PRODUCT)/%,$(_path)),) +LOCAL_PRODUCT_MODULE := true +else ifneq ($(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(_path)),) +LOCAL_SYSTEM_EXT_MODULE := true +endif +_path := + +# TODO(b/135957588) Remove following workaround +# LOCAL_PRODUCT_SERVICES_MODULE to LOCAL_PRODUCT_MODULE for all Android.mk +ifndef LOCAL_PRODUCT_MODULE +LOCAL_PRODUCT_MODULE := $(LOCAL_PRODUCT_SERVICES_MODULE) +endif + +ifndef LOCAL_PROPRIETARY_MODULE + LOCAL_PROPRIETARY_MODULE := $(LOCAL_VENDOR_MODULE) +endif +ifndef LOCAL_VENDOR_MODULE + LOCAL_VENDOR_MODULE := $(LOCAL_PROPRIETARY_MODULE) +endif +ifneq ($(filter-out $(LOCAL_PROPRIETARY_MODULE),$(LOCAL_VENDOR_MODULE))$(filter-out $(LOCAL_VENDOR_MODULE),$(LOCAL_PROPRIETARY_MODULE)),) +$(call pretty-error,Only one of LOCAL_PROPRIETARY_MODULE[$(LOCAL_PROPRIETARY_MODULE)] and LOCAL_VENDOR_MODULE[$(LOCAL_VENDOR_MODULE)] may be set, or they must be equal) +endif + +ifeq ($(LOCAL_HOST_MODULE),true) +my_image_variant := host +else ifeq ($(LOCAL_VENDOR_MODULE),true) +my_image_variant := vendor +else ifeq ($(LOCAL_OEM_MODULE),true) +my_image_variant := vendor +else ifeq ($(LOCAL_ODM_MODULE),true) +my_image_variant := vendor +else ifeq ($(LOCAL_PRODUCT_MODULE),true) +my_image_variant := product +else +my_image_variant := core +endif + +non_system_module := $(filter true, \ + $(LOCAL_PRODUCT_MODULE) \ + $(LOCAL_SYSTEM_EXT_MODULE) \ + $(LOCAL_VENDOR_MODULE) \ + $(LOCAL_PROPRIETARY_MODULE)) + +include $(BUILD_SYSTEM)/local_vndk.mk +include $(BUILD_SYSTEM)/local_systemsdk.mk +include $(BUILD_SYSTEM)/local_current_sdk.mk + +my_module_tags := $(LOCAL_MODULE_TAGS) +ifeq ($(my_host_cross),true) + my_module_tags := +endif + +# Ninja has an implicit dependency on the command being run, and kati will +# regenerate the ninja manifest if any read makefile changes, so there is no +# need to have dependencies on makefiles. +# This won't catch all the cases where LOCAL_ADDITIONAL_DEPENDENCIES contains +# a .mk file, because a few users of LOCAL_ADDITIONAL_DEPENDENCIES don't include +# base_rules.mk, but it will fix the most common ones. +LOCAL_ADDITIONAL_DEPENDENCIES := $(filter-out %.mk,$(LOCAL_ADDITIONAL_DEPENDENCIES)) + +my_bad_deps := $(strip $(foreach dep,$(filter-out | ||,$(LOCAL_ADDITIONAL_DEPENDENCIES)),\ + $(if $(findstring /,$(dep)),,$(dep)))) +ifneq ($(my_bad_deps),) +$(call pretty-warning,"Bad LOCAL_ADDITIONAL_DEPENDENCIES: $(my_bad_deps)") +$(call pretty-error,"LOCAL_ADDITIONAL_DEPENDENCIES must only contain paths (not module names)") +endif + +########################################################### +## Validate and define fallbacks for input LOCAL_* variables. +########################################################### + +## Dump a .csv file of all modules and their tags +#ifneq ($(tag-list-first-time),false) +#$(shell rm -f tag-list.csv) +#tag-list-first-time := false +#endif +#$(shell echo $(lastword $(filter-out config/% out/%,$(MAKEFILE_LIST))),$(LOCAL_MODULE),$(strip $(LOCAL_MODULE_CLASS)),$(subst $(space),$(comma),$(sort $(my_module_tags))) >> tag-list.csv) + +LOCAL_UNINSTALLABLE_MODULE := $(strip $(LOCAL_UNINSTALLABLE_MODULE)) +my_module_tags := $(sort $(my_module_tags)) +ifeq (,$(my_module_tags)) + my_module_tags := optional +endif + +# User tags are not allowed anymore. Fail early because it will not be installed +# like it used to be. +ifneq ($(filter $(my_module_tags),user),) + $(warning *** Module name: $(LOCAL_MODULE)) + $(warning *** Makefile location: $(LOCAL_MODULE_MAKEFILE)) + $(warning * ) + $(warning * Module is attempting to use the 'user' tag. This) + $(warning * used to cause the module to be installed automatically.) + $(warning * Now, the module must be listed in the PRODUCT_PACKAGES) + $(warning * section of a product makefile to have it installed.) + $(warning * ) + $(error user tag detected on module.) +endif + +my_bad_module_tags := $(filter eng debug,$(my_module_tags)) +ifdef my_bad_module_tags + ifeq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + $(call pretty-warning,LOCAL_MODULE_TAGS := $(my_bad_module_tags) does not do anything for uninstallable modules) + endif + $(call pretty-error,LOCAL_MODULE_TAGS := $(my_bad_module_tags) is obsolete. See $(CHANGES_URL)#LOCAL_MODULE_TAGS) +endif + +# Only the tags mentioned in this test are expected to be set by module +# makefiles. Anything else is either a typo or a source of unexpected +# behaviors. +ifneq ($(filter-out tests optional samples,$(my_module_tags)),) +$(call pretty-error,unusual tags: $(filter-out tests optional samples,$(my_module_tags))) +endif + +# Add implicit tags. +# +# If the local directory or one of its parents contains a MODULE_LICENSE_GPL +# file, tag the module as "gnu". Search for "*_GPL*", "*_LGPL*" and "*_MPL*" +# so that we can also find files like MODULE_LICENSE_GPL_AND_AFL +# +gpl_license_file := $(call find-parent-file,$(LOCAL_PATH),MODULE_LICENSE*_GPL* MODULE_LICENSE*_MPL* MODULE_LICENSE*_LGPL*) +ifneq ($(gpl_license_file),) + my_module_tags += gnu + ALL_GPL_MODULE_LICENSE_FILES += $(gpl_license_file) +endif + +LOCAL_MODULE_CLASS := $(strip $(LOCAL_MODULE_CLASS)) +ifneq ($(words $(LOCAL_MODULE_CLASS)),1) + $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must contain exactly one word, not "$(LOCAL_MODULE_CLASS)") +endif + +my_32_64_bit_suffix := $(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32) + +ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) +my_multilib_module_path := $(strip $(LOCAL_MODULE_PATH_$(my_32_64_bit_suffix))) +ifdef my_multilib_module_path +my_module_path := $(my_multilib_module_path) +else +my_module_path := $(strip $(LOCAL_MODULE_PATH)) +endif +my_module_path := $(patsubst %/,%,$(my_module_path)) +my_module_relative_path := $(strip $(LOCAL_MODULE_RELATIVE_PATH)) + +ifdef LOCAL_IS_HOST_MODULE + partition_tag := + actual_partition_tag := +else +ifeq (true,$(strip $(LOCAL_VENDOR_MODULE))) + partition_tag := _VENDOR + # A vendor module could be on the vendor partition at "vendor" or the system + # partition at "system/vendor". + actual_partition_tag := $(if $(filter true,$(BOARD_USES_VENDORIMAGE)),vendor,system) +else ifeq (true,$(strip $(LOCAL_OEM_MODULE))) + partition_tag := _OEM + actual_partition_tag := oem +else ifeq (true,$(strip $(LOCAL_ODM_MODULE))) + partition_tag := _ODM + # An ODM module could be on the odm partition at "odm", the vendor partition + # at "vendor/odm", or the system partition at "system/vendor/odm". + actual_partition_tag := $(if $(filter true,$(BOARD_USES_ODMIMAGE)),odm,$(if $(filter true,$(BOARD_USES_VENDORIMAGE)),vendor,system)) +else ifeq (true,$(strip $(LOCAL_PRODUCT_MODULE))) + partition_tag := _PRODUCT + # A product module could be on the product partition at "product" or the + # system partition at "system/product". + actual_partition_tag := $(if $(filter true,$(BOARD_USES_PRODUCTIMAGE)),product,system) +else ifeq (true,$(strip $(LOCAL_SYSTEM_EXT_MODULE))) + partition_tag := _SYSTEM_EXT + # A system_ext-specific module could be on the system_ext partition at + # "system_ext" or the system partition at "system/system_ext". + actual_partition_tag := $(if $(filter true,$(BOARD_USES_SYSTEM_EXTIMAGE)),system_ext,system) +else ifeq (NATIVE_TESTS,$(LOCAL_MODULE_CLASS)) + partition_tag := _DATA + actual_partition_tag := data +else + # The definition of should-install-to-system will be different depending + # on which goal (e.g., sdk or just droid) is being built. + partition_tag := $(if $(call should-install-to-system,$(my_module_tags)),,_DATA) + actual_partition_tag := $(if $(partition_tag),data,system) +endif +endif +# For test modules that lack a suite tag, set null-suite as the default. +# We only support adding a default suite to native tests, native benchmarks, and instrumentation tests. +# This is because they are the only tests we currently auto-generate test configs for. +ifndef LOCAL_COMPATIBILITY_SUITE + ifneq ($(filter NATIVE_TESTS NATIVE_BENCHMARK, $(LOCAL_MODULE_CLASS)),) + LOCAL_COMPATIBILITY_SUITE := null-suite + endif + ifneq ($(filter APPS, $(LOCAL_MODULE_CLASS)),) + ifneq ($(filter $(my_module_tags),tests),) + LOCAL_COMPATIBILITY_SUITE := null-suite + endif + endif +endif + +use_testcase_folder := +ifeq ($(my_module_path),) + ifneq ($(LOCAL_MODULE),$(filter $(LOCAL_MODULE),$(DEFAULT_DATA_OUT_MODULES))) + ifdef LOCAL_COMPATIBILITY_SUITE + ifneq (true, $(LOCAL_IS_HOST_MODULE)) + use_testcase_folder := true + endif + endif + endif +endif + +ifeq ($(LOCAL_IS_UNIT_TEST),true) + ifeq ($(LOCAL_IS_HOST_MODULE),true) + LOCAL_COMPATIBILITY_SUITE += host-unit-tests + endif +endif + +ifeq ($(my_module_path),) + install_path_var := $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT$(partition_tag)_$(LOCAL_MODULE_CLASS) + ifeq (true,$(LOCAL_PRIVILEGED_MODULE)) + install_path_var := $(install_path_var)_PRIVILEGED + endif + + my_module_path := $($(install_path_var)) + + # If use_testcase_folder be set, and LOCAL_MODULE_PATH not set, + # overwrite the default path under testcase. + ifeq ($(use_testcase_folder),true) + arch_dir := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) + testcase_folder := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)/$(arch_dir) + my_module_path := $(testcase_folder) + arch_dir := + endif + + ifeq ($(strip $(my_module_path)),) + $(error $(LOCAL_PATH): unhandled install path "$(install_path_var) for $(LOCAL_MODULE)") + endif +endif +ifneq ($(my_module_relative_path),) + my_module_path := $(my_module_path)/$(my_module_relative_path) +endif +endif # not LOCAL_UNINSTALLABLE_MODULE + +ifneq ($(strip $(LOCAL_BUILT_MODULE)$(LOCAL_INSTALLED_MODULE)),) + $(error $(LOCAL_PATH): LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles) +endif + +my_register_name := $(LOCAL_MODULE) +ifeq ($(my_host_cross),true) + my_register_name := host_cross_$(LOCAL_MODULE) +endif +ifdef LOCAL_2ND_ARCH_VAR_PREFIX +ifndef LOCAL_NO_2ND_ARCH_MODULE_SUFFIX +my_register_name := $(my_register_name)$($(my_prefix)2ND_ARCH_MODULE_SUFFIX) +endif +endif + +ifeq ($(my_host_cross),true) + my_all_targets := host_cross_$(my_register_name)_all_targets +else ifneq ($(LOCAL_IS_HOST_MODULE),) + my_all_targets := host_$(my_register_name)_all_targets +else + my_all_targets := device_$(my_register_name)_all_targets +endif + +# Make sure that this IS_HOST/CLASS/MODULE combination is unique. +module_id := MODULE.$(if \ + $(LOCAL_IS_HOST_MODULE),$($(my_prefix)OS),TARGET).$(LOCAL_MODULE_CLASS).$(my_register_name) +ifdef $(module_id) +$(error $(LOCAL_PATH): $(module_id) already defined by $($(module_id))) +endif +$(module_id) := $(LOCAL_PATH) + +# These are the same as local-intermediates-dir / local-generated-sources dir, but faster +intermediates.COMMON := $($(my_prefix)OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates +ifneq (,$(filter $(my_prefix)$(LOCAL_MODULE_CLASS),$(COMMON_MODULE_CLASSES))) + intermediates := $($(my_prefix)OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates + generated_sources_dir := $($(my_prefix)OUT_COMMON_GEN)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates +else + ifneq (,$(filter $(LOCAL_MODULE_CLASS),$(PER_ARCH_MODULE_CLASSES))) + intermediates := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates + else + intermediates := $($(my_prefix)OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates + endif + generated_sources_dir := $($(my_prefix)OUT_GEN)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates +endif + +ifneq ($(LOCAL_OVERRIDES_MODULES),) + ifndef LOCAL_IS_HOST_MODULE + ifeq ($(LOCAL_MODULE_CLASS),EXECUTABLES) + EXECUTABLES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES)) + else ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) + SHARED_LIBRARIES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES)) + else ifeq ($(LOCAL_MODULE_CLASS),ETC) + ETC.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES)) + else + $(call pretty-error,LOCAL_MODULE_CLASS := $(LOCAL_MODULE_CLASS) cannot use LOCAL_OVERRIDES_MODULES) + endif + else + $(call pretty-error,host modules cannot use LOCAL_OVERRIDES_MODULES) + endif +endif + +########################################################### +# Pick a name for the intermediate and final targets +########################################################### +include $(BUILD_SYSTEM)/configure_module_stem.mk + +LOCAL_BUILT_MODULE := $(intermediates)/$(my_built_module_stem) + +ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) + ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error, LOCAL_SOONG_INSTALLED_MODULE can only be used from $(SOONG_ANDROID_MK)) + endif + # Use the install path requested by Soong. + LOCAL_INSTALLED_MODULE := $(LOCAL_SOONG_INSTALLED_MODULE) +else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + # Apk and its attachments reside in its own subdir. + ifeq ($(LOCAL_MODULE_CLASS),APPS) + # framework-res.apk doesn't like the additional layer. + ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) + # Neither do Runtime Resource Overlay apks, which contain just the overlaid resources. + else ifeq ($(LOCAL_IS_RUNTIME_RESOURCE_OVERLAY),true) + else + ifneq ($(use_testcase_folder),true) + my_module_path := $(my_module_path)/$(LOCAL_MODULE) + endif + endif + endif + LOCAL_INSTALLED_MODULE := $(my_module_path)/$(my_installed_module_stem) +endif + +# Assemble the list of targets to create PRIVATE_ variables for. +LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE) + +########################################################### +## Create .toc files from shared objects to reduce unnecessary rebuild +# .toc files have the list of external dynamic symbols without their addresses. +# As .KATI_RESTAT is specified to .toc files and commit-change-for-toc is used, +# dependent binaries of a .toc file will be rebuilt only when the content of +# the .toc file is changed. +# +# Don't create .toc files for Soong shared libraries, that is handled in +# Soong and soong_cc_prebuilt.mk +########################################################### +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) +ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) +LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE).toc +$(LOCAL_BUILT_MODULE).toc: $(LOCAL_BUILT_MODULE) + $(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@.tmp) + $(call commit-change-for-toc,$@) + +# Kati adds restat=1 to ninja. GNU make does nothing for this. +.KATI_RESTAT: $(LOCAL_BUILT_MODULE).toc +# Build .toc file when using mm, mma, or make $(my_register_name) +$(my_all_targets): $(LOCAL_BUILT_MODULE).toc +endif +endif + +########################################################### +## logtags: Add .logtags files to global list +########################################################### + +logtags_sources := $(filter %.logtags,$(LOCAL_SRC_FILES)) $(LOCAL_LOGTAGS_FILES) + +ifneq ($(strip $(logtags_sources)),) +event_log_tags := $(foreach f,$(addprefix $(LOCAL_PATH)/,$(logtags_sources)),$(call clean-path,$(f))) +else +event_log_tags := +endif + +########################################################### +## make clean- targets +########################################################### +cleantarget := clean-$(my_register_name) +.PHONY: $(cleantarget) +$(cleantarget) : PRIVATE_MODULE := $(my_register_name) +$(cleantarget) : PRIVATE_CLEAN_FILES := \ + $(LOCAL_BUILT_MODULE) \ + $(LOCAL_INSTALLED_MODULE) \ + $(intermediates) +$(cleantarget):: + @echo "Clean: $(PRIVATE_MODULE)" + $(hide) rm -rf $(PRIVATE_CLEAN_FILES) + +########################################################### +## Common definitions for module. +########################################################### +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PATH:=$(LOCAL_PATH) +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE) +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host) +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PREFIX := $(my_prefix) + +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_INTERMEDIATES_DIR:= $(intermediates) +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX) + +# Tell the module and all of its sub-modules who it is. +$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_MODULE:= $(my_register_name) + +# Provide a short-hand for building this module. +# We name both BUILT and INSTALLED in case +# LOCAL_UNINSTALLABLE_MODULE is set. +.PHONY: $(my_all_targets) +$(my_all_targets): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE) $(LOCAL_ADDITIONAL_CHECKED_MODULE) + +.PHONY: $(my_register_name) +$(my_register_name): $(my_all_targets) + +ifneq ($(my_register_name),$(LOCAL_MODULE)) +# $(LOCAL_MODULE) covers all the multilib targets. +.PHONY: $(LOCAL_MODULE) +$(LOCAL_MODULE) : $(my_all_targets) +endif + +# Set up phony targets that covers all modules under the given paths. +# This allows us to build everything in given paths by running mmma/mma. +define my_path_comp +parent := $(patsubst %/,%,$(dir $(1))) +parent_target := MODULES-IN-$$(subst /,-,$$(parent)) +.PHONY: $$(parent_target) +$$(parent_target): $(2) +ifndef $$(parent_target) + $$(parent_target) := true + ifneq (,$$(findstring /,$$(parent))) + $$(eval $$(call my_path_comp,$$(parent),$$(parent_target))) + endif +endif +endef + +_local_path := $(patsubst %/,%,$(LOCAL_PATH)) +_local_path_target := MODULES-IN-$(subst /,-,$(_local_path)) + +.PHONY: $(_local_path_target) +$(_local_path_target): $(my_register_name) + +ifndef $(_local_path_target) + $(_local_path_target) := true + ifneq (,$(findstring /,$(_local_path))) + $(eval $(call my_path_comp,$(_local_path),$(_local_path_target))) + endif +endif + +_local_path := +_local_path_target := +my_path_comp := + +########################################################### +## Module installation rule +########################################################### + +my_installed_symlinks := + +ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) + # Soong already generated the copy rule, but make the installed location depend on the Make + # copy of the intermediates for now, as some rules that collect intermediates may expect + # them to exist. + $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE) + + $(foreach symlink, $(LOCAL_SOONG_INSTALL_SYMLINKS), \ + $(call declare-0p-target,$(symlink))) + $(my_all_targets) : | $(LOCAL_SOONG_INSTALL_SYMLINKS) +else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD) + $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE) + @echo "Install: $@" + ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(copy-file-or-link-to-new-target) + else + $(copy-file-to-new-target) + endif + $(PRIVATE_POST_INSTALL_CMD) + + # Rule to install the module's companion symlinks + my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix))) + $(foreach symlink,$(my_installed_symlinks),\ + $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink))\ + $(call declare-0p-target,$(symlink))) + + $(my_all_targets) : | $(my_installed_symlinks) + +endif # !LOCAL_UNINSTALLABLE_MODULE + +########################################################### +## VINTF manifest fragment and init.rc goals +########################################################### + +my_vintf_installed:= +my_vintf_path:= +my_vintf_pairs:= +my_init_rc_installed := +my_init_rc_path := +my_init_rc_pairs := +ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + ifndef LOCAL_IS_HOST_MODULE + # Rule to install the module's companion vintf fragments. + ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),) + my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS) + else + my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml)) + endif + ifneq ($(strip $(my_vintf_fragments)),) + # Make doesn't support recovery as an output partition, but some Soong modules installed in recovery + # have init.rc files that need to be installed alongside them. Manually handle the case where the + # output file is in the recovery partition. + my_vintf_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC)) + my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(my_vintf_path)/vintf/manifest/$(notdir $(xml))) + my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml))) + + # Only set up copy rules once, even if another arch variant shares it + my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs)) + my_vintf_new_installed := $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs)) + + ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs) + + $(my_all_targets) : $(my_vintf_new_installed) + endif # my_vintf_fragments + + # Rule to install the module's companion init.rc. + ifneq ($(strip $(LOCAL_FULL_INIT_RC)),) + my_init_rc := $(LOCAL_FULL_INIT_RC) + else + my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc)) + endif + ifneq ($(strip $(my_init_rc)),) + # Make doesn't support recovery or ramdisk as an output partition, + # but some Soong modules installed in recovery or ramdisk + # have init.rc files that need to be installed alongside them. + # Manually handle the case where the + # output file is in the recovery or ramdisk partition. + ifneq (,$(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path))) + my_init_rc_path := $(TARGET_RECOVERY_ROOT_OUT)/system/etc + else ifneq (,$(filter $(TARGET_RAMDISK_OUT)/%,$(my_module_path))) + my_init_rc_path := $(TARGET_RAMDISK_OUT)/system/etc + else + my_init_rc_path := $(TARGET_OUT$(partition_tag)_ETC) + endif + my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc))) + my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc))) + + # Make sure we only set up the copy rules once, even if another arch variant + # shares a common LOCAL_INIT_RC. + my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs)) + my_init_rc_new_installed := $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs)) + + ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs) + + $(my_all_targets) : $(my_init_rc_installed) + endif # my_init_rc + + endif # !LOCAL_IS_HOST_MODULE +endif # !LOCAL_UNINSTALLABLE_MODULE + +########################################################### +## CHECK_BUILD goals +########################################################### +my_checked_module := +# If nobody has defined a more specific module for the +# checked modules, use LOCAL_BUILT_MODULE. +ifdef LOCAL_CHECKED_MODULE + my_checked_module := $(LOCAL_CHECKED_MODULE) +else + my_checked_module := $(LOCAL_BUILT_MODULE) +endif + +my_checked_module += $(LOCAL_ADDITIONAL_CHECKED_MODULE) + +# If they request that this module not be checked, then don't. +# PLEASE DON'T SET THIS. ANY PLACES THAT SET THIS WITHOUT +# GOOD REASON WILL HAVE IT REMOVED. +ifdef LOCAL_DONT_CHECK_MODULE + my_checked_module := +endif +# Don't check build target module defined for the 2nd arch +ifndef LOCAL_IS_HOST_MODULE +ifdef LOCAL_2ND_ARCH_VAR_PREFIX + my_checked_module := +endif +endif + +########################################################### +## Test Data +########################################################### +my_test_data_pairs := +my_installed_test_data := +# Source to relative dst file paths for reuse in LOCAL_COMPATIBILITY_SUITE. +my_test_data_file_pairs := + +ifneq ($(strip $(filter NATIVE_TESTS,$(LOCAL_MODULE_CLASS)) $(LOCAL_IS_FUZZ_TARGET)),) +ifneq ($(strip $(LOCAL_TEST_DATA)),) +ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + +# Soong LOCAL_TEST_DATA is of the form :: +# or :, to be installed to +# // or /, +# respectively. +ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + define copy_test_data_pairs + _src_base := $$(call word-colon,1,$$(td)) + _file := $$(call word-colon,2,$$(td)) + _relative_install_path := $$(call word-colon,3,$$(td)) + ifeq (,$$(_relative_install_path)) + _relative_dest_file := $$(_file) + else + _relative_dest_file := $$(call append-path,$$(_relative_install_path),$$(_file)) + endif + my_test_data_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(call append-path,$$(my_module_path),$$(_relative_dest_file)) + my_test_data_file_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(_relative_dest_file) + endef +else + define copy_test_data_pairs + _src_base := $$(call word-colon,1,$$(td)) + _file := $$(call word-colon,2,$$(td)) + ifndef _file + _file := $$(_src_base) + _src_base := $$(LOCAL_PATH) + endif + ifneq (,$$(findstring ..,$$(_file))) + $$(call pretty-error,LOCAL_TEST_DATA may not include '..': $$(_file)) + endif + ifneq (,$$(filter/%,$$(_src_base) $$(_file))) + $$(call pretty-error,LOCAL_TEST_DATA may not include absolute paths: $$(_src_base) $$(_file)) + endif + my_test_data_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(call append-path,$$(my_module_path),$$(_file)) + my_test_data_file_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(_file) + endef +endif + +$(foreach td,$(LOCAL_TEST_DATA),$(eval $(copy_test_data_pairs))) + +copy_test_data_pairs := + +my_installed_test_data := $(call copy-many-files,$(my_test_data_pairs)) +$(LOCAL_INSTALLED_MODULE): $(my_installed_test_data) + +endif +endif +endif + +########################################################### +## Compatibility suite files. +########################################################### +ifdef LOCAL_COMPATIBILITY_SUITE +ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + +# If we are building a native test or benchmark and its stem variants are not defined, +# separate the multiple architectures into subdirectories of the testcase folder. +arch_dir := +is_native := +multi_arch := +ifeq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) + is_native := true + multi_arch := true +endif +ifdef LOCAL_MULTILIB + multi_arch := true +# These conditionals allow this functionality to be mimicked in Soong +else ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) + multi_arch := true + endif +endif + +ifdef multi_arch +arch_dir := /$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) +else +ifeq ($(use_testcase_folder),true) + arch_dir := /$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) +endif +endif + +multi_arch := + +my_default_test_module := +my_default_test_module := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(arch_dir)/$(my_installed_module_stem) +ifneq ($(LOCAL_INSTALLED_MODULE),$(my_default_test_module)) +# Install into the testcase folder +$(LOCAL_INSTALLED_MODULE) : $(my_default_test_module) +endif + +# The module itself. +$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_$(suite) := $(patsubst %:$(LOCAL_INSTALLED_MODULE),$(LOCAL_INSTALLED_MODULE):$(LOCAL_INSTALLED_MODULE),\ + $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ + $(LOCAL_BUILT_MODULE):$(dir)/$(my_installed_module_stem)))) \ + $(eval my_compat_dist_config_$(suite) := )) + + +# Auto-generate build config. +ifneq (,$(LOCAL_FULL_TEST_CONFIG)) + test_config := $(LOCAL_FULL_TEST_CONFIG) +else ifneq (,$(LOCAL_TEST_CONFIG)) + test_config := $(LOCAL_PATH)/$(LOCAL_TEST_CONFIG) +else + test_config := $(wildcard $(LOCAL_PATH)/AndroidTest.xml) +endif +ifeq (,$(test_config)) + ifneq (true,$(is_native)) + is_instrumentation_test := true + ifeq (true, $(LOCAL_IS_HOST_MODULE)) + is_instrumentation_test := false + endif + # If LOCAL_MODULE_CLASS is not APPS, it's certainly not an instrumentation + # test. However, some packages for test data also have LOCAL_MODULE_CLASS + # set to APPS. These will require flag LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG + # to disable auto-generating test config file. + ifneq (APPS, $(LOCAL_MODULE_CLASS)) + is_instrumentation_test := false + endif + endif + # CTS modules can be used for test data, so test config files must be + # explicitly created using AndroidTest.xml + ifeq (,$(filter cts, $(LOCAL_COMPATIBILITY_SUITE))) + ifneq (true, $(LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG)) + ifeq (true, $(filter true,$(is_native) $(is_instrumentation_test))) + include $(BUILD_SYSTEM)/autogen_test_config.mk + test_config := $(autogen_test_config_file) + autogen_test_config_file := + endif + endif + endif +endif +is_instrumentation_test := + +# Currently this flag variable is true only for the `android_test_helper_app` type module +# which should not have any .config file +ifeq (true, $(LOCAL_DISABLE_TEST_CONFIG)) + test_config := +endif + +# Make sure we only add the files once for multilib modules. +ifdef $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files + # Sync the auto_test_config value for multilib modules. + ifdef $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_autogen + ALL_MODULES.$(my_register_name).auto_test_config := true + endif +else + $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files := true + # LOCAL_COMPATIBILITY_SUPPORT_FILES is a list of [:]. + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_$(suite) += $(foreach f, $(LOCAL_COMPATIBILITY_SUPPORT_FILES), \ + $(eval p := $(subst :,$(space),$(f))) \ + $(eval s := $(word 1,$(p))) \ + $(eval n := $(or $(word 2,$(p)),$(notdir $(word 1, $(p))))) \ + $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(s):$(dir)/$(n))))) + + ifneq (,$(test_config)) + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(test_config):$(dir)/$(LOCAL_MODULE).config))) + endif + + ifneq (,$(LOCAL_EXTRA_FULL_TEST_CONFIGS)) + $(foreach test_config_file, $(LOCAL_EXTRA_FULL_TEST_CONFIGS), \ + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(test_config_file):$(dir)/$(basename $(notdir $(test_config_file))).config)))) + endif + + ifneq (,$(wildcard $(LOCAL_PATH)/DynamicConfig.xml)) + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(LOCAL_PATH)/DynamicConfig.xml:$(dir)/$(LOCAL_MODULE).dynamic))) + endif + + ifneq (,$(wildcard $(LOCAL_PATH)/$(LOCAL_MODULE)_*.config)) + $(foreach extra_config, $(wildcard $(LOCAL_PATH)/$(LOCAL_MODULE)_*.config), \ + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(extra_config):$(dir)/$(notdir $(extra_config)))))) + endif +endif # $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files + +# HACK: pretend a soong LOCAL_FULL_TEST_CONFIG is autogenerated by setting the flag in +# module-info.json +# TODO: (b/113029686) Add explicit flag from Soong to determine if a test was +# autogenerated. +ifneq (,$(filter $(SOONG_OUT_DIR)%,$(LOCAL_FULL_TEST_CONFIG))) + ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + ALL_MODULES.$(my_register_name).auto_test_config := true + endif +endif + + +ifeq ($(use_testcase_folder),true) +ifneq ($(my_test_data_file_pairs),) +# Filter out existng installed test data paths when collecting test data files to be installed and +# indexed as they cause build rule conflicts. Instead put them in a separate list which is only +# used for indexing. +$(foreach pair, $(my_test_data_file_pairs), \ + $(eval parts := $(subst :,$(space),$(pair))) \ + $(eval src_path := $(word 1,$(parts))) \ + $(eval file := $(word 2,$(parts))) \ + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ + $(call filter-copy-pair,$(src_path),$(call append-path,$(dir),$(file)),$(my_installed_test_data)))) \ + $(eval my_compat_dist_test_data_$(suite) += \ + $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ + $(filter $(my_installed_test_data),$(call append-path,$(dir),$(file))))))) +endif +else +ifneq ($(my_test_data_file_pairs),) +$(foreach pair, $(my_test_data_file_pairs), \ + $(eval parts := $(subst :,$(space),$(pair))) \ + $(eval src_path := $(word 1,$(parts))) \ + $(eval file := $(word 2,$(parts))) \ + $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ + $(src_path):$(call append-path,$(dir),$(file)))))) +endif +endif + + + +arch_dir := +is_native := + +$(call create-suite-dependencies) +$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_config_$(suite) := ) \ + $(eval my_compat_dist_test_data_$(suite) := )) + +endif # LOCAL_UNINSTALLABLE_MODULE +endif # LOCAL_COMPATIBILITY_SUITE + +my_supported_variant := +ifeq ($(my_host_cross),true) + my_supported_variant := HOST_CROSS +else + ifdef LOCAL_IS_HOST_MODULE + my_supported_variant := HOST + else + my_supported_variant := DEVICE + endif +endif +########################################################### +## Add test module to ALL_DISABLED_PRESUBMIT_TESTS if LOCAL_PRESUBMIT_DISABLED is set to true. +########################################################### +ifeq ($(LOCAL_PRESUBMIT_DISABLED),true) + ALL_DISABLED_PRESUBMIT_TESTS += $(LOCAL_MODULE) +endif # LOCAL_PRESUBMIT_DISABLED + +########################################################### +## Register with ALL_MODULES +########################################################### + +ifndef ALL_MODULES.$(my_register_name).PATH + # These keys are no longer used, they've been replaced by keys that specify + # target/host/host_cross (REQUIRED_FROM_TARGET / REQUIRED_FROM_HOST) and similar. + # + # Marking them obsolete to ensure that anyone using these internal variables looks for + # alternates. + $(KATI_obsolete_var ALL_MODULES.$(my_register_name).REQUIRED) + $(KATI_obsolete_var ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED) + $(KATI_obsolete_var ALL_MODULES.$(my_register_name).HOST_REQUIRED) + $(KATI_obsolete_var ALL_MODULES.$(my_register_name).TARGET_REQUIRED) +endif + +ALL_MODULES += $(my_register_name) + +# Don't use += on subvars, or else they'll end up being +# recursively expanded. +ALL_MODULES.$(my_register_name).CLASS := \ + $(ALL_MODULES.$(my_register_name).CLASS) $(LOCAL_MODULE_CLASS) +ALL_MODULES.$(my_register_name).PATH := \ + $(ALL_MODULES.$(my_register_name).PATH) $(LOCAL_PATH) +ALL_MODULES.$(my_register_name).TAGS := \ + $(ALL_MODULES.$(my_register_name).TAGS) $(my_module_tags) +ALL_MODULES.$(my_register_name).CHECKED := \ + $(ALL_MODULES.$(my_register_name).CHECKED) $(my_checked_module) +ALL_MODULES.$(my_register_name).BUILT := \ + $(ALL_MODULES.$(my_register_name).BUILT) $(LOCAL_BUILT_MODULE) +ifndef LOCAL_IS_HOST_MODULE +ALL_MODULES.$(my_register_name).TARGET_BUILT := \ + $(ALL_MODULES.$(my_register_name).TARGET_BUILT) $(LOCAL_BUILT_MODULE) +endif +ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) + # Store the list of paths to installed locations of files provided by this + # module. Used as dependencies of the image packaging rules when the module + # is installed by the current product. + ALL_MODULES.$(my_register_name).INSTALLED := \ + $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \ + $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\ + $(word 2,$(subst :,$(space),$(f)))) \ + $(LOCAL_SOONG_INSTALL_SYMLINKS) \ + $(my_init_rc_installed) \ + $(my_installed_test_data) \ + $(my_vintf_installed)) + # Store the list of colon-separated pairs of the built and installed locations + # of files provided by this module. Used by custom packaging rules like + # package-modules.mk that need to copy the built files to a custom install + # location during packaging. + # + # Translate copies from $(LOCAL_PREBUILT_MODULE_FILE) to $(LOCAL_BUILT_MODULE) + # so that package-modules.mk gets any transtive dependencies added to + # $(LOCAL_BUILT_MODULE), for example unstripped symbols files. + ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \ + $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \ + $(patsubst $(LOCAL_PREBUILT_MODULE_FILE):%,$(LOCAL_BUILT_MODULE):%,$(LOCAL_SOONG_INSTALL_PAIRS)) \ + $(my_init_rc_pairs) \ + $(my_test_data_pairs) \ + $(my_vintf_pairs)) +else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + ALL_MODULES.$(my_register_name).INSTALLED := \ + $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \ + $(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed) $(my_installed_symlinks) \ + $(my_installed_test_data) $(my_vintf_installed)) + ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \ + $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \ + $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \ + $(my_init_rc_pairs) $(my_test_data_pairs) $(my_vintf_pairs)) +endif +ifdef LOCAL_PICKUP_FILES +# Files or directories ready to pick up by the build system +# when $(LOCAL_BUILT_MODULE) is done. +ALL_MODULES.$(my_register_name).PICKUP_FILES := \ + $(ALL_MODULES.$(my_register_name).PICKUP_FILES) $(LOCAL_PICKUP_FILES) +endif +# Record the platform availability of this module. Note that the availability is not +# meaningful for non-installable modules (e.g., static libs) or host modules. +# We only care about modules that are installable to the device. +ifeq (true,$(LOCAL_NOT_AVAILABLE_FOR_PLATFORM)) + ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) + ifndef LOCAL_IS_HOST_MODULE + ALL_MODULES.$(my_register_name).NOT_AVAILABLE_FOR_PLATFORM := true + endif + endif +endif + +my_required_modules := $(LOCAL_REQUIRED_MODULES) \ + $(LOCAL_REQUIRED_MODULES_$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) +ifdef LOCAL_IS_HOST_MODULE +my_required_modules += $(LOCAL_REQUIRED_MODULES_$($(my_prefix)OS)) +endif + +ALL_MODULES.$(my_register_name).SHARED_LIBS := \ + $(ALL_MODULES.$(my_register_name).SHARED_LIBS) $(LOCAL_SHARED_LIBRARIES) + +ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS := \ + $(ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS) $(LOCAL_SYSTEM_SHARED_LIBRARIES) + +ALL_MODULES.$(my_register_name).LOCAL_RUNTIME_LIBRARIES := \ + $(ALL_MODULES.$(my_register_name).LOCAL_RUNTIME_LIBRARIES) $(LOCAL_RUNTIME_LIBRARIES) + +ifdef LOCAL_TEST_DATA + # Export the list of targets that are handled as data inputs and required + # by tests at runtime. The LOCAL_TEST_DATA format is generated from below + # https://cs.android.com/android/platform/superproject/+/master:build/soong/android/androidmk.go;l=925-944;drc=master + # which format is like $(path):$(relative_file) but for module-info, only + # the string after ":" is needed. + ALL_MODULES.$(my_register_name).TEST_DATA := \ + $(strip $(ALL_MODULES.$(my_register_name).TEST_DATA) \ + $(foreach f, $(LOCAL_TEST_DATA),\ + $(call word-colon,2,$(f)))) +endif + +ifdef LOCAL_TEST_DATA_BINS + ALL_MODULES.$(my_register_name).TEST_DATA_BINS := \ + $(ALL_MODULES.$(my_register_name).TEST_DATA_BINS) $(LOCAL_TEST_DATA_BINS) +endif + +ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS := \ + $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS) \ + $(filter-out $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS),$(my_supported_variant)) + +########################################################################## +## When compiling against the VNDK, add the .vendor or .product suffix to +## required modules. +########################################################################## +ifneq ($(LOCAL_USE_VNDK),) + ##################################################### + ## Soong modules may be built three times, once for + ## /system, once for /vendor and once for /product. + ## If we're using the VNDK, switch all soong + ## libraries over to the /vendor or /product variant. + ##################################################### + ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + # We don't do this renaming for soong-defined modules since they already + # have correct names (with .vendor or .product suffix when necessary) in + # their LOCAL_*_LIBRARIES. + ifeq ($(LOCAL_USE_VNDK_PRODUCT),true) + my_required_modules := $(foreach l,$(my_required_modules),\ + $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) + else + my_required_modules := $(foreach l,$(my_required_modules),\ + $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) + endif + endif +endif + +ifdef LOCAL_IS_HOST_MODULE + ifneq ($(my_host_cross),true) + ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST := \ + $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST) $(my_required_modules)) + ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST := \ + $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST)\ + $(my_required_modules)) + ALL_MODULES.$(my_register_name).TARGET_REQUIRED_FROM_HOST := \ + $(strip $(ALL_MODULES.$(my_register_name).TARGET_REQUIRED_FROM_HOST)\ + $(LOCAL_TARGET_REQUIRED_MODULES)) + else + ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST_CROSS := \ + $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST_CROSS) $(my_required_modules)) + ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST_CROSS := \ + $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST_CROSS)\ + $(my_required_modules)) + ifdef LOCAL_TARGET_REQUIRED_MODULES + $(call pretty-error,LOCAL_TARGET_REQUIRED_MODULES may not be used from host_cross modules) + endif + endif + ifdef LOCAL_HOST_REQUIRED_MODULES + $(call pretty-error,LOCAL_HOST_REQUIRED_MODULES may not be used from host modules. Use LOCAL_REQUIRED_MODULES instead) + endif +else + ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET := \ + $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET) $(my_required_modules)) + ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_TARGET := \ + $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_TARGET)\ + $(my_required_modules)) + ALL_MODULES.$(my_register_name).HOST_REQUIRED_FROM_TARGET := \ + $(strip $(ALL_MODULES.$(my_register_name).HOST_REQUIRED_FROM_TARGET)\ + $(LOCAL_HOST_REQUIRED_MODULES)) + ifdef LOCAL_TARGET_REQUIRED_MODULES + $(call pretty-error,LOCAL_TARGET_REQUIRED_MODULES may not be used from target modules. Use LOCAL_REQUIRED_MODULES instead) + endif +endif +ALL_MODULES.$(my_register_name).EVENT_LOG_TAGS := \ + $(ALL_MODULES.$(my_register_name).EVENT_LOG_TAGS) $(event_log_tags) +ALL_MODULES.$(my_register_name).MAKEFILE := \ + $(ALL_MODULES.$(my_register_name).MAKEFILE) $(LOCAL_MODULE_MAKEFILE) +ifdef LOCAL_MODULE_OWNER +ALL_MODULES.$(my_register_name).OWNER := \ + $(sort $(ALL_MODULES.$(my_register_name).OWNER) $(LOCAL_MODULE_OWNER)) +endif +ifdef LOCAL_2ND_ARCH_VAR_PREFIX +ALL_MODULES.$(my_register_name).FOR_2ND_ARCH := true +endif +ALL_MODULES.$(my_register_name).FOR_HOST_CROSS := $(my_host_cross) +ALL_MODULES.$(my_register_name).MODULE_NAME := $(LOCAL_MODULE) +ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := \ + $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES) \ + $(filter-out $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES),$(LOCAL_COMPATIBILITY_SUITE)) +ALL_MODULES.$(my_register_name).TEST_CONFIG := $(test_config) +ALL_MODULES.$(my_register_name).EXTRA_TEST_CONFIGS := $(LOCAL_EXTRA_FULL_TEST_CONFIGS) +ALL_MODULES.$(my_register_name).TEST_MAINLINE_MODULES := $(LOCAL_TEST_MAINLINE_MODULES) +ifndef LOCAL_IS_HOST_MODULE +ALL_MODULES.$(my_register_name).FILE_CONTEXTS := $(LOCAL_FILE_CONTEXTS) +endif +ifdef LOCAL_IS_UNIT_TEST +ALL_MODULES.$(my_register_name).IS_UNIT_TEST := $(LOCAL_IS_UNIT_TEST) +endif +test_config := + +INSTALLABLE_FILES.$(LOCAL_INSTALLED_MODULE).MODULE := $(my_register_name) + +########################################################## +# Track module-level dependencies. +# Use $(LOCAL_MODULE) instead of $(my_register_name) to ignore module's bitness. +# (b/204397180) Unlock RECORD_ALL_DEPS was acknowledged reasonable for better Atest performance. +ALL_DEPS.MODULES += $(LOCAL_MODULE) +ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(sort \ + $(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) \ + $(LOCAL_STATIC_LIBRARIES) \ + $(LOCAL_WHOLE_STATIC_LIBRARIES) \ + $(LOCAL_SHARED_LIBRARIES) \ + $(LOCAL_DYLIB_LIBRARIES) \ + $(LOCAL_RLIB_LIBRARIES) \ + $(LOCAL_PROC_MACRO_LIBRARIES) \ + $(LOCAL_HEADER_LIBRARIES) \ + $(LOCAL_STATIC_JAVA_LIBRARIES) \ + $(LOCAL_JAVA_LIBRARIES) \ + $(LOCAL_JNI_SHARED_LIBRARIES)) + +license_files := $(call find-parent-file,$(LOCAL_PATH),MODULE_LICENSE*) +ALL_DEPS.$(LOCAL_MODULE).LICENSE := $(sort $(ALL_DEPS.$(LOCAL_MODULE).LICENSE) $(license_files)) + +########################################################### +## Take care of my_module_tags +########################################################### + +# Keep track of all the tags we've seen. +ALL_MODULE_TAGS := $(sort $(ALL_MODULE_TAGS) $(my_module_tags)) + +# Add this module name to the tag list of each specified tag. +$(foreach tag,$(filter-out optional,$(my_module_tags)),\ + $(eval ALL_MODULE_NAME_TAGS.$(tag) := $$(ALL_MODULE_NAME_TAGS.$(tag)) $(my_register_name))) + +########################################################### +## umbrella targets used to verify builds +########################################################### +j_or_n := +ifneq (,$(filter EXECUTABLES SHARED_LIBRARIES STATIC_LIBRARIES HEADER_LIBRARIES NATIVE_TESTS RLIB_LIBRARIES DYLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS))) +j_or_n := native +else +ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS))) +j_or_n := java +endif +endif +ifdef LOCAL_IS_HOST_MODULE +h_or_t := host +ifeq ($(my_host_cross),true) +h_or_hc_or_t := host-cross +else +h_or_hc_or_t := host +endif +else +h_or_hc_or_t := target +h_or_t := target +endif + + +ifdef j_or_n +$(j_or_n) $(h_or_t) $(j_or_n)-$(h_or_hc_or_t) : $(my_checked_module) +ifneq (,$(filter $(my_module_tags),tests)) +$(j_or_n)-$(h_or_t)-tests $(j_or_n)-tests $(h_or_t)-tests : $(my_checked_module) +endif +$(LOCAL_MODULE)-$(h_or_hc_or_t) : $(my_all_targets) +.PHONY: $(LOCAL_MODULE)-$(h_or_hc_or_t) +ifeq ($(j_or_n),native) +$(LOCAL_MODULE)-$(h_or_hc_or_t)$(my_32_64_bit_suffix) : $(my_all_targets) +.PHONY: $(LOCAL_MODULE)-$(h_or_hc_or_t)$(my_32_64_bit_suffix) +endif +endif + +########################################################### +# Ensure privileged applications always have LOCAL_PRIVILEGED_MODULE +########################################################### +ifndef LOCAL_PRIVILEGED_MODULE + ifneq (,$(filter $(TARGET_OUT_APPS_PRIVILEGED)/% $(TARGET_OUT_VENDOR_APPS_PRIVILEGED)/%,$(my_module_path))) + LOCAL_PRIVILEGED_MODULE := true + endif +endif + +########################################################### +## NOTICE files +########################################################### + +include $(BUILD_NOTICE_FILE) diff --git a/make/core/binary.mk b/make/core/binary.mk new file mode 100644 index 0000000..665270e --- /dev/null +++ b/make/core/binary.mk @@ -0,0 +1,1831 @@ +########################################################### +## Standard rules for building binary object files from +## asm/c/cpp/yacc/lex/etc source files. +## +## The list of object files is exported in $(all_objects). +########################################################### + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +include $(BUILD_SYSTEM)/use_lld_setup.mk +####################################### + +################################################## +# Compute the dependency of the shared libraries +################################################## +# On the target, we compile with -nostdlib, so we must add in the +# default system shared libraries, unless they have requested not +# to by supplying a LOCAL_SYSTEM_SHARED_LIBRARIES value. One would +# supply that, for example, when building libc itself. +ifdef LOCAL_IS_HOST_MODULE + ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) + my_system_shared_libraries := + else + my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) + endif +else + ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) + my_system_shared_libraries := libc libm libdl + else + my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) + my_system_shared_libraries := $(patsubst libc,libc libdl,$(my_system_shared_libraries)) + endif +endif + +# Third party code has additional no-override flags. +is_third_party := +ifneq ($(filter external/% hardware/% vendor/%,$(LOCAL_PATH)),) + is_third_party := true +endif + +my_soong_problems := + +# The following LOCAL_ variables will be modified in this file. +# Because the same LOCAL_ variables may be used to define modules for both 1st arch and 2nd arch, +# we can't modify them in place. +my_src_files := $(LOCAL_SRC_FILES) +my_src_files_exclude := $(LOCAL_SRC_FILES_EXCLUDE) +my_static_libraries := $(LOCAL_STATIC_LIBRARIES) +my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES) +my_shared_libraries := $(filter-out $(my_system_shared_libraries),$(LOCAL_SHARED_LIBRARIES)) +my_header_libraries := $(LOCAL_HEADER_LIBRARIES) +my_cflags := $(LOCAL_CFLAGS) +my_conlyflags := $(LOCAL_CONLYFLAGS) +my_cppflags := $(LOCAL_CPPFLAGS) +my_cflags_no_override := $(GLOBAL_CLANG_CFLAGS_NO_OVERRIDE) +my_cppflags_no_override := $(GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE) +ifdef is_third_party + my_cflags_no_override += $(GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE) + my_cppflags_no_override += $(GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE) +endif +my_ldflags := $(LOCAL_LDFLAGS) +my_ldlibs := $(LOCAL_LDLIBS) +my_asflags := $(LOCAL_ASFLAGS) +my_cc := $(LOCAL_CC) +my_cc_wrapper := $(CC_WRAPPER) +my_cxx := $(LOCAL_CXX) +my_cxx_link := $(LOCAL_CXX) +my_cxx_ldlibs := +my_cxx_wrapper := $(CXX_WRAPPER) +my_c_includes := $(LOCAL_C_INCLUDES) +my_generated_sources := $(LOCAL_GENERATED_SOURCES) +my_additional_dependencies := $(LOCAL_ADDITIONAL_DEPENDENCIES) +my_export_c_include_dirs := $(LOCAL_EXPORT_C_INCLUDE_DIRS) +my_export_c_include_deps := $(LOCAL_EXPORT_C_INCLUDE_DEPS) +my_arflags := + +# Disable clang-tidy if it is not found. +ifeq ($(PATH_TO_CLANG_TIDY),) + my_tidy_enabled := false +else + # If LOCAL_TIDY is not defined, use global WITH_TIDY + my_tidy_enabled := $(LOCAL_TIDY) + ifeq ($(my_tidy_enabled),) + my_tidy_enabled := $(WITH_TIDY) + endif +endif + +# my_tidy_checks is empty if clang-tidy is disabled. +my_tidy_checks := +my_tidy_flags := +ifneq (,$(filter 1 true,$(my_tidy_enabled))) + # Set up global default checks + my_tidy_checks := $(WITH_TIDY_CHECKS) + ifeq ($(my_tidy_checks),) + my_tidy_checks := $(call default_global_tidy_checks,$(LOCAL_PATH)) + endif + # Append local clang-tidy checks. + ifneq ($(LOCAL_TIDY_CHECKS),) + my_tidy_checks := $(my_tidy_checks),$(LOCAL_TIDY_CHECKS) + endif + my_tidy_flags := $(strip $(WITH_TIDY_FLAGS) $(LOCAL_TIDY_FLAGS)) + # If tidy flags are not specified, default to check all header files. + ifeq ($(my_tidy_flags),) + my_tidy_flags := $(call default_tidy_header_filter,$(LOCAL_PATH)) + endif + # If clang-tidy is not enabled globally, add the -quiet flag. + ifeq (,$(filter 1 true,$(WITH_TIDY))) + my_tidy_flags += -quiet -extra-arg-before=-fno-caret-diagnostics + endif + + ifneq ($(my_tidy_checks),) + # We might be using the static analyzer through clang-tidy. + # https://bugs.llvm.org/show_bug.cgi?id=32914 + my_tidy_flags += -extra-arg-before=-D__clang_analyzer__ + + # A recent change in clang-tidy (r328258) enabled destructor inlining, + # which appears to cause a number of false positives. Until that's + # resolved, this turns off the effects of r328258. + # https://bugs.llvm.org/show_bug.cgi?id=37459 + my_tidy_flags += -extra-arg-before=-Xclang + my_tidy_flags += -extra-arg-before=-analyzer-config + my_tidy_flags += -extra-arg-before=-Xclang + my_tidy_flags += -extra-arg-before=c++-temp-dtor-inlining=false + endif +endif + +my_tidy_checks := $(subst $(space),,$(my_tidy_checks)) + +# Configure the pool to use for clang rules. +# If LOCAL_CC or LOCAL_CXX is set don't use goma or RBE. +# If clang-tidy is being used, don't use the RBE pool (as clang-tidy runs in +# the same action, and is not remoted) +my_pool := +ifeq (,$(strip $(my_cc))$(strip $(my_cxx))$(strip $(my_tidy_checks))) + my_pool := $(GOMA_OR_RBE_POOL) +endif + +ifneq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_PATHS),$(filter $(dir)%,$(LOCAL_PATH))))) +ifeq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_EXCLUDE_PATHS),$(filter $(dir)%,$(LOCAL_PATH))))) + my_native_coverage := true +else + my_native_coverage := false +endif +else + my_native_coverage := false +endif +ifneq ($(NATIVE_COVERAGE),true) + my_native_coverage := false +endif + +# Exclude directories from checking allowed manual binder interface lists. +# TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths. +ifneq (,$(filter $(addsuffix %,$(ALLOWED_MANUAL_INTERFACE_PATHS)),$(LOCAL_PATH))) + my_cflags += -DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES +endif + +my_allow_undefined_symbols := $(strip $(LOCAL_ALLOW_UNDEFINED_SYMBOLS)) +ifdef SANITIZE_HOST +ifdef LOCAL_IS_HOST_MODULE +my_allow_undefined_symbols := true +endif +endif + +my_ndk_sysroot := +my_ndk_sysroot_include := +my_ndk_sysroot_lib := +my_api_level := 10000 + +my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) + +ifneq ($(LOCAL_SDK_VERSION),) + ifdef LOCAL_IS_HOST_MODULE + $(error $(LOCAL_PATH): LOCAL_SDK_VERSION cannot be used in host module) + endif + + # Make sure we've built the NDK. + my_additional_dependencies += $(SOONG_OUT_DIR)/ndk_base.timestamp + + ifneq (,$(filter arm64 x86_64,$(my_arch))) + my_min_sdk_version := 21 + else + my_min_sdk_version := $(MIN_SUPPORTED_SDK_VERSION) + endif + + # Historically we've just set up a bunch of symlinks in prebuilts/ndk to map + # missing API levels to existing ones where necessary, but we're not doing + # that for the generated libraries. Clip the API level to the minimum where + # appropriate. + my_ndk_api := $(LOCAL_SDK_VERSION) + ifneq ($(my_ndk_api),current) + my_ndk_api := $(call math_max,$(my_ndk_api),$(my_min_sdk_version)) + endif + + my_ndk_crt_version := $(my_ndk_api) + + my_ndk_hist_api := $(my_ndk_api) + ifeq ($(my_ndk_api),current) + # The last API level supported by the old prebuilt NDKs. + my_ndk_hist_api := 24 + else + my_api_level := $(my_ndk_api) + endif + + my_ndk_source_root := \ + $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources + my_ndk_sysroot := \ + $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/platforms/android-$(my_ndk_hist_api)/arch-$(my_arch) + my_built_ndk := $(SOONG_OUT_DIR)/ndk + my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE) + my_ndk_sysroot_include := \ + $(my_built_ndk)/sysroot/usr/include \ + $(my_built_ndk)/sysroot/usr/include/$(my_ndk_triple) \ + $(my_ndk_sysroot)/usr/include \ + + # x86_64 is a multilib toolchain, so their libraries are + # installed in /usr/lib64. Aarch64, on the other hand, is not a multilib + # compiler, so its libraries are in /usr/lib. + ifneq (,$(filter x86_64,$(my_arch))) + my_ndk_libdir_name := lib64 + else + my_ndk_libdir_name := lib + endif + + my_ndk_platform_dir := \ + $(my_built_ndk)/platforms/android-$(my_ndk_api)/arch-$(my_arch) + my_built_ndk_libs := $(my_ndk_platform_dir)/usr/$(my_ndk_libdir_name) + my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/$(my_ndk_libdir_name) + + # The bionic linker now has support for packed relocations and gnu style + # hashes (which are much faster!), but shipping to older devices requires + # the old style hash. Fortunately, we can build with both and it'll work + # anywhere. + my_ldflags += -Wl,--hash-style=both + + # We don't want to expose the relocation packer to the NDK just yet. + LOCAL_PACK_MODULE_RELOCATIONS := false + + # Set up the NDK stl variant. Starting from NDK-r5 the c++ stl resides in a separate location. + # See ndk/docs/CPLUSPLUS-SUPPORT.html + my_ndk_stl_include_path := + my_ndk_stl_shared_lib_fullpath := + my_ndk_stl_static_lib := + my_cpu_variant := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)CPU_ABI) + LOCAL_NDK_STL_VARIANT := $(strip $(LOCAL_NDK_STL_VARIANT)) + ifeq (,$(LOCAL_NDK_STL_VARIANT)) + LOCAL_NDK_STL_VARIANT := system + endif + ifneq (1,$(words $(filter none system c++_static c++_shared, $(LOCAL_NDK_STL_VARIANT)))) + $(error $(LOCAL_PATH): Unknown LOCAL_NDK_STL_VARIANT $(LOCAL_NDK_STL_VARIANT)) + endif + + ifeq (system,$(LOCAL_NDK_STL_VARIANT)) + my_ndk_stl_include_path := $(my_ndk_source_root)/cxx-stl/system/include + my_system_shared_libraries += libstdc++ + else ifneq (,$(filter c++_%, $(LOCAL_NDK_STL_VARIANT))) + my_ndk_stl_include_path := \ + $(my_ndk_source_root)/cxx-stl/llvm-libc++/include + my_ndk_stl_include_path += \ + $(my_ndk_source_root)/cxx-stl/llvm-libc++abi/include + + my_libcxx_libdir := \ + $(my_ndk_source_root)/cxx-stl/llvm-libc++/libs/$(my_cpu_variant) + + ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT)) + my_ndk_stl_static_lib := \ + $(my_libcxx_libdir)/libc++_static.a \ + $(my_libcxx_libdir)/libc++abi.a + else + my_ndk_stl_shared_lib_fullpath := $(my_libcxx_libdir)/libc++_shared.so + endif + + ifneq ($(my_ndk_api),current) + ifeq ($(call math_lt,$(my_ndk_api),21),true) + my_ndk_stl_include_path += $(my_ndk_source_root)/android/support/include + my_ndk_stl_static_lib += $(my_libcxx_libdir)/libandroid_support.a + endif + endif + + my_ndk_stl_static_lib += $(my_libcxx_libdir)/libunwind.a + my_ldlibs += -ldl + else # LOCAL_NDK_STL_VARIANT must be none + # Do nothing. + endif + + # Clang's coverage/profile runtime needs symbols like 'stderr' that were not + # exported from libc prior to API level 23 + ifneq ($(my_ndk_api),current) + ifeq ($(call math_lt, $(my_ndk_api),23),true) + my_native_coverage := false + endif + endif +endif + +ifeq ($(NATIVE_COVERAGE),true) + ifndef LOCAL_IS_HOST_MODULE + my_ldflags += -Wl,--wrap,getenv + + ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES) + ifeq ($(LOCAL_SDK_VERSION),) + my_whole_static_libraries += libprofile-extras + else + my_whole_static_libraries += libprofile-extras_ndk + endif + endif + endif +endif + +ifneq ($(LOCAL_USE_VNDK),) + # Required VNDK version for vendor modules is BOARD_VNDK_VERSION. + my_api_level := $(BOARD_VNDK_VERSION) + ifeq ($(my_api_level),current) + # Build with current PLATFORM_VNDK_VERSION. + # If PLATFORM_VNDK_VERSION has a CODENAME, it will return + # __ANDROID_API_FUTURE__. + my_api_level := $(call codename-or-sdk-to-sdk,$(PLATFORM_VNDK_VERSION)) + else + # Build with current BOARD_VNDK_VERSION. + my_api_level := $(call codename-or-sdk-to-sdk,$(BOARD_VNDK_VERSION)) + endif + my_cflags += -D__ANDROID_VNDK__ + ifneq ($(LOCAL_USE_VNDK_VENDOR),) + # Vendor modules have LOCAL_USE_VNDK_VENDOR when + # BOARD_VNDK_VERSION is defined. + my_cflags += -D__ANDROID_VENDOR__ + else ifneq ($(LOCAL_USE_VNDK_PRODUCT),) + # Product modules have LOCAL_USE_VNDK_PRODUCT when + # PRODUCT_PRODUCT_VNDK_VERSION is defined. + my_cflags += -D__ANDROID_PRODUCT__ + endif +endif + +ifndef LOCAL_IS_HOST_MODULE +# For device libraries, move LOCAL_LDLIBS references to my_shared_libraries. We +# no longer need to use my_ldlibs to pick up NDK prebuilt libraries since we're +# linking my_shared_libraries by full path now. +my_allowed_ldlibs := + +# Sort ldlibs and ldflags between -l and other linker flags +# We'll do this again later, since there are still changes happening, but that's fine. +my_ldlib_flags := $(my_ldflags) $(my_ldlibs) +my_ldlibs := $(filter -l%,$(my_ldlib_flags)) +my_ldflags := $(filter-out -l%,$(my_ldlib_flags)) +my_ldlib_flags := + +# Move other ldlibs back to shared libraries +my_shared_libraries += $(patsubst -l%,lib%,$(filter-out $(my_allowed_ldlibs),$(my_ldlibs))) +my_ldlibs := $(filter $(my_allowed_ldlibs),$(my_ldlibs)) +else # LOCAL_IS_HOST_MODULE + # Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of + # device builds + my_ldlibs += -ldl -lpthread -lm + ifneq ($(HOST_OS),darwin) + my_ldlibs += -lrt + endif +endif + +ifneq ($(LOCAL_SDK_VERSION),) + my_all_ndk_libraries := $(NDK_KNOWN_LIBS) + my_ndk_shared_libraries := \ + $(filter $(my_all_ndk_libraries),\ + $(my_shared_libraries) $(my_system_shared_libraries)) + + my_shared_libraries := \ + $(filter-out $(my_all_ndk_libraries),$(my_shared_libraries)) + my_system_shared_libraries := \ + $(filter-out $(my_all_ndk_libraries),$(my_system_shared_libraries)) +endif + +# MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because +# all code is position independent, and then those warnings get promoted to +# errors. +ifneq ($(LOCAL_NO_PIC),true) + ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + my_cflags += -fPIE + ifndef BUILD_HOST_static + ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + my_ldflags += -pie + endif + endif + else + my_cflags += -fPIC + endif +endif + +ifdef LOCAL_IS_HOST_MODULE +my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)OS)) $(LOCAL_SRC_FILES_$($(my_prefix)OS)_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) +my_static_libraries += $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)OS)) +my_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)OS)) +my_header_libraries += $(LOCAL_HEADER_LIBRARIES_$($(my_prefix)OS)) +my_cflags += $(LOCAL_CFLAGS_$($(my_prefix)OS)) +my_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)OS)) +my_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)OS)) +my_ldlibs += $(LOCAL_LDLIBS_$($(my_prefix)OS)) +my_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)OS)) +my_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)OS)) +my_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)OS)) +endif + +my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_$(my_32_64_bit_suffix)) +my_src_files_exclude += $(LOCAL_SRC_FILES_EXCLUDE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_EXCLUDE_$(my_32_64_bit_suffix)) +my_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SHARED_LIBRARIES_$(my_32_64_bit_suffix)) +my_cflags += $(LOCAL_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CFLAGS_$(my_32_64_bit_suffix)) +my_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CPPFLAGS_$(my_32_64_bit_suffix)) +my_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_LDFLAGS_$(my_32_64_bit_suffix)) +my_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_ASFLAGS_$(my_32_64_bit_suffix)) +my_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_C_INCLUDES_$(my_32_64_bit_suffix)) +my_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_GENERATED_SOURCES_$(my_32_64_bit_suffix)) + +my_missing_exclude_files := $(filter-out $(my_src_files),$(my_src_files_exclude)) +ifneq ($(my_missing_exclude_files),) +$(warning Files are listed in LOCAL_SRC_FILES_EXCLUDE but not LOCAL_SRC_FILES) +$(error $(my_missing_exclude_files)) +endif +my_src_files := $(filter-out $(my_src_files_exclude),$(my_src_files)) + +# Strip '/' from the beginning of each src file. This helps the ../ detection in case +# the source file is in the form of /../file +my_src_files := $(patsubst /%,%,$(my_src_files)) + +my_clang := $(strip $(LOCAL_CLANG)) +ifdef LOCAL_CLANG_$(my_32_64_bit_suffix) +my_clang := $(strip $(LOCAL_CLANG_$(my_32_64_bit_suffix))) +endif +ifdef LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) +my_clang := $(strip $(LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))) +endif +ifeq ($(my_clang),false) + $(call pretty-error,LOCAL_CLANG false is no longer supported) +endif + +ifeq ($(LOCAL_C_STD),) + my_c_std_version := $(DEFAULT_C_STD_VERSION) +else ifeq ($(LOCAL_C_STD),experimental) + my_c_std_version := $(EXPERIMENTAL_C_STD_VERSION) +else + my_c_std_version := $(LOCAL_C_STD) +endif + +ifeq ($(LOCAL_CPP_STD),) + my_cpp_std_version := $(DEFAULT_CPP_STD_VERSION) +else ifeq ($(LOCAL_CPP_STD),experimental) + my_cpp_std_version := $(EXPERIMENTAL_CPP_STD_VERSION) +else + my_cpp_std_version := $(LOCAL_CPP_STD) +endif + +my_c_std_conlyflags := +my_cpp_std_cppflags := +ifneq (,$(my_c_std_version)) + my_c_std_conlyflags := -std=$(my_c_std_version) +endif + +ifneq (,$(my_cpp_std_version)) + my_cpp_std_cppflags := -std=$(my_cpp_std_version) +endif + +# Extra cflags for projects under external/ directory +ifneq ($(filter external/%,$(LOCAL_PATH)),) + my_cflags += $(CLANG_EXTERNAL_CFLAGS) +endif + +# arch-specific static libraries go first so that generic ones can depend on them +my_static_libraries := $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_static_libraries) +my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_WHOLE_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_whole_static_libraries) +my_header_libraries := $(LOCAL_HEADER_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_HEADER_LIBRARIES_$(my_32_64_bit_suffix)) $(my_header_libraries) + +include $(BUILD_SYSTEM)/cxx_stl_setup.mk + +ifneq ($(strip $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER)),) + my_linker := $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER) +else + my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LINKER) +endif + +include $(BUILD_SYSTEM)/config_sanitizers.mk + +ifneq ($(filter ../%,$(my_src_files)),) +my_soong_problems += dotdot_srcs +endif +ifneq ($(foreach i,$(my_c_includes),$(filter %/..,$(i))$(findstring /../,$(i))),) +my_soong_problems += dotdot_incs +endif + +########################################################### +## Explicitly declare assembly-only __ASSEMBLY__ macro for +## assembly source +########################################################### +my_asflags += -D__ASSEMBLY__ + +########################################################### +# TODO: support a mix of standard extensions so that this isn't necessary +LOCAL_CPP_EXTENSION := $(strip $(LOCAL_CPP_EXTENSION)) +ifeq ($(LOCAL_CPP_EXTENSION),) + LOCAL_CPP_EXTENSION := .cpp +endif + +# Certain modules like libdl have to have symbols resolved at runtime and blow +# up if --no-undefined is passed to the linker. +ifeq ($(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS)),) + ifeq ($(my_allow_undefined_symbols),) + ifneq ($(HOST_OS),darwin) + my_ldflags += -Wl,--no-undefined + endif + else + ifdef LOCAL_IS_HOST_MODULE + ifeq ($(HOST_OS),darwin) + # darwin defaults to treating undefined symbols as errors + my_ldflags += -Wl,-undefined,dynamic_lookup + endif + endif + endif +endif + +ifeq (true,$(LOCAL_GROUP_STATIC_LIBRARIES)) +$(LOCAL_BUILT_MODULE): PRIVATE_GROUP_STATIC_LIBRARIES := true +else +$(LOCAL_BUILT_MODULE): PRIVATE_GROUP_STATIC_LIBRARIES := +endif + +########################################################### +## Define arm-vs-thumb-mode flags. +########################################################### +LOCAL_ARM_MODE := $(strip $(LOCAL_ARM_MODE)) +ifeq ($($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),arm) +normal_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),thumb) + +# Read the values from something like TARGET_arm_CFLAGS or +# TARGET_thumb_CFLAGS. HOST_(arm|thumb)_CFLAGS values aren't +# actually used (although they are usually empty). +normal_objects_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)$(normal_objects_mode)_CFLAGS) + +else +normal_objects_mode := +normal_objects_cflags := +endif + +########################################################### +## Define per-module debugging flags. Users can turn on +## debugging for a particular module by setting DEBUG_MODULE_ModuleName +## to a non-empty value in their environment or buildspec.mk, +## and setting HOST_/TARGET_CUSTOM_DEBUG_CFLAGS to the +## debug flags that they want to use. +########################################################### +ifdef DEBUG_MODULE_$(strip $(LOCAL_MODULE)) + debug_cflags := $($(my_prefix)CUSTOM_DEBUG_CFLAGS) +else + debug_cflags := +endif + +#################################################### +## Keep track of src -> obj mapping +#################################################### + +my_tracked_gen_files := +my_tracked_src_files := + +########################################################### +## Stuff source generated from one-off tools +########################################################### +$(my_generated_sources): PRIVATE_MODULE := $(my_register_name) + +my_gen_sources_copy := $(patsubst $(generated_sources_dir)/%,$(intermediates)/%,$(filter $(generated_sources_dir)/%,$(my_generated_sources))) + +$(my_gen_sources_copy): $(intermediates)/% : $(generated_sources_dir)/% + @echo "Copy: $@" + $(copy-file-to-target) + +my_generated_sources := $(patsubst $(generated_sources_dir)/%,$(intermediates)/%,$(my_generated_sources)) + +# Generated sources that will actually produce object files. +# Other files (like headers) are allowed in LOCAL_GENERATED_SOURCES, +# since other compiled sources may depend on them, and we set up +# the dependencies. +my_gen_src_files := $(filter %.c %$(LOCAL_CPP_EXTENSION) %.S %.s,$(my_generated_sources)) + +#################################################### +## Compile RenderScript with reflected C++ +#################################################### + +renderscript_sources := $(filter %.rscript %.fs,$(my_src_files)) + +ifneq (,$(renderscript_sources)) +my_soong_problems += rs + +renderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources)) +RenderScript_file_stamp := $(intermediates)/RenderScriptCPP.stamp +renderscript_intermediate := $(intermediates)/renderscript + +renderscript_target_api := + +ifneq (,$(LOCAL_RENDERSCRIPT_TARGET_API)) +renderscript_target_api := $(LOCAL_RENDERSCRIPT_TARGET_API) +else +ifneq (,$(LOCAL_SDK_VERSION)) +# Set target-api for LOCAL_SDK_VERSIONs other than current. +ifneq (,$(filter-out current system_current test_current, $(LOCAL_SDK_VERSION))) +renderscript_target_api := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)) +endif +endif # LOCAL_SDK_VERSION is set +endif # LOCAL_RENDERSCRIPT_TARGET_API is set + + +ifeq ($(LOCAL_RENDERSCRIPT_CC),) +LOCAL_RENDERSCRIPT_CC := $(LLVM_RS_CC) +endif + +# Turn on all warnings and warnings as errors for RS compiles. +# This can be disabled with LOCAL_RENDERSCRIPT_FLAGS := -Wno-error +renderscript_flags := -Wall -Werror +renderscript_flags += $(LOCAL_RENDERSCRIPT_FLAGS) +# -m32 or -m64 +renderscript_flags += -m$(my_32_64_bit_suffix) + +renderscript_includes := \ + $(TOPDIR)external/clang/lib/Headers \ + $(TOPDIR)frameworks/rs/script_api/include \ + $(LOCAL_RENDERSCRIPT_INCLUDES) + +ifneq ($(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE),) +renderscript_includes := $(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE) +endif + +bc_dep_files := $(addprefix $(renderscript_intermediate)/, \ + $(patsubst %.fs,%.d, $(patsubst %.rscript,%.d, $(notdir $(renderscript_sources))))) + +$(RenderScript_file_stamp): PRIVATE_RS_INCLUDES := $(renderscript_includes) +$(RenderScript_file_stamp): PRIVATE_RS_CC := $(LOCAL_RENDERSCRIPT_CC) +$(RenderScript_file_stamp): PRIVATE_RS_FLAGS := $(renderscript_flags) +$(RenderScript_file_stamp): PRIVATE_RS_SOURCE_FILES := $(renderscript_sources_fullpath) +$(RenderScript_file_stamp): PRIVATE_RS_OUTPUT_DIR := $(renderscript_intermediate) +$(RenderScript_file_stamp): PRIVATE_RS_TARGET_API := $(patsubst current,0,$(renderscript_target_api)) +$(RenderScript_file_stamp): PRIVATE_DEP_FILES := $(bc_dep_files) +$(RenderScript_file_stamp): $(renderscript_sources_fullpath) $(LOCAL_RENDERSCRIPT_CC) + $(transform-renderscripts-to-cpp-and-bc) + +# include the dependency files (.d) generated by llvm-rs-cc. +$(call include-depfile,$(RenderScript_file_stamp).d,$(RenderScript_file_stamp)) + +LOCAL_INTERMEDIATE_TARGETS += $(RenderScript_file_stamp) + +rs_generated_cpps := $(addprefix \ + $(renderscript_intermediate)/ScriptC_,$(patsubst %.fs,%.cpp, $(patsubst %.rscript,%.cpp, \ + $(notdir $(renderscript_sources))))) + +$(call track-src-file-gen,$(renderscript_sources),$(rs_generated_cpps)) + +# This is just a no-op rule to make sure gmake doesn't skip updating the dependents. +$(rs_generated_cpps) : $(RenderScript_file_stamp) + @echo "Updated RS generated cpp file $@." + $(hide) touch $@ + +my_c_includes += $(renderscript_intermediate) +my_generated_sources += $(rs_generated_cpps) + +endif + + +########################################################### +## Compile the .proto files to .cc (or .c) and then to .o +########################################################### +ifeq ($(strip $(LOCAL_PROTOC_OPTIMIZE_TYPE)),) + LOCAL_PROTOC_OPTIMIZE_TYPE := lite +endif +proto_sources := $(filter %.proto,$(my_src_files)) +ifneq ($(proto_sources),) +proto_gen_dir := $(generated_sources_dir)/proto +proto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources)) + +my_rename_cpp_ext := +ifneq (,$(filter nanopb-c nanopb-c-enable_malloc nanopb-c-16bit nanopb-c-enable_malloc-16bit nanopb-c-32bit nanopb-c-enable_malloc-32bit, $(LOCAL_PROTOC_OPTIMIZE_TYPE))) +my_proto_source_suffix := .c +my_proto_c_includes := external/nanopb-c +my_protoc_flags := --nanopb_out=$(proto_gen_dir) \ + --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb +my_protoc_deps := $(NANOPB_SRCS) $(proto_sources_fullpath:%.proto=%.options) +else +my_proto_source_suffix := $(LOCAL_CPP_EXTENSION) +ifneq ($(my_proto_source_suffix),.cc) +# aprotoc is hardcoded to write out only .cc file. +# We need to rename the extension to $(LOCAL_CPP_EXTENSION) if it's not .cc. +my_rename_cpp_ext := true +endif +my_proto_c_includes := external/protobuf/src +my_cflags += -DGOOGLE_PROTOBUF_NO_RTTI +my_protoc_flags := --cpp_out=$(if $(filter lite lite-static,$(LOCAL_PROTOC_OPTIMIZE_TYPE)),lite:,)$(proto_gen_dir) +my_protoc_deps := +endif +my_proto_c_includes += $(proto_gen_dir) + +proto_generated_cpps := $(addprefix $(proto_gen_dir)/, \ + $(patsubst %.proto,%.pb$(my_proto_source_suffix),$(proto_sources_fullpath))) + +# Ensure the transform-proto-to-cc rule is only defined once in multilib build. +ifndef $(my_host)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined +$(proto_generated_cpps): PRIVATE_PROTO_INCLUDES := $(TOP) +$(proto_generated_cpps): PRIVATE_PROTOC_FLAGS := $(LOCAL_PROTOC_FLAGS) $(my_protoc_flags) +$(proto_generated_cpps): PRIVATE_RENAME_CPP_EXT := $(my_rename_cpp_ext) +$(proto_generated_cpps): $(proto_gen_dir)/%.pb$(my_proto_source_suffix): %.proto $(my_protoc_deps) $(PROTOC) + $(transform-proto-to-cc) + +$(my_host)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined := true +endif +# Ideally we can generate the source directly into $(intermediates). +# But many Android.mks assume the .pb.hs are in $(generated_sources_dir). +# As a workaround, we make a copy in the $(intermediates). +proto_intermediate_dir := $(intermediates)/proto +proto_intermediate_cpps := $(patsubst $(proto_gen_dir)/%,$(proto_intermediate_dir)/%,\ + $(proto_generated_cpps)) +$(proto_intermediate_cpps) : $(proto_intermediate_dir)/% : $(proto_gen_dir)/% + @echo "Copy: $@" + $(copy-file-to-target) + $(hide) cp $(basename $<).h $(basename $@).h +$(call track-src-file-gen,$(proto_sources),$(proto_intermediate_cpps)) + +my_generated_sources += $(proto_intermediate_cpps) + +my_c_includes += $(my_proto_c_includes) +# Auto-export the generated proto source dir. +my_export_c_include_dirs += $(my_proto_c_includes) + +ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc) + my_static_libraries += libprotobuf-c-nano-enable_malloc +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c) + my_static_libraries += libprotobuf-c-nano +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc-16bit) + my_static_libraries += libprotobuf-c-nano-enable_malloc-16bit +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-16bit) + my_static_libraries += libprotobuf-c-nano-16bit +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc-32bit) + my_static_libraries += libprotobuf-c-nano-enable_malloc-32bit +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-32bit) + my_static_libraries += libprotobuf-c-nano-32bit +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),full) + ifdef LOCAL_SDK_VERSION + my_static_libraries += libprotobuf-cpp-full-ndk + else + my_shared_libraries += libprotobuf-cpp-full + endif +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),lite-static) + my_static_libraries += libprotobuf-cpp-lite +else + ifdef LOCAL_SDK_VERSION + my_static_libraries += libprotobuf-cpp-lite-ndk + else + my_shared_libraries += libprotobuf-cpp-lite + endif +endif +endif # $(proto_sources) non-empty + +########################################################### +## AIDL: Compile .aidl files to .cpp and .h files +########################################################### +aidl_src := $(strip $(filter %.aidl,$(my_src_files))) +aidl_gen_cpp := +ifneq ($(aidl_src),) + +# Use the intermediates directory to avoid writing our own .cpp -> .o rules. +aidl_gen_cpp_root := $(intermediates)/aidl-generated/src +aidl_gen_include_root := $(intermediates)/aidl-generated/include + +# Multi-architecture builds have distinct intermediates directories. +# Thus we'll actually generate source for each architecture. +$(foreach s,$(aidl_src),\ + $(eval $(call define-aidl-cpp-rule,$(s),$(aidl_gen_cpp_root),aidl_gen_cpp))) +$(foreach cpp,$(aidl_gen_cpp), \ + $(call include-depfile,$(addsuffix .aidl.d,$(basename $(cpp))),$(cpp))) +$(call track-src-file-gen,$(aidl_src),$(aidl_gen_cpp)) + +$(aidl_gen_cpp) : PRIVATE_MODULE := $(LOCAL_MODULE) +$(aidl_gen_cpp) : PRIVATE_HEADER_OUTPUT_DIR := $(aidl_gen_include_root) +$(aidl_gen_cpp) : PRIVATE_AIDL_FLAGS := $(addprefix -I,$(LOCAL_AIDL_INCLUDES)) + +# Add generated headers to include paths. +my_c_includes += $(aidl_gen_include_root) +my_export_c_include_dirs += $(aidl_gen_include_root) +# Pick up the generated C++ files later for transformation to .o files. +my_generated_sources += $(aidl_gen_cpp) + +endif # $(aidl_src) non-empty + +########################################################### +## Compile the .vts files to .cc (or .c) and then to .o +########################################################### + +vts_src := $(strip $(filter %.vts,$(my_src_files))) +vts_gen_cpp := +ifneq ($(vts_src),) +my_soong_problems += vts + +# Use the intermediates directory to avoid writing our own .cpp -> .o rules. +vts_gen_cpp_root := $(intermediates)/vts-generated/src +vts_gen_include_root := $(intermediates)/vts-generated/include + +# Multi-architecture builds have distinct intermediates directories. +# Thus we'll actually generate source for each architecture. +$(foreach s,$(vts_src),\ + $(eval $(call define-vts-cpp-rule,$(s),$(vts_gen_cpp_root),vts_gen_cpp))) +$(call track-src-file-gen,$(vts_src),$(vts_gen_cpp)) + +$(vts_gen_cpp) : PRIVATE_MODULE := $(LOCAL_MODULE) +$(vts_gen_cpp) : PRIVATE_HEADER_OUTPUT_DIR := $(vts_gen_include_root) +$(vts_gen_cpp) : PRIVATE_VTS_FLAGS := $(addprefix -I,$(LOCAL_VTS_INCLUDES)) $(addprefix -m,$(LOCAL_VTS_MODE)) + +# Add generated headers to include paths. +my_c_includes += $(vts_gen_include_root) +my_export_c_include_dirs += $(vts_gen_include_root) +# Pick up the generated C++ files later for transformation to .o files. +my_generated_sources += $(vts_gen_cpp) + +endif # $(vts_src) non-empty + +########################################################### +## YACC: Compile .y/.yy files to .c/.cpp and then to .o. +########################################################### + +y_yacc_sources := $(filter %.y,$(my_src_files)) +y_yacc_cs := $(addprefix \ + $(intermediates)/,$(y_yacc_sources:.y=.c)) +ifneq ($(y_yacc_cs),) +$(y_yacc_cs): $(intermediates)/%.c: \ + $(TOPDIR)$(LOCAL_PATH)/%.y $(BISON) $(BISON_DATA) $(M4) \ + $(my_additional_dependencies) + $(call transform-y-to-c-or-cpp) +$(call track-src-file-gen,$(y_yacc_sources),$(y_yacc_cs)) + +my_generated_sources += $(y_yacc_cs) +endif + +yy_yacc_sources := $(filter %.yy,$(my_src_files)) +yy_yacc_cpps := $(addprefix \ + $(intermediates)/,$(yy_yacc_sources:.yy=$(LOCAL_CPP_EXTENSION))) +ifneq ($(yy_yacc_cpps),) +$(yy_yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \ + $(TOPDIR)$(LOCAL_PATH)/%.yy $(BISON) $(BISON_DATA) $(M4) \ + $(my_additional_dependencies) + $(call transform-y-to-c-or-cpp) +$(call track-src-file-gen,$(yy_yacc_sources),$(yy_yacc_cpps)) + +my_generated_sources += $(yy_yacc_cpps) +endif + +########################################################### +## LEX: Compile .l/.ll files to .c/.cpp and then to .o. +########################################################### + +l_lex_sources := $(filter %.l,$(my_src_files)) +l_lex_cs := $(addprefix \ + $(intermediates)/,$(l_lex_sources:.l=.c)) +ifneq ($(l_lex_cs),) +$(l_lex_cs): $(LEX) $(M4) +$(l_lex_cs): $(intermediates)/%.c: \ + $(TOPDIR)$(LOCAL_PATH)/%.l + $(transform-l-to-c-or-cpp) +$(call track-src-file-gen,$(l_lex_sources),$(l_lex_cs)) + +my_generated_sources += $(l_lex_cs) +endif + +ll_lex_sources := $(filter %.ll,$(my_src_files)) +ll_lex_cpps := $(addprefix \ + $(intermediates)/,$(ll_lex_sources:.ll=$(LOCAL_CPP_EXTENSION))) +ifneq ($(ll_lex_cpps),) +$(ll_lex_cpps): $(LEX) $(M4) +$(ll_lex_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \ + $(TOPDIR)$(LOCAL_PATH)/%.ll + $(transform-l-to-c-or-cpp) +$(call track-src-file-gen,$(ll_lex_sources),$(ll_lex_cpps)) + +my_generated_sources += $(ll_lex_cpps) +endif + +########################################################### +## C++: Compile .cpp files to .o. +########################################################### + +ifneq ($(filter %$(LOCAL_CPP_EXTENSION).arm,$(my_src_files)),) +$(call pretty-error,Files ending in $(LOCAL_CPP_EXTENSION).arm are deprecated. See $(CHANGES_URL)#file_arm) +endif + +dotdot_sources := $(filter ../%$(LOCAL_CPP_EXTENSION),$(my_src_files)) +dotdot_objects := +$(foreach s,$(dotdot_sources),\ + $(eval $(call compile-dotdot-cpp-file,$(s),\ + $(my_additional_dependencies),\ + dotdot_objects,\ + $(my_pool)))) +$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects)) + +cpp_normal_sources := $(filter-out ../%,$(filter %$(LOCAL_CPP_EXTENSION),$(my_src_files))) +cpp_objects := $(addprefix $(intermediates)/,$(cpp_normal_sources:$(LOCAL_CPP_EXTENSION)=.o)) +$(call track-src-file-obj,$(cpp_normal_sources),$(cpp_objects)) + +$(dotdot_objects) $(cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) +$(dotdot_objects) $(cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) + +ifneq ($(strip $(cpp_objects)),) +$(cpp_objects): .KATI_NINJA_POOL := $(my_pool) +$(cpp_objects): $(intermediates)/%.o: \ + $(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \ + $(my_additional_dependencies) $(CLANG_CXX) + $(transform-$(PRIVATE_HOST)cpp-to-o) +$(call include-depfiles-for-objs, $(cpp_objects)) +endif + +cpp_objects += $(dotdot_objects) + +########################################################### +## C++: Compile generated .cpp files to .o. +########################################################### + +gen_cpp_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(my_generated_sources)) +gen_cpp_objects := $(gen_cpp_sources:%$(LOCAL_CPP_EXTENSION)=%.o) +$(call track-gen-file-obj,$(gen_cpp_sources),$(gen_cpp_objects)) + +ifneq ($(strip $(gen_cpp_objects)),) +# Compile all generated files as thumb. +$(gen_cpp_objects): .KATI_NINJA_POOL := $(my_pool) +$(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) +$(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) +$(gen_cpp_objects): $(intermediates)/%.o: \ + $(intermediates)/%$(LOCAL_CPP_EXTENSION) \ + $(my_additional_dependencies) $(CLANG_CXX) + $(transform-$(PRIVATE_HOST)cpp-to-o) +$(call include-depfiles-for-objs, $(gen_cpp_objects)) +endif + +########################################################### +## S: Compile generated .S and .s files to .o. +########################################################### + +gen_S_sources := $(filter %.S,$(my_generated_sources)) +gen_S_objects := $(gen_S_sources:%.S=%.o) +$(call track-gen-file-obj,$(gen_S_sources),$(gen_S_objects)) + +ifneq ($(strip $(gen_S_sources)),) +$(gen_S_objects): .KATI_NINJA_POOL := $(my_pool) +$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)s-to-o) +$(call include-depfiles-for-objs, $(gen_S_objects)) +endif + +gen_s_sources := $(filter %.s,$(my_generated_sources)) +gen_s_objects := $(gen_s_sources:%.s=%.o) +$(call track-gen-file-obj,$(gen_s_sources),$(gen_s_objects)) + +ifneq ($(strip $(gen_s_objects)),) +$(gen_s_objects): .KATI_NINJA_POOL := $(my_pool) +$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)s-to-o) +endif + +gen_asm_objects := $(gen_S_objects) $(gen_s_objects) +$(gen_asm_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) + +########################################################### +## o: Include generated .o files in output. +########################################################### + +gen_o_objects := $(filter %.o,$(my_generated_sources)) + +########################################################### +## C: Compile .c files to .o. +########################################################### + +ifneq ($(filter %.c.arm,$(my_src_files)),) +$(call pretty-error,Files ending in .c.arm are deprecated. See $(CHANGES_URL)#file_arm) +endif + +dotdot_sources := $(filter ../%.c, $(my_src_files)) +dotdot_objects := +$(foreach s, $(dotdot_sources),\ + $(eval $(call compile-dotdot-c-file,$(s),\ + $(my_additional_dependencies),\ + dotdot_objects,\ + $(my_pool)))) +$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects)) + +c_normal_sources := $(filter-out ../%,$(filter %.c,$(my_src_files))) +c_objects := $(addprefix $(intermediates)/,$(c_normal_sources:.c=.o)) +$(call track-src-file-obj,$(c_normal_sources),$(c_objects)) + +$(dotdot_objects) $(c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) +$(dotdot_objects) $(c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) + +ifneq ($(strip $(c_objects)),) +$(c_objects): .KATI_NINJA_POOL := $(my_pool) +$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)c-to-o) +$(call include-depfiles-for-objs, $(c_objects)) +endif + +c_objects += $(dotdot_objects) + +########################################################### +## C: Compile generated .c files to .o. +########################################################### + +gen_c_sources := $(filter %.c,$(my_generated_sources)) +gen_c_objects := $(gen_c_sources:%.c=%.o) +$(call track-gen-file-obj,$(gen_c_sources),$(gen_c_objects)) + +ifneq ($(strip $(gen_c_objects)),) +# Compile all generated files as thumb. +$(gen_c_objects): .KATI_NINJA_POOL := $(my_pool) +$(gen_c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) +$(gen_c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) +$(gen_c_objects): $(intermediates)/%.o: $(intermediates)/%.c \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)c-to-o) +$(call include-depfiles-for-objs, $(gen_c_objects)) +endif + +########################################################### +## ObjC: Compile .m files to .o +########################################################### + +objc_sources := $(filter %.m,$(my_src_files)) +objc_objects := $(addprefix $(intermediates)/,$(objc_sources:.m=.o)) +$(call track-src-file-obj,$(objc_sources),$(objc_objects)) + +ifneq ($(strip $(objc_objects)),) +my_soong_problems += objc +$(objc_objects): .KATI_NINJA_POOL := $(my_pool) +$(objc_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.m \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)m-to-o) +$(call include-depfiles-for-objs, $(objc_objects)) +endif + +########################################################### +## ObjC++: Compile .mm files to .o +########################################################### + +objcpp_sources := $(filter %.mm,$(my_src_files)) +objcpp_objects := $(addprefix $(intermediates)/,$(objcpp_sources:.mm=.o)) +$(call track-src-file-obj,$(objcpp_sources),$(objcpp_objects)) + +ifneq ($(strip $(objcpp_objects)),) +$(objcpp_objects): .KATI_NINJA_POOL := $(my_pool) +$(objcpp_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.mm \ + $(my_additional_dependencies) $(CLANG_CXX) + $(transform-$(PRIVATE_HOST)mm-to-o) +$(call include-depfiles-for-objs, $(objcpp_objects)) +endif + +########################################################### +## AS: Compile .S files to .o. +########################################################### + +asm_sources_S := $(filter %.S,$(my_src_files)) +dotdot_sources := $(filter ../%,$(asm_sources_S)) +asm_sources_S := $(filter-out ../%,$(asm_sources_S)) +asm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o)) +$(call track-src-file-obj,$(asm_sources_S),$(asm_objects_S)) + +dotdot_objects_S := +$(foreach s,$(dotdot_sources),\ + $(eval $(call compile-dotdot-s-file,$(s),\ + $(my_additional_dependencies),\ + dotdot_objects_S,\ + $(my_pool)))) +$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects_S)) + +ifneq ($(strip $(asm_objects_S)),) +$(asm_objects_S): .KATI_NINJA_POOL := $(my_pool) +$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)s-to-o) +$(call include-depfiles-for-objs, $(asm_objects_S)) +endif + +asm_sources_s := $(filter %.s,$(my_src_files)) +dotdot_sources := $(filter ../%,$(asm_sources_s)) +asm_sources_s := $(filter-out ../%,$(asm_sources_s)) +asm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o)) +$(call track-src-file-obj,$(asm_sources_s),$(asm_objects_s)) + +dotdot_objects_s := +$(foreach s,$(dotdot_sources),\ + $(eval $(call compile-dotdot-s-file-no-deps,$(s),\ + $(my_additional_dependencies),\ + dotdot_objects_s,\ + $(my_pool)))) +$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects_s)) + +ifneq ($(strip $(asm_objects_s)),) +$(asm_objects_s): .KATI_NINJA_POOL := $(my_pool) +$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s \ + $(my_additional_dependencies) $(CLANG) + $(transform-$(PRIVATE_HOST)s-to-o) +endif + +asm_objects := $(dotdot_objects_S) $(dotdot_objects_s) $(asm_objects_S) $(asm_objects_s) +$(asm_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) + + +# .asm for x86/x86_64 needs to be compiled with yasm. +asm_sources_asm := $(filter %.asm,$(my_src_files)) +ifneq ($(strip $(asm_sources_asm)),) +asm_objects_asm := $(addprefix $(intermediates)/,$(asm_sources_asm:.asm=.o)) +$(asm_objects_asm): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.asm \ + $(my_additional_dependencies) $(YASM) + $(transform-asm-to-o) +$(call track-src-file-obj,$(asm_sources_asm),$(asm_objects_asm)) + +asm_objects += $(asm_objects_asm) +endif + +################################################################### +## Convert to sanitized names where they exist. +## These lists come from sanitizerStaticLibsMap; see +## build/soong/cc/sanitize.go +## +## $(1): list of static dependencies +## $(2): name of sanitizer (e.g. cfi, hwasan) +################################################################## +define use_soong_sanitized_static_libraries + $(foreach lib,$(1),$(if $(filter $(lib),\ + $(SOONG_$(2)_$(my_image_variant)_$(my_arch)_STATIC_LIBRARIES)),\ + $(lib).$(2),$(lib))) +endef + +################################################################### +## When compiling a CFI enabled target, use the .cfi variant of any +## static dependencies (where they exist). +################################################################## +ifneq ($(filter cfi,$(my_sanitize)),) + my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\ + $(my_whole_static_libraries),cfi) + my_static_libraries := $(call use_soong_sanitized_static_libraries,\ + $(my_static_libraries),cfi) +endif + +################################################################### +## When compiling a hwasan enabled target, use the .hwasan variant +## of any static dependencies (where they exist). +################################################################## +ifneq ($(filter hwaddress,$(my_sanitize)),) + my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\ + $(my_whole_static_libraries),hwasan) + my_static_libraries := $(call use_soong_sanitized_static_libraries,\ + $(my_static_libraries),hwasan) +endif + +########################################################### +## When compiling against the VNDK, use LL-NDK libraries +########################################################### +ifneq ($(LOCAL_USE_VNDK),) + ##################################################### + ## Soong modules may be built three times, once for + ## /system, once for /vendor and once for /product. + ## If we're using the VNDK, switch all soong + ## libraries over to the /vendor or /product variant. + ##################################################### + ifeq ($(LOCAL_USE_VNDK_PRODUCT),true) + my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\ + $(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l))) + my_static_libraries := $(foreach l,$(my_static_libraries),\ + $(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l))) + my_shared_libraries := $(foreach l,$(my_shared_libraries),\ + $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) + my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries),\ + $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) + my_header_libraries := $(foreach l,$(my_header_libraries),\ + $(if $(SPLIT_PRODUCT.HEADER_LIBRARIES.$(l)),$(l).product,$(l))) + else + my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\ + $(if $(SPLIT_VENDOR.STATIC_LIBRARIES.$(l)),$(l).vendor,$(l))) + my_static_libraries := $(foreach l,$(my_static_libraries),\ + $(if $(SPLIT_VENDOR.STATIC_LIBRARIES.$(l)),$(l).vendor,$(l))) + my_shared_libraries := $(foreach l,$(my_shared_libraries),\ + $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) + my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries),\ + $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) + my_header_libraries := $(foreach l,$(my_header_libraries),\ + $(if $(SPLIT_VENDOR.HEADER_LIBRARIES.$(l)),$(l).vendor,$(l))) + endif +endif + +# Platform can use vendor public libraries. If a required shared lib is one of +# the vendor public libraries, the lib is switched to the stub version of the lib. +ifeq ($(LOCAL_USE_VNDK),) + my_shared_libraries := $(foreach l,$(my_shared_libraries),\ + $(if $(filter $(l),$(VENDOR_PUBLIC_LIBRARIES)),$(l).vendorpublic,$(l))) +endif + +########################################################### +## When compiling against the NDK, use SDK variants of Soong libraries +########################################################### + +ifneq ($(LOCAL_SDK_VERSION),) + my_whole_static_libraries := $(call use_soong_sdk_libraries,$(my_whole_static_libraries)) + my_static_libraries := $(call use_soong_sdk_libraries,$(my_static_libraries)) + my_shared_libraries := $(call use_soong_sdk_libraries,$(my_shared_libraries)) + my_system_shared_libraries := $(call use_soong_sdk_libraries,$(my_system_shared_libraries)) + my_header_libraries := $(call use_soong_sdk_libraries,$(my_header_libraries)) +endif + +########################################################## +## Set up installed module dependency +## We cannot compute the full path of the LOCAL_SHARED_LIBRARIES for +## they may cusomize their install path with LOCAL_MODULE_PATH +########################################################## +# Get the list of INSTALLED libraries as module names. +ifneq ($(LOCAL_SDK_VERSION),) + installed_shared_library_module_names := \ + $(my_shared_libraries) +else + installed_shared_library_module_names := \ + $(my_shared_libraries) $(my_system_shared_libraries) +endif + +# The real dependency will be added after all Android.mks are loaded and the install paths +# of the shared libraries are determined. +ifdef LOCAL_INSTALLED_MODULE +ifdef installed_shared_library_module_names +$(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ + $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(installed_shared_library_module_names)) +endif +endif + + +#################################################### +## Verify that NDK-built libraries only link against +## other NDK-built libraries +#################################################### + +include $(BUILD_SYSTEM)/allowed_ndk_types.mk + +ifdef LOCAL_SDK_VERSION +my_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type) +my_warn_types := $(my_warn_ndk_types) +my_allowed_types := $(my_allowed_ndk_types) +else ifdef LOCAL_USE_VNDK + _name := $(patsubst %.vendor,%,$(LOCAL_MODULE)) + _name := $(patsubst %.product,%,$(LOCAL_MODULE)) + ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),) + ifeq ($(filter $(_name),$(VNDK_PRIVATE_LIBRARIES)),) + my_link_type := native:vndk + else + my_link_type := native:vndk_private + endif + my_warn_types := + my_allowed_types := native:vndk native:vndk_private + else ifeq ($(LOCAL_USE_VNDK_PRODUCT),true) + # Modules installed to /product cannot directly depend on modules marked + # with vendor_available: false + my_link_type := native:product + my_warn_types := + my_allowed_types := native:product native:vndk native:platform_vndk + else + # Modules installed to /vendor cannot directly depend on modules marked + # with vendor_available: false + my_link_type := native:vendor + my_warn_types := + my_allowed_types := native:vendor native:vndk native:platform_vndk + endif +else ifneq ($(filter $(TARGET_RECOVERY_OUT)/%,$(call get_non_asan_path,$(LOCAL_MODULE_PATH))),) +my_link_type := native:recovery +my_warn_types := +# TODO(b/113303515) remove native:platform and my_allowed_ndk_types +my_allowed_types := native:recovery native:platform native:platform_vndk $(my_allowed_ndk_types) +else +my_link_type := native:platform +my_warn_types := $(my_warn_ndk_types) +my_allowed_types := $(my_allowed_ndk_types) native:platform native:platform_vndk +endif + +my_link_deps := $(addprefix STATIC_LIBRARIES:,$(my_whole_static_libraries) $(my_static_libraries)) +ifneq ($(filter-out STATIC_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),) +my_link_deps += $(addprefix SHARED_LIBRARIES:,$(my_shared_libraries)) +endif + +my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) +my_common := +include $(BUILD_SYSTEM)/link_type.mk + +########################################################### +## Common object handling. +########################################################### + +my_unused_src_files := $(filter-out $(logtags_sources) $(my_tracked_src_files),$(my_src_files) $(my_gen_src_files)) +ifneq ($(my_unused_src_files),) + $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Unused source files: $(my_unused_src_files)) +endif + +# some rules depend on asm_objects being first. If your code depends on +# being first, it's reasonable to require it to be assembly +normal_objects := \ + $(asm_objects) \ + $(cpp_objects) \ + $(gen_cpp_objects) \ + $(gen_asm_objects) \ + $(c_objects) \ + $(gen_c_objects) \ + $(objc_objects) \ + $(objcpp_objects) + +new_order_normal_objects := $(foreach f,$(my_src_files),$(my_src_file_obj_$(f))) +new_order_normal_objects += $(foreach f,$(my_gen_src_files),$(my_src_file_obj_$(f))) + +ifneq ($(sort $(normal_objects)),$(sort $(new_order_normal_objects))) +$(warning $(LOCAL_MODULE_MAKEFILE) Internal build system warning: New object list does not match old) +$(info Only in old: $(filter-out $(new_order_normal_objects),$(sort $(normal_objects)))) +$(info Only in new: $(filter-out $(normal_objects),$(sort $(new_order_normal_objects)))) +endif + +ifeq ($(BINARY_OBJECTS_ORDER),soong) +normal_objects := $(new_order_normal_objects) +endif + +normal_objects += $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES)) + +all_objects := $(normal_objects) $(gen_o_objects) + +LOCAL_INTERMEDIATE_TARGETS += $(all_objects) + +# Cleanup file tracking +$(foreach f,$(my_tracked_gen_files),$(eval my_src_file_gen_$(s):=)) +my_tracked_gen_files := +$(foreach f,$(my_tracked_src_files),$(eval my_src_file_obj_$(s):=)) +my_tracked_src_files := + +my_c_includes += $(TOPDIR)$(LOCAL_PATH) $(intermediates) $(generated_sources_dir) + +my_c_includes := $(foreach inc,$(my_c_includes),$(call clean-path,$(inc))) + +my_outside_includes := $(filter-out $(OUT_DIR)/%,$(filter /%,$(my_c_includes)) $(filter ../%,$(my_c_includes))) +ifneq ($(my_outside_includes),) + ifeq ($(BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS),true) + $(call pretty-warning,C_INCLUDES must be under the source or output directories: $(my_outside_includes)) + else + $(call pretty-error,C_INCLUDES must be under the source or output directories: $(my_outside_includes)) + endif +endif + +# all_objects includes gen_o_objects which were part of LOCAL_GENERATED_SOURCES; +# use normal_objects here to avoid creating circular dependencies. This assumes +# that custom build rules which generate .o files don't consume other generated +# sources as input (or if they do they take care of that dependency themselves). +$(normal_objects) : | $(my_generated_sources) +ALL_C_CPP_ETC_OBJECTS += $(all_objects) + + +########################################################### +# Standard library handling. +########################################################### + +########################################################### +# The list of libraries that this module will link against are in +# these variables. Each is a list of bare module names like "libc libm". +# +# LOCAL_SHARED_LIBRARIES +# LOCAL_STATIC_LIBRARIES +# LOCAL_WHOLE_STATIC_LIBRARIES +# +# We need to convert the bare names into the dependencies that +# we'll use for LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE. +# LOCAL_BUILT_MODULE should depend on the BUILT versions of the +# libraries, so that simply building this module doesn't force +# an install of a library. Similarly, LOCAL_INSTALLED_MODULE +# should depend on the INSTALLED versions of the libraries so +# that they get installed when this module does. +########################################################### +# NOTE: +# WHOLE_STATIC_LIBRARIES are libraries that are pulled into the +# module without leaving anything out, which is useful for turning +# a collection of .a files into a .so file. Linking against a +# normal STATIC_LIBRARY will only pull in code/symbols that are +# referenced by the module. (see gcc/ld's --whole-archive option) +########################################################### + +# Get the list of BUILT libraries, which are under +# various intermediates directories. +so_suffix := $($(my_prefix)SHLIB_SUFFIX) +a_suffix := $($(my_prefix)STATIC_LIB_SUFFIX) + +ifneq ($(LOCAL_SDK_VERSION),) +built_shared_libraries := \ + $(foreach lib,$(my_shared_libraries), \ + $(call intermediates-dir-for, \ + SHARED_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(so_suffix)) +built_shared_library_deps := $(addsuffix .toc, $(built_shared_libraries)) + +# Add the NDK libraries to the built module dependency +my_system_shared_libraries_fullpath := \ + $(my_ndk_stl_shared_lib_fullpath) \ + $(addprefix $(my_ndk_sysroot_lib)/, \ + $(addsuffix $(so_suffix), $(my_system_shared_libraries))) + +# We need to preserve the ordering of LOCAL_SHARED_LIBRARIES regardless of +# whether the libs are generated or prebuilt, so we simply can't split into two +# lists and use addprefix. +my_ndk_shared_libraries_fullpath := \ + $(foreach _lib,$(my_ndk_shared_libraries),\ + $(if $(filter $(NDK_KNOWN_LIBS),$(_lib)),\ + $(my_built_ndk_libs)/$(_lib)$(so_suffix),\ + $(my_ndk_sysroot_lib)/$(_lib)$(so_suffix))) + +built_shared_libraries += \ + $(my_ndk_shared_libraries_fullpath) \ + $(my_system_shared_libraries_fullpath) \ + +built_shared_library_deps += \ + $(my_ndk_shared_libraries_fullpath) \ + $(my_system_shared_libraries_fullpath) \ + +else +built_shared_libraries := \ + $(foreach lib,$(installed_shared_library_module_names), \ + $(call intermediates-dir-for, \ + SHARED_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(so_suffix)) +built_shared_library_deps := $(addsuffix .toc, $(built_shared_libraries)) +my_system_shared_libraries_fullpath := +endif + +built_static_libraries := \ + $(foreach lib,$(my_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix)) + +ifdef LOCAL_SDK_VERSION +built_static_libraries += $(my_ndk_stl_static_lib) +endif + +built_whole_libraries := \ + $(foreach lib,$(my_whole_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix)) + +# We don't care about installed static libraries, since the +# libraries have already been linked into the module at that point. +# We do, however, care about the NOTICE files for any static +# libraries that we use. (see notice_files.mk) +installed_static_library_notice_file_targets := \ + $(foreach lib,$(my_static_libraries) $(my_whole_static_libraries), \ + NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-STATIC_LIBRARIES-$(lib)) + +$(notice_target): | $(installed_static_library_notice_file_targets) +$(LOCAL_INSTALLED_MODULE): | $(notice_target) + +# Default is -fno-rtti. +ifeq ($(strip $(LOCAL_RTTI_FLAG)),) +LOCAL_RTTI_FLAG := -fno-rtti +endif + +########################################################### +# Rule-specific variable definitions +########################################################### + +my_cflags += $(LOCAL_CLANG_CFLAGS) +my_conlyflags += $(LOCAL_CLANG_CONLYFLAGS) +my_cppflags += $(LOCAL_CLANG_CPPFLAGS) +my_asflags += $(LOCAL_CLANG_ASFLAGS) +my_ldflags += $(LOCAL_CLANG_LDFLAGS) +my_cflags += $(LOCAL_CLANG_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CFLAGS_$(my_32_64_bit_suffix)) +my_conlyflags += $(LOCAL_CLANG_CONLYFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CONLYFLAGS_$(my_32_64_bit_suffix)) +my_cppflags += $(LOCAL_CLANG_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CPPFLAGS_$(my_32_64_bit_suffix)) +my_ldflags += $(LOCAL_CLANG_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_LDFLAGS_$(my_32_64_bit_suffix)) +my_asflags += $(LOCAL_CLANG_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_ASFLAGS_$(my_32_64_bit_suffix)) +my_cflags := $(call convert-to-clang-flags,$(my_cflags)) +my_cppflags := $(call convert-to-clang-flags,$(my_cppflags)) +my_asflags := $(call convert-to-clang-flags,$(my_asflags)) +my_ldflags := $(call convert-to-clang-flags,$(my_ldflags)) + +# No one should ever use this flag. On GCC it's mere presence will disable all +# warnings, even those that are specified after it (contrary to typical warning +# flag behavior). This circumvents CFLAGS_NO_OVERRIDE from forcibly enabling the +# warnings that are *always* bugs. +my_illegal_flags := -w +my_cflags := $(filter-out $(my_illegal_flags),$(my_cflags)) +my_cppflags := $(filter-out $(my_illegal_flags),$(my_cppflags)) +my_conlyflags := $(filter-out $(my_illegal_flags),$(my_conlyflags)) + +# We can enforce some rules more strictly in the code we own. my_strict +# indicates if this is code that we can be stricter with. If we have rules that +# we want to apply to *our* code (but maybe can't for vendor/device specific +# things), we could extend this to be a ternary value. +my_strict := true +ifneq ($(filter external/%,$(LOCAL_PATH)),) + my_strict := false +endif + +# Can be used to make some annotations stricter for code we can fix (such as +# when we mark functions as deprecated). +ifeq ($(my_strict),true) + my_cflags += -DANDROID_STRICT +endif + +# Check if -Werror or -Wno-error is used in C compiler flags. +# Header libraries do not need cflags. +my_all_cflags := $(my_cflags) $(my_cppflags) $(my_cflags_no_override) +ifneq (HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)) + # Prebuilt modules do not need cflags. + ifeq (,$(LOCAL_PREBUILT_MODULE_FILE)) + # Issue warning if -Wno-error is used. + ifneq (,$(filter -Wno-error,$(my_all_cflags))) + $(eval MODULES_USING_WNO_ERROR := $(MODULES_USING_WNO_ERROR) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE)) + else + # Issue warning if -Werror is not used. Add it. + ifeq (,$(filter -Werror,$(my_all_cflags))) + # Add -Wall -Werror unless the project is in the WARNING_ALLOWED project list. + ifeq (,$(strip $(call find_warning_allowed_projects,$(LOCAL_PATH)))) + my_cflags := -Wall -Werror $(my_cflags) + else + $(eval MODULES_ADDED_WALL := $(MODULES_ADDED_WALL) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE)) + my_cflags := -Wall $(my_cflags) + endif + endif + endif + endif +endif + +ifneq (,$(filter -Weverything,$(my_all_cflags))) + ifeq (,$(ANDROID_TEMPORARILY_ALLOW_WEVERYTHING)) + $(call pretty-error, -Weverything is not allowed in Android.mk files.\ + Build with `m ANDROID_TEMPORARILY_ALLOW_WEVERYTHING=true` to experiment locally with -Weverything.) + endif +endif + +ifneq ($(my_tidy_checks),) + tidy_only: $(cpp_objects) $(c_objects) $(gen_c_objects) $(gen_cpp_objects) + + # Add dependency of clang-tidy and clang-tidy.sh + $(cpp_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) + $(c_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) + $(gen_cpp_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) + $(gen_c_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) +endif + +# Move -l* entries from ldflags to ldlibs, and everything else to ldflags +my_ldlib_flags := $(my_ldflags) $(my_ldlibs) +my_ldlibs := $(filter -l%,$(my_ldlib_flags)) +my_ldflags := $(filter-out -l%,$(my_ldlib_flags)) + +# One last verification check for ldlibs +my_allowed_ldlibs := +ifndef LOCAL_IS_HOST_MODULE + ifneq ($(LOCAL_SDK_VERSION),) + my_allowed_ldlibs := $(NDK_KNOWN_LIBS:lib%=-l%) + endif +else + my_allowed_ldlibs := $($(my_prefix)AVAILABLE_LIBRARIES) +endif + +my_bad_ldlibs := $(filter-out $(my_allowed_ldlibs),$(my_ldlibs)) +ifneq ($(my_bad_ldlibs),) + $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Bad LOCAL_LDLIBS entries: $(my_bad_ldlibs)) +endif + +# my_cxx_ldlibs may contain linker flags need to wrap certain libraries +# (start-group/end-group), so append after the check above. +my_ldlibs += $(my_cxx_ldlibs) + +########################################################### +## Define PRIVATE_ variables from global vars +########################################################### +ifndef LOCAL_IS_HOST_MODULE + +ifdef LOCAL_USE_VNDK + my_target_global_c_includes := + my_target_global_c_system_includes := $(TARGET_OUT_HEADERS) +else ifdef LOCAL_SDK_VERSION + my_target_global_c_includes := + my_target_global_c_system_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include) +else ifdef BOARD_VNDK_VERSION + my_target_global_c_includes := $(SRC_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) + my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) +else + my_target_global_c_includes := $(SRC_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) + my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) $(TARGET_OUT_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) +endif + +my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS) +my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags) +my_target_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags) +ifeq ($(my_use_clang_lld),true) + my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LLDFLAGS) + include $(BUILD_SYSTEM)/pack_dyn_relocs_setup.mk + ifeq ($(my_pack_module_relocations),true) + my_target_global_ldflags += -Wl,--pack-dyn-relocs=android+relr -Wl,--use-android-relr-tags + else + my_target_global_ldflags += -Wl,--pack-dyn-relocs=none + endif +else + my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS) +endif # my_use_clang_lld + +my_target_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)TRIPLE) +ifndef LOCAL_IS_HOST_MODULE + my_target_triple_flag := -target $(my_target_triple)$(my_api_level) +else + my_target_triple_flag := -target $(my_target_triple) +endif +my_asflags += $(my_target_triple_flag) +my_cflags += $(my_target_triple_flag) +my_ldflags += $(my_target_triple_flag) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_target_global_c_includes) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_target_global_c_system_includes) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CFLAGS := $(my_target_global_cflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CONLYFLAGS := $(my_target_global_conlyflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CPPFLAGS := $(my_target_global_cppflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_LDFLAGS := $(my_target_global_ldflags) + +else # LOCAL_IS_HOST_MODULE + +my_host_global_c_includes := $(SRC_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) +my_host_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) + +my_host_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS) +my_host_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags) +my_host_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags) +ifeq ($(my_use_clang_lld),true) + my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LLDFLAGS) +else + my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS) +endif # my_use_clang_lld + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_host_global_c_includes) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_host_global_c_system_includes) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CFLAGS := $(my_host_global_cflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CONLYFLAGS := $(my_host_global_conlyflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CPPFLAGS := $(my_host_global_cppflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_LDFLAGS := $(my_host_global_ldflags) +endif # LOCAL_IS_HOST_MODULE + +# To enable coverage for a given module, set LOCAL_NATIVE_COVERAGE=true and +# build with NATIVE_COVERAGE=true in your enviornment. +ifeq ($(NATIVE_COVERAGE),true) + ifeq ($(my_native_coverage),true) + # Note that clang coverage doesn't play nicely with acov out of the box. + # Clang apparently generates .gcno files that aren't compatible with + # gcov-4.8. This can be solved by installing gcc-4.6 and invoking lcov + # with `--gcov-tool /usr/bin/gcov-4.6`. + # + # http://stackoverflow.com/questions/17758126/clang-code-coverage-invalid-output + my_cflags += --coverage -O0 + my_ldflags += --coverage + endif + + my_coverage_lib := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT) + + $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_COVERAGE_LIB := $(my_coverage_lib) + $(LOCAL_INTERMEDIATE_TARGETS): $(my_coverage_lib) +endif + +#################################################### +## Import includes +#################################################### +imported_includes := + +ifdef LOCAL_USE_VNDK + imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)) +else ifdef LOCAL_SDK_VERSION + # Apps shouldn't need device-specific kernel headers +else ifdef BOARD_VNDK_VERSION + # For devices building with the VNDK, only the VNDK gets device-specific kernel headers by default + # In soong, it's entirely opt-in +else + # For older non-VNDK builds, continue adding in kernel headers to everything like we used to + imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)) +endif + +imported_includes := $(strip \ + $(imported_includes) \ + $(foreach l, $(installed_shared_library_module_names), \ + $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \ + $(foreach l, $(my_static_libraries) $(my_whole_static_libraries), \ + $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \ + $(foreach l, $(my_header_libraries), \ + $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) + +$(foreach dep,$(imported_includes),\ + $(eval EXPORTS.$$(dep).USERS := $$(EXPORTS.$$(dep).USERS) $$(all_objects))) + +########################################################### +## Define PRIVATE_ variables used by multiple module types +########################################################### +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_NO_DEFAULT_COMPILER_FLAGS := \ + $(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS)) + +ifeq ($(strip $(WITH_STATIC_ANALYZER)),) + LOCAL_NO_STATIC_ANALYZER := true +endif + +ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),) + my_syntax_arch := host +else + my_syntax_arch := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) +endif + +ifeq ($(strip $(my_cc)),) + my_cc := $(my_cc_wrapper) $(CLANG) +endif + +SYNTAX_TOOLS_PREFIX := \ + $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/libexec + +ifneq ($(LOCAL_NO_STATIC_ANALYZER),true) + my_cc := CCC_CC=$(CLANG) CLANG=$(CLANG) \ + $(SYNTAX_TOOLS_PREFIX)/ccc-analyzer +endif + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CC := $(my_cc) + +ifeq ($(strip $(my_cxx)),) + my_cxx := $(my_cxx_wrapper) $(CLANG_CXX) +endif + +ifeq ($(strip $(my_cxx_link)),) + my_cxx_link := $(CLANG_CXX) +endif + +ifneq ($(LOCAL_NO_STATIC_ANALYZER),true) + my_cxx := CCC_CXX=$(CLANG_CXX) CLANG_CXX=$(CLANG_CXX) \ + $(SYNTAX_TOOLS_PREFIX)/c++-analyzer + my_cxx_link := CCC_CXX=$(CLANG_CXX) CLANG_CXX=$(CLANG_CXX) \ + $(SYNTAX_TOOLS_PREFIX)/c++-analyzer +endif + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LINKER := $(my_linker) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX := $(my_cxx) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX_LINK := $(my_cxx_link) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_YACCFLAGS := $(LOCAL_YACCFLAGS) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASFLAGS := $(my_asflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CONLYFLAGS := $(my_conlyflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS := $(my_cflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS := $(my_cppflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS_NO_OVERRIDE := $(my_cflags_no_override) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS_NO_OVERRIDE := $(my_cppflags_no_override) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RTTI_FLAG := $(LOCAL_RTTI_FLAG) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEBUG_CFLAGS := $(debug_cflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_C_INCLUDES := $(my_c_includes) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_IMPORTED_INCLUDES := $(imported_includes) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDFLAGS := $(my_ldflags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDLIBS := $(my_ldlibs) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_CHECKS := $(my_tidy_checks) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_FLAGS := $(my_tidy_flags) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ARFLAGS := $(my_arflags) + +# this is really the way to get the files onto the command line instead +# of using $^, because then LOCAL_ADDITIONAL_DEPENDENCIES doesn't work +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_SHARED_LIBRARIES := $(built_shared_libraries) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_STATIC_LIBRARIES := $(built_static_libraries) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(built_whole_libraries) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_OBJECTS := $(strip $(all_objects)) + +########################################################### +# Define library dependencies. +########################################################### +# all_libraries is used for the dependencies on LOCAL_BUILT_MODULE. +all_libraries := \ + $(built_shared_library_deps) \ + $(my_system_shared_libraries_fullpath) \ + $(built_static_libraries) \ + $(built_whole_libraries) + +########################################################### +# Export includes +########################################################### + +# Headers exported by whole static libraries are also exported by this library. +export_include_deps := $(strip \ + $(foreach l,$(my_whole_static_libraries), \ + $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) +# Re-export requested headers from shared libraries. +export_include_deps += $(strip \ + $(foreach l,$(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS), \ + $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) +# Re-export requested headers from static libraries. +export_include_deps += $(strip \ + $(foreach l,$(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS), \ + $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) +# Re-export requested headers from header libraries. +export_include_deps += $(strip \ + $(foreach l,$(LOCAL_EXPORT_HEADER_LIBRARY_HEADERS), \ + $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) + +ifneq ($(strip $(my_export_c_include_dirs)$(export_include_deps)),) + EXPORTS_LIST += $(intermediates) + EXPORTS.$(intermediates).FLAGS := $(foreach d,$(my_export_c_include_dirs),-I $(call clean-path,$(d))) + EXPORTS.$(intermediates).REEXPORT := $(export_include_deps) + EXPORTS.$(intermediates).DEPS := $(my_export_c_include_deps) $(my_generated_sources) $(LOCAL_EXPORT_C_INCLUDE_DEPS) +endif + +ifneq (,$(filter-out $(LOCAL_PATH)/%,$(my_export_c_include_dirs))) +my_soong_problems += non_local__export_c_include_dirs +endif + +SOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \ + $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems) +SOONG_CONV.$(LOCAL_MODULE).DEPS := \ + $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \ + $(filter-out $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_RUNTIME_LIBRARY),\ + $(my_static_libraries) \ + $(my_whole_static_libraries) \ + $(my_shared_libraries) \ + $(my_system_shared_libraries)) +SOONG_CONV.$(LOCAL_MODULE).TYPE := native +SOONG_CONV.$(LOCAL_MODULE).MAKEFILES := \ + $(SOONG_CONV.$(LOCAL_MODULE).MAKEFILES) $(LOCAL_MODULE_MAKEFILE) +SOONG_CONV.$(LOCAL_MODULE).INSTALLED:= \ + $(SOONG_CONV.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE) +SOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE) + +########################################################### +# Coverage packaging. +########################################################### +ifeq ($(my_native_coverage),true) +my_gcno_objects := \ + $(cpp_objects) \ + $(gen_cpp_objects) \ + $(c_objects) \ + $(gen_c_objects) \ + $(objc_objects) \ + $(objcpp_objects) + +LOCAL_GCNO_FILES := $(patsubst %.o,%.gcno,$(my_gcno_objects)) +$(foreach f,$(my_gcno_objects),$(eval $(call gcno-touch-rule,$(f),$(f:.o=.gcno)))) +endif diff --git a/make/core/board_config.mk b/make/core/board_config.mk new file mode 100644 index 0000000..dc50a68 --- /dev/null +++ b/make/core/board_config.mk @@ -0,0 +1,1118 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ############################################################### +# This file includes BoardConfig.mk for the device being built, +# and checks the variable defined therein. +# ############################################################### + +_board_strip_readonly_list := +_board_strip_readonly_list += BOARD_BOOTLOADER_IN_UPDATE_PACKAGE +_board_strip_readonly_list += BOARD_EGL_CFG +_board_strip_readonly_list += BOARD_HAVE_BLUETOOTH +_board_strip_readonly_list += BOARD_INSTALLER_CMDLINE +_board_strip_readonly_list += BOARD_KERNEL_CMDLINE +_board_strip_readonly_list += BOARD_BOOT_HEADER_VERSION +_board_strip_readonly_list += BOARD_BOOTCONFIG +_board_strip_readonly_list += BOARD_KERNEL_BASE +_board_strip_readonly_list += BOARD_USES_GENERIC_AUDIO +_board_strip_readonly_list += BOARD_USES_RECOVERY_AS_BOOT +_board_strip_readonly_list += BOARD_VENDOR_USE_AKMD +_board_strip_readonly_list += BOARD_WPA_SUPPLICANT_DRIVER +_board_strip_readonly_list += BOARD_WLAN_DEVICE +_board_strip_readonly_list += TARGET_BOARD_PLATFORM +_board_strip_readonly_list += TARGET_BOARD_PLATFORM_GPU +_board_strip_readonly_list += TARGET_BOOTLOADER_BOARD_NAME +_board_strip_readonly_list += TARGET_FS_CONFIG_GEN +_board_strip_readonly_list += TARGET_NO_BOOTLOADER +_board_strip_readonly_list += TARGET_NO_KERNEL +_board_strip_readonly_list += TARGET_NO_RECOVERY +_board_strip_readonly_list += TARGET_NO_RADIOIMAGE +_board_strip_readonly_list += TARGET_HARDWARE_3D +_board_strip_readonly_list += WITH_DEXPREOPT + +# Arch variables +_board_strip_readonly_list += TARGET_ARCH +_board_strip_readonly_list += TARGET_ARCH_VARIANT +_board_strip_readonly_list += TARGET_CPU_ABI +_board_strip_readonly_list += TARGET_CPU_ABI2 +_board_strip_readonly_list += TARGET_CPU_VARIANT +_board_strip_readonly_list += TARGET_CPU_VARIANT_RUNTIME +_board_strip_readonly_list += TARGET_2ND_ARCH +_board_strip_readonly_list += TARGET_2ND_ARCH_VARIANT +_board_strip_readonly_list += TARGET_2ND_CPU_ABI +_board_strip_readonly_list += TARGET_2ND_CPU_ABI2 +_board_strip_readonly_list += TARGET_2ND_CPU_VARIANT +_board_strip_readonly_list += TARGET_2ND_CPU_VARIANT_RUNTIME +# TARGET_ARCH_SUITE is an alternative arch configuration to TARGET_ARCH (and related variables), +# that can be used for soong-only builds to build for several architectures at once. +# Allowed values currently are "ndk" and "mainline_sdk". +_board_strip_readonly_list += TARGET_ARCH_SUITE + +# File system variables +_board_strip_readonly_list += BOARD_FLASH_BLOCK_SIZE +_board_strip_readonly_list += BOARD_BOOTIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_RECOVERYIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_USERDATAIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_CACHEIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_VENDORIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_PRODUCTIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_ODMIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_ODMIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE +_board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE +_board_strip_readonly_list += BOARD_PVMFWIMAGE_PARTITION_SIZE + +# Logical partitions related variables. +_board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE +_board_strip_readonly_list += BOARD_SUPER_PARTITION_SIZE +_board_strip_readonly_list += BOARD_SUPER_PARTITION_GROUPS + +# Kernel related variables +_board_strip_readonly_list += BOARD_KERNEL_BINARIES +_board_strip_readonly_list += BOARD_KERNEL_MODULE_INTERFACE_VERSIONS + +# Variables related to generic kernel image (GKI) and generic boot image +# - BOARD_USES_GENERIC_KERNEL_IMAGE is the global variable that defines if the +# board uses GKI and generic boot image. +# Update mechanism of the boot image is not enforced by this variable. +# - BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE controls whether the recovery image +# contains a kernel or not. +# - BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT controls whether ramdisk +# recovery resources are built to vendor_boot. +# - BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT controls whether recovery +# resources are built as a standalone recovery ramdisk in vendor_boot. +# - BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT controls whether GSI AVB keys are +# built to vendor_boot. +# - BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES controls whether boot images in $OUT are added +# to target files package directly. +_board_strip_readonly_list += BOARD_USES_GENERIC_KERNEL_IMAGE +_board_strip_readonly_list += BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE +_board_strip_readonly_list += BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT +_board_strip_readonly_list += BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT +_board_strip_readonly_list += BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT +_board_strip_readonly_list += BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES + +# Prebuilt image variables +_board_strip_readonly_list += BOARD_PREBUILT_INIT_BOOT_IMAGE + +# Defines the list of logical vendor ramdisk names to build or include in vendor_boot. +_board_strip_readonly_list += BOARD_VENDOR_RAMDISK_FRAGMENTS + +# These are all variables used to build $(INSTALLED_MISC_INFO_TARGET) +# in build/make/core/Makefile. Their values get used in command line +# arguments, so they have to be stripped to make the ninja files stable. +_board_strip_list := +_board_strip_list += BOARD_DTBOIMG_PARTITION_SIZE +_board_strip_list += BOARD_AVB_DTBO_KEY_PATH +_board_strip_list += BOARD_AVB_DTBO_ALGORITHM +_board_strip_list += BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_AVB_PVMFW_KEY_PATH +_board_strip_list += BOARD_AVB_PVMFW_ALGORITHM +_board_strip_list += BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST +_board_strip_list += BOARD_BPT_DISK_SIZE +_board_strip_list += BOARD_BPT_INPUT_FILES +_board_strip_list += BOARD_BPT_MAKE_TABLE_ARGS +_board_strip_list += BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_AVB_VBMETA_VENDOR_ALGORITHM +_board_strip_list += BOARD_AVB_VBMETA_VENDOR_KEY_PATH +_board_strip_list += BOARD_AVB_VBMETA_VENDOR +_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ALGORITHM +_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_KEY_PATH +_board_strip_list += BOARD_AVB_VBMETA_SYSTEM +_board_strip_list += BOARD_AVB_RECOVERY_KEY_PATH +_board_strip_list += BOARD_AVB_RECOVERY_ALGORITHM +_board_strip_list += BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_AVB_VENDOR_BOOT_KEY_PATH +_board_strip_list += BOARD_AVB_VENDOR_BOOT_ALGORITHM +_board_strip_list += BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH +_board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_ALGORITHM +_board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_ROLLBACK_INDEX_LOCATION +_board_strip_list += BOARD_GKI_SIGNING_SIGNATURE_ARGS +_board_strip_list += BOARD_GKI_SIGNING_ALGORITHM +_board_strip_list += BOARD_GKI_SIGNING_KEY_PATH +_board_strip_list += BOARD_MKBOOTIMG_ARGS +_board_strip_list += BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE +_board_strip_list += BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE +_board_strip_list += ODM_MANIFEST_SKUS + + +_build_broken_var_list := \ + BUILD_BROKEN_DUP_RULES \ + BUILD_BROKEN_DUP_SYSPROP \ + BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES \ + BUILD_BROKEN_ENFORCE_SYSPROP_OWNER \ + BUILD_BROKEN_INPUT_DIR_MODULES \ + BUILD_BROKEN_MISSING_REQUIRED_MODULES \ + BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS \ + BUILD_BROKEN_PREBUILT_ELF_FILES \ + BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW \ + BUILD_BROKEN_USES_NETWORK \ + BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE \ + BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES \ + +_build_broken_var_list += \ + $(foreach m,$(AVAILABLE_BUILD_MODULE_TYPES) \ + $(DEFAULT_WARNING_BUILD_MODULE_TYPES) \ + $(DEFAULT_ERROR_BUILD_MODULE_TYPES), \ + BUILD_BROKEN_USES_$(m)) + +_board_true_false_vars := $(_build_broken_var_list) +_board_strip_readonly_list += $(_build_broken_var_list) \ + BUILD_BROKEN_NINJA_USES_ENV_VARS + +# Conditional to building on linux, as dex2oat currently does not work on darwin. +ifeq ($(HOST_OS),linux) + WITH_DEXPREOPT := true +endif + +# ############################################################### +# Broken build defaults +# ############################################################### +$(foreach v,$(_build_broken_var_list),$(eval $(v) :=)) +BUILD_BROKEN_NINJA_USES_ENV_VARS := + +# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE) +# or under vendor/*/$(TARGET_DEVICE). Search in both places, but +# make sure only one exists. +# Real boards should always be associated with an OEM vendor. +ifdef TARGET_DEVICE_DIR + ifneq ($(origin TARGET_DEVICE_DIR),command line) + $(error TARGET_DEVICE_DIR may not be set manually) + endif + board_config_mk := $(TARGET_DEVICE_DIR)/BoardConfig.mk +else + board_config_mk := \ + $(strip $(sort $(wildcard \ + $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \ + $(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \ + $(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \ + ))) + ifeq ($(board_config_mk),) + $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE)) + endif + ifneq ($(words $(board_config_mk)),1) + $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk)) + endif + TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk))) + .KATI_READONLY := TARGET_DEVICE_DIR +endif + +# TODO(colefaust) change this if to RBC_PRODUCT_CONFIG when +# the board configuration is known to work on everything +# the product config works on. +ifndef RBC_BOARD_CONFIG +include $(board_config_mk) +else + $(shell mkdir -p $(OUT_DIR)/rbc) + $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_board_config.mk) + + $(shell $(OUT_DIR)/mk2rbc \ + --mode=write -r --outdir $(OUT_DIR)/rbc \ + --boardlauncher=$(OUT_DIR)/rbc/boardlauncher.rbc \ + --input_variables=$(OUT_DIR)/rbc/make_vars_pre_board_config.mk \ + --makefile_list=$(OUT_DIR)/.module_paths/configuration.list \ + $(board_config_mk)) + ifneq ($(.SHELLSTATUS),0) + $(error board configuration converter failed: $(.SHELLSTATUS)) + endif + + $(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_board_config_results.mk \ + $(OUT_DIR)/rbcrun RBC_OUT="make" $(OUT_DIR)/rbc/boardlauncher.rbc) + ifneq ($(.SHELLSTATUS),0) + $(error board configuration runner failed: $(.SHELLSTATUS)) + endif + + include $(OUT_DIR)/rbc/rbc_board_config_results.mk +endif + +ifneq (,$(and $(TARGET_ARCH),$(TARGET_ARCH_SUITE))) + $(error $(board_config_mk) erroneously sets both TARGET_ARCH and TARGET_ARCH_SUITE) +endif +ifeq ($(TARGET_ARCH)$(TARGET_ARCH_SUITE),) + $(error Target architectures not defined by board config: $(board_config_mk)) +endif +ifeq ($(TARGET_CPU_ABI)$(TARGET_ARCH_SUITE),) + $(error TARGET_CPU_ABI not defined by board config: $(board_config_mk)) +endif + +ifneq ($(MALLOC_IMPL),) + $(warning *** Unsupported option MALLOC_IMPL defined by board config: $(board_config_mk).) + $(error Use `MALLOC_SVELTE := true` to configure jemalloc for low-memory) +endif +board_config_mk := + +# Clean up and verify BoardConfig variables +$(foreach var,$(_board_strip_readonly_list),$(eval $(var) := $$(strip $$($(var))))) +$(foreach var,$(_board_strip_list),$(eval $(var) := $$(strip $$($(var))))) +$(foreach var,$(_board_true_false_vars), \ + $(if $(filter-out true false,$($(var))), \ + $(error Valid values of $(var) are "true", "false", and "". Not "$($(var))"))) + +# Default *_CPU_VARIANT_RUNTIME to CPU_VARIANT if unspecified. +TARGET_CPU_VARIANT_RUNTIME := $(or $(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_CPU_VARIANT)) +TARGET_2ND_CPU_VARIANT_RUNTIME := $(or $(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_CPU_VARIANT)) + +ifdef TARGET_ARCH + # The combo makefiles check and set defaults for various CPU configuration + combo_target := TARGET_ + combo_2nd_arch_prefix := + include $(BUILD_SYSTEM)/combo/select.mk +endif + +ifdef TARGET_2ND_ARCH + combo_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) + include $(BUILD_SYSTEM)/combo/select.mk +endif + +.KATI_READONLY := $(_board_strip_readonly_list) + +INTERNAL_KERNEL_CMDLINE := $(BOARD_KERNEL_CMDLINE) +ifneq (,$(BOARD_BOOTCONFIG)) + INTERNAL_KERNEL_CMDLINE += bootconfig + INTERNAL_BOOTCONFIG := $(BOARD_BOOTCONFIG) +endif + +ifneq ($(filter %64,$(TARGET_ARCH)),) + TARGET_IS_64_BIT := true +endif + +ifeq (,$(filter true,$(TARGET_SUPPORTS_32_BIT_APPS) $(TARGET_SUPPORTS_64_BIT_APPS))) + TARGET_SUPPORTS_32_BIT_APPS := true +endif + +# Quick check to warn about likely cryptic errors later in the build. +ifeq ($(TARGET_IS_64_BIT),true) + ifeq (,$(filter true false,$(TARGET_SUPPORTS_64_BIT_APPS))) + $(error Building a 32-bit-app-only product on a 64-bit device. \ + If this is intentional, set TARGET_SUPPORTS_64_BIT_APPS := false) + endif +endif + +# "ro.product.cpu.abilist32" and "ro.product.cpu.abilist64" are +# comma separated lists of the 32 and 64 bit ABIs (in order of +# preference) that the target supports. If TARGET_CPU_ABI_LIST_{32,64}_BIT +# are defined by the board config, we use them. Else, we construct +# these lists based on whether TARGET_IS_64_BIT is set. +# +# Note that this assumes that the 2ND_CPU_ABI for a 64 bit target +# is always 32 bits. If this isn't the case, these variables should +# be overriden in the board configuration. +# +# Similarly, TARGET_NATIVE_BRIDGE_2ND_ABI for a 64 bit target is always +# 32 bits. Note that all CPU_ABIs are preferred over all NATIVE_BRIDGE_ABIs. +_target_native_bridge_abi_list_32_bit := +_target_native_bridge_abi_list_64_bit := + +ifeq (,$(TARGET_CPU_ABI_LIST_64_BIT)) + ifeq (true|true,$(TARGET_IS_64_BIT)|$(TARGET_SUPPORTS_64_BIT_APPS)) + TARGET_CPU_ABI_LIST_64_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2) + _target_native_bridge_abi_list_64_bit := $(TARGET_NATIVE_BRIDGE_ABI) + endif +endif + +# "arm64-v8a-hwasan", the ABI for libraries compiled with HWASAN, is supported +# in all builds with SANITIZE_TARGET=hwaddress. +ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),) + ifneq ($(filter arm64-v8a,$(TARGET_CPU_ABI_LIST_64_BIT)),) + TARGET_CPU_ABI_LIST_64_BIT := arm64-v8a-hwasan $(TARGET_CPU_ABI_LIST_64_BIT) + endif +endif + +ifeq (,$(TARGET_CPU_ABI_LIST_32_BIT)) + ifneq (true,$(TARGET_IS_64_BIT)) + TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2) + _target_native_bridge_abi_list_32_bit := $(TARGET_NATIVE_BRIDGE_ABI) + else + ifeq (true,$(TARGET_SUPPORTS_32_BIT_APPS)) + # For a 64 bit target, assume that the 2ND_CPU_ABI + # is a 32 bit ABI. + TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_2ND_CPU_ABI) $(TARGET_2ND_CPU_ABI2) + _target_native_bridge_abi_list_32_bit := $(TARGET_NATIVE_BRIDGE_2ND_ABI) + endif + endif +endif + +# "ro.product.cpu.abilist" is a comma separated list of ABIs (in order +# of preference) that the target supports. If a TARGET_CPU_ABI_LIST +# is specified by the board configuration, we use that. If not, we +# build a list out of the TARGET_CPU_ABIs specified by the config. +# Add NATIVE_BRIDGE_ABIs at the end to keep order of preference. +ifeq (,$(TARGET_CPU_ABI_LIST)) + TARGET_CPU_ABI_LIST := $(TARGET_CPU_ABI_LIST_64_BIT) $(TARGET_CPU_ABI_LIST_32_BIT) \ + $(_target_native_bridge_abi_list_64_bit) $(_target_native_bridge_abi_list_32_bit) +endif + +# Add NATIVE_BRIDGE_ABIs at the end of 32 and 64 bit CPU_ABIs to keep order of preference. +TARGET_CPU_ABI_LIST_32_BIT += $(_target_native_bridge_abi_list_32_bit) +TARGET_CPU_ABI_LIST_64_BIT += $(_target_native_bridge_abi_list_64_bit) + +# Strip whitespace from the ABI list string. +TARGET_CPU_ABI_LIST := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST))) +TARGET_CPU_ABI_LIST_32_BIT := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST_32_BIT))) +TARGET_CPU_ABI_LIST_64_BIT := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST_64_BIT))) + +# Check if config about image building is valid or not. +define check_image_config + $(eval _uc_name := $(call to-upper,$(1))) \ + $(eval _lc_name := $(call to-lower,$(1))) \ + $(if $(filter $(_lc_name),$(TARGET_COPY_OUT_$(_uc_name))), \ + $(if $(BOARD_USES_$(_uc_name)IMAGE),, \ + $(error If TARGET_COPY_OUT_$(_uc_name) is '$(_lc_name)', either BOARD_PREBUILT_$(_uc_name)IMAGE or BOARD_$(_uc_name)IMAGE_FILE_SYSTEM_TYPE must be set)), \ + $(if $(BOARD_USES_$(_uc_name)IMAGE), \ + $(error TARGET_COPY_OUT_$(_uc_name) must be set to '$(_lc_name)' to use a $(_lc_name) image))) \ + $(eval _uc_name :=) \ + $(eval _lc_name :=) +endef + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_RAMDISK +ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) +TARGET_COPY_OUT_RAMDISK := $(TARGET_COPY_OUT_ROOT) +endif + +########################################### +# Configure whether we're building the system image +BUILDING_SYSTEM_IMAGE := true +ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),) + ifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE + ifndef BOARD_SYSTEMIMAGE_PARTITION_SIZE + BUILDING_SYSTEM_IMAGE := + endif + endif +else ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),false) + BUILDING_SYSTEM_IMAGE := +endif +.KATI_READONLY := BUILDING_SYSTEM_IMAGE + +# Are we building a system_other image +BUILDING_SYSTEM_OTHER_IMAGE := +ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),) + ifdef BUILDING_SYSTEM_IMAGE + ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true) + BUILDING_SYSTEM_OTHER_IMAGE := true + endif + endif +else ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),true) + BUILDING_SYSTEM_OTHER_IMAGE := true + ifndef BUILDING_SYSTEM_IMAGE + $(error PRODUCT_BUILD_SYSTEM_OTHER_IMAGE = true requires building the system image) + endif +endif +.KATI_READONLY := BUILDING_SYSTEM_OTHER_IMAGE + +# Are we building a cache image +BUILDING_CACHE_IMAGE := +ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),) + ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE + BUILDING_CACHE_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),true) + BUILDING_CACHE_IMAGE := true + ifndef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_CACHE_IMAGE set to true, but BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +.KATI_READONLY := BUILDING_CACHE_IMAGE + +# Are we building a boot image +BUILDING_BOOT_IMAGE := +ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),) + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + BUILDING_BOOT_IMAGE := + else ifdef BOARD_PREBUILT_BOOTIMAGE + BUILDING_BOOT_IMAGE := + else ifdef BOARD_BOOTIMAGE_PARTITION_SIZE + BUILDING_BOOT_IMAGE := true + else ifneq (,$(foreach kernel,$(BOARD_KERNEL_BINARIES),$(BOARD_$(call to-upper,$(kernel))_BOOTIMAGE_PARTITION_SIZE))) + BUILDING_BOOT_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),true) + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + $(warning *** PRODUCT_BUILD_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT.) + $(warning *** Skipping building boot image.) + BUILDING_BOOT_IMAGE := + else + BUILDING_BOOT_IMAGE := true + endif +endif +.KATI_READONLY := BUILDING_BOOT_IMAGE + +# Are we building an init boot image +BUILDING_INIT_BOOT_IMAGE := +ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),) + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + BUILDING_INIT_BOOT_IMAGE := + else ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE + BUILDING_INIT_BOOT_IMAGE := + else ifdef BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE + BUILDING_INIT_BOOT_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),true) + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + $(error PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.) + else + BUILDING_INIT_BOOT_IMAGE := true + endif +endif +.KATI_READONLY := BUILDING_INIT_BOOT_IMAGE + +# Are we building a recovery image +BUILDING_RECOVERY_IMAGE := +ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),) + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + BUILDING_RECOVERY_IMAGE := true + else ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) + # Set to true to build recovery resources for vendor_boot + BUILDING_RECOVERY_IMAGE := true + else ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE + ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY))) + BUILDING_RECOVERY_IMAGE := true + endif + endif +else ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),true) + BUILDING_RECOVERY_IMAGE := true +endif +.KATI_READONLY := BUILDING_RECOVERY_IMAGE + +# Are we building a vendor boot image +BUILDING_VENDOR_BOOT_IMAGE := +ifdef BOARD_BOOT_HEADER_VERSION + ifneq ($(call math_gt_or_eq,$(BOARD_BOOT_HEADER_VERSION),3),) + ifeq ($(PRODUCT_BUILD_VENDOR_BOOT_IMAGE),) + BUILDING_VENDOR_BOOT_IMAGE := true + else ifeq ($(PRODUCT_BUILD_VENDOR_BOOT_IMAGE),true) + BUILDING_VENDOR_BOOT_IMAGE := true + endif + endif +endif +.KATI_READONLY := BUILDING_VENDOR_BOOT_IMAGE + +# Are we building a vendor kernel boot image +BUILDING_VENDOR_KERNEL_BOOT_IMAGE := +ifeq ($(PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE),true) + ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true) + $(error BUILDING_VENDOR_BOOT_IMAGE is required, but BUILDING_VENDOR_BOOT_IMAGE is not true) + endif + ifndef BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE + $(error BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE is required when PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE is true) + endif + BUILDING_VENDOR_KERNEL_BOOT_IMAGE := true +else ifeq ($(PRODUCT_BUILD_VENDOR_KERNEL),) + ifdef BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE + ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true) + BUILDING_VENDOR_KERNEL_BOOT_IMAGE := true + endif + endif +endif # end of PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE +.KATI_READONLY := BUILDING_VENDOR_KERNEL_BOOT_IMAGE + +# Are we building a ramdisk image +BUILDING_RAMDISK_IMAGE := true +ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),) + # TODO: Be smarter about this. This probably only needs to happen when one of the follow is true: + # BUILDING_BOOT_IMAGE + # BUILDING_RECOVERY_IMAGE +else ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),false) + BUILDING_RAMDISK_IMAGE := +endif +.KATI_READONLY := BUILDING_RAMDISK_IMAGE + +# Are we building a debug vendor_boot image +BUILDING_DEBUG_VENDOR_BOOT_IMAGE := +# Can't build vendor_boot-debug.img if BOARD_BUILD_SYSTEM_ROOT_IMAGE is true, +# because building debug vendor_boot image requires a ramdisk. +ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but so is BOARD_BUILD_SYSTEM_ROOT_IMAGE. \ + Skip building the debug vendor_boot image.) + endif +# Can't build vendor_boot-debug.img if we're not building a ramdisk. +else ifndef BUILDING_RAMDISK_IMAGE + ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a ramdisk image. \ + Skip building the debug vendor_boot image.) + endif +# Can't build vendor_boot-debug.img if we're not building a vendor_boot.img. +else ifndef BUILDING_VENDOR_BOOT_IMAGE + ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a vendor_boot image. \ + Skip building the debug vendor_boot image.) + endif +else + ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),) + BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true + else ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) + BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true + endif +endif +.KATI_READONLY := BUILDING_DEBUG_VENDOR_BOOT_IMAGE + +_has_boot_img_artifact := +ifneq ($(strip $(TARGET_NO_KERNEL)),true) + ifdef BUILDING_BOOT_IMAGE + _has_boot_img_artifact := true + endif + # BUILDING_RECOVERY_IMAGE && BOARD_USES_RECOVERY_AS_BOOT implies that + # recovery is being built with the file name *boot.img*, which still counts + # as "building boot.img". + ifdef BUILDING_RECOVERY_IMAGE + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + _has_boot_img_artifact := true + endif + endif +endif + +# Are we building a debug boot image +BUILDING_DEBUG_BOOT_IMAGE := +# Can't build boot-debug.img if BOARD_BUILD_SYSTEM_ROOT_IMAGE is true, +# because building debug boot image requires a ramdisk. +ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but so is BOARD_BUILD_SYSTEM_ROOT_IMAGE. \ + Skip building the debug boot image.) + endif +# Can't build boot-debug.img if we're not building a ramdisk. +else ifndef BUILDING_RAMDISK_IMAGE + ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a ramdisk image. \ + Skip building the debug boot image.) + endif +# Can't build boot-debug.img if we're not building a boot.img. +else ifndef _has_boot_img_artifact + ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a boot image. \ + Skip building the debug boot image.) + endif +else ifdef BUILDING_INIT_BOOT_IMAGE + ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) + $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we don't have a ramdisk in the boot image. \ + Skip building the debug boot image.) + endif +else + ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),) + BUILDING_DEBUG_BOOT_IMAGE := true + # Don't build boot-debug.img if we're already building vendor_boot-debug.img. + ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE + BUILDING_DEBUG_BOOT_IMAGE := + endif + else ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) + BUILDING_DEBUG_BOOT_IMAGE := true + endif +endif +.KATI_READONLY := BUILDING_DEBUG_BOOT_IMAGE +_has_boot_img_artifact := + +# Are we building a userdata image +BUILDING_USERDATA_IMAGE := +ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),) + ifdef BOARD_USERDATAIMAGE_PARTITION_SIZE + BUILDING_USERDATA_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),true) + BUILDING_USERDATA_IMAGE := true +endif +.KATI_READONLY := BUILDING_USERDATA_IMAGE + +# Are we building a vbmeta image +BUILDING_VBMETA_IMAGE := true +ifeq ($(PRODUCT_BUILD_VBMETA_IMAGE),false) + BUILDING_VBMETA_IMAGE := +endif +.KATI_READONLY := BUILDING_VBMETA_IMAGE + +# Are we building a super_empty image +BUILDING_SUPER_EMPTY_IMAGE := +ifeq ($(PRODUCT_BUILD_SUPER_EMPTY_IMAGE),) + ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) + ifneq ($(BOARD_SUPER_PARTITION_SIZE),) + BUILDING_SUPER_EMPTY_IMAGE := true + endif + endif +else ifeq ($(PRODUCT_BUILD_SUPER_EMPTY_IMAGE),true) + ifneq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) + $(error PRODUCT_BUILD_SUPER_EMPTY_IMAGE set to true, but PRODUCT_USE_DYNAMIC_PARTITIONS is not true) + endif + ifeq ($(BOARD_SUPER_PARTITION_SIZE),) + $(error PRODUCT_BUILD_SUPER_EMPTY_IMAGE set to true, but BOARD_SUPER_PARTITION_SIZE is not defined) + endif + BUILDING_SUPER_EMPTY_IMAGE := true +endif +.KATI_READONLY := BUILDING_SUPER_EMPTY_IMAGE + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR +ifeq ($(TARGET_COPY_OUT_VENDOR),$(_vendor_path_placeholder)) + TARGET_COPY_OUT_VENDOR := system/vendor +else ifeq ($(filter vendor system/vendor,$(TARGET_COPY_OUT_VENDOR)),) + $(error TARGET_COPY_OUT_VENDOR must be either 'vendor' or 'system/vendor', seeing '$(TARGET_COPY_OUT_VENDOR)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),$(PRODUCT_COPY_FILES)) + +BOARD_USES_VENDORIMAGE := +ifdef BOARD_PREBUILT_VENDORIMAGE + BOARD_USES_VENDORIMAGE := true +endif +ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_VENDORIMAGE := true +endif +# TODO(b/137169253): For now, some AOSP targets build with prebuilt vendor image. +# But target's BOARD_PREBUILT_VENDORIMAGE is not filled. +ifeq ($(TARGET_COPY_OUT_VENDOR),vendor) + BOARD_USES_VENDORIMAGE := true +else ifdef BOARD_USES_VENDORIMAGE + $(error TARGET_COPY_OUT_VENDOR must be set to 'vendor' to use a vendor image) +endif +.KATI_READONLY := BOARD_USES_VENDORIMAGE + +BUILDING_VENDOR_IMAGE := +ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),) + ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE + BUILDING_VENDOR_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),true) + BUILDING_VENDOR_IMAGE := true + ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_VENDOR_IMAGE set to true, but BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_VENDORIMAGE + BUILDING_VENDOR_IMAGE := +endif +.KATI_READONLY := BUILDING_VENDOR_IMAGE + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_PRODUCT +ifeq ($(TARGET_COPY_OUT_PRODUCT),$(_product_path_placeholder)) +TARGET_COPY_OUT_PRODUCT := system/product +else ifeq ($(filter product system/product,$(TARGET_COPY_OUT_PRODUCT)),) +$(error TARGET_COPY_OUT_PRODUCT must be either 'product' or 'system/product', seeing '$(TARGET_COPY_OUT_PRODUCT)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_product_path_placeholder),$(TARGET_COPY_OUT_PRODUCT),$(PRODUCT_COPY_FILES)) + +BOARD_USES_PRODUCTIMAGE := +ifdef BOARD_PREBUILT_PRODUCTIMAGE + BOARD_USES_PRODUCTIMAGE := true +endif +ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_PRODUCTIMAGE := true +endif +$(call check_image_config,product) +.KATI_READONLY := BOARD_USES_PRODUCTIMAGE + +BUILDING_PRODUCT_IMAGE := +ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),) + ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE + BUILDING_PRODUCT_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),true) + BUILDING_PRODUCT_IMAGE := true + ifndef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_PRODUCT_IMAGE set to true, but BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_PRODUCTIMAGE + BUILDING_PRODUCT_IMAGE := +endif +.KATI_READONLY := BUILDING_PRODUCT_IMAGE + +########################################### +# TODO(b/135957588) TARGET_COPY_OUT_PRODUCT_SERVICES will be set to +# TARGET_COPY_OUT_PRODUCT as a workaround. +TARGET_COPY_OUT_PRODUCT_SERVICES := $(TARGET_COPY_OUT_PRODUCT) + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_SYSTEM_EXT +ifeq ($(TARGET_COPY_OUT_SYSTEM_EXT),$(_system_ext_path_placeholder)) +TARGET_COPY_OUT_SYSTEM_EXT := system/system_ext +else ifeq ($(filter system_ext system/system_ext,$(TARGET_COPY_OUT_SYSTEM_EXT)),) +$(error TARGET_COPY_OUT_SYSTEM_EXT must be either 'system_ext' or 'system/system_ext', seeing '$(TARGET_COPY_OUT_SYSTEM_EXT)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_system_ext_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_EXT),$(PRODUCT_COPY_FILES)) + +BOARD_USES_SYSTEM_EXTIMAGE := +ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE + BOARD_USES_SYSTEM_EXTIMAGE := true +endif +ifdef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_SYSTEM_EXTIMAGE := true +endif +$(call check_image_config,system_ext) +.KATI_READONLY := BOARD_USES_SYSTEM_EXTIMAGE + +BUILDING_SYSTEM_EXT_IMAGE := +ifeq ($(PRODUCT_BUILD_SYSTEM_EXT_IMAGE),) + ifdef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE + BUILDING_SYSTEM_EXT_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_SYSTEM_EXT_IMAGE),true) + BUILDING_SYSTEM_EXT_IMAGE := true + ifndef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_SYSTEM_EXT_IMAGE set to true, but BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE + BUILDING_SYSTEM_EXT_IMAGE := +endif +.KATI_READONLY := BUILDING_SYSTEM_EXT_IMAGE + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR_DLKM +ifeq ($(TARGET_COPY_OUT_VENDOR_DLKM),$(_vendor_dlkm_path_placeholder)) + TARGET_COPY_OUT_VENDOR_DLKM := $(TARGET_COPY_OUT_VENDOR)/vendor_dlkm +else ifeq ($(filter vendor_dlkm system/vendor/vendor_dlkm vendor/vendor_dlkm,$(TARGET_COPY_OUT_VENDOR_DLKM)),) + $(error TARGET_COPY_OUT_VENDOR_DLKM must be either 'vendor_dlkm', 'system/vendor/vendor_dlkm' or 'vendor/vendor_dlkm', seeing '$(TARGET_COPY_OUT_VENDOR_DLKM)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_vendor_dlkm_path_placeholder),$(TARGET_COPY_OUT_VENDOR_DLKM),$(PRODUCT_COPY_FILES)) + +BOARD_USES_VENDOR_DLKMIMAGE := +ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE + BOARD_USES_VENDOR_DLKMIMAGE := true +endif +ifdef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_VENDOR_DLKMIMAGE := true +endif +$(call check_image_config,vendor_dlkm) + +BUILDING_VENDOR_DLKM_IMAGE := +ifeq ($(PRODUCT_BUILD_VENDOR_DLKM_IMAGE),) + ifdef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE + BUILDING_VENDOR_DLKM_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_VENDOR_DLKM_IMAGE),true) + BUILDING_VENDOR_DLKM_IMAGE := true + ifndef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_VENDOR_DLKM_IMAGE set to true, but BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE + BUILDING_VENDOR_DLKM_IMAGE := +endif +.KATI_READONLY := BUILDING_VENDOR_DLKM_IMAGE + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_ODM +ifeq ($(TARGET_COPY_OUT_ODM),$(_odm_path_placeholder)) + TARGET_COPY_OUT_ODM := $(TARGET_COPY_OUT_VENDOR)/odm +else ifeq ($(filter odm system/vendor/odm vendor/odm,$(TARGET_COPY_OUT_ODM)),) + $(error TARGET_COPY_OUT_ODM must be either 'odm', 'system/vendor/odm' or 'vendor/odm', seeing '$(TARGET_COPY_OUT_ODM)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),$(PRODUCT_COPY_FILES)) + +BOARD_USES_ODMIMAGE := +ifdef BOARD_PREBUILT_ODMIMAGE + BOARD_USES_ODMIMAGE := true +endif +ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_ODMIMAGE := true +endif +$(call check_image_config,odm) + +BUILDING_ODM_IMAGE := +ifeq ($(PRODUCT_BUILD_ODM_IMAGE),) + ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE + BUILDING_ODM_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_ODM_IMAGE),true) + BUILDING_ODM_IMAGE := true + ifndef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_ODM_IMAGE set to true, but BOARD_ODMIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_ODMIMAGE + BUILDING_ODM_IMAGE := +endif +.KATI_READONLY := BUILDING_ODM_IMAGE + + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_ODM_DLKM +ifeq ($(TARGET_COPY_OUT_ODM_DLKM),$(_odm_dlkm_path_placeholder)) + TARGET_COPY_OUT_ODM_DLKM := $(TARGET_COPY_OUT_VENDOR)/odm_dlkm +else ifeq ($(filter odm_dlkm system/vendor/odm_dlkm vendor/odm_dlkm,$(TARGET_COPY_OUT_ODM_DLKM)),) + $(error TARGET_COPY_OUT_ODM_DLKM must be either 'odm_dlkm', 'system/vendor/odm_dlkm' or 'vendor/odm_dlkm', seeing '$(TARGET_COPY_OUT_ODM_DLKM)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_odm_dlkm_path_placeholder),$(TARGET_COPY_OUT_ODM_DLKM),$(PRODUCT_COPY_FILES)) + +BOARD_USES_ODM_DLKMIMAGE := +ifdef BOARD_PREBUILT_ODM_DLKMIMAGE + BOARD_USES_ODM_DLKMIMAGE := true +endif +ifdef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_ODM_DLKMIMAGE := true +endif +$(call check_image_config,odm_dlkm) + +BUILDING_ODM_DLKM_IMAGE := +ifeq ($(PRODUCT_BUILD_ODM_DLKM_IMAGE),) + ifdef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE + BUILDING_ODM_DLKM_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_ODM_DLKM_IMAGE),true) + BUILDING_ODM_DLKM_IMAGE := true + ifndef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_ODM_DLKM_IMAGE set to true, but BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_ODM_DLKMIMAGE + BUILDING_ODM_DLKM_IMAGE := +endif +.KATI_READONLY := BUILDING_ODM_DLKM_IMAGE + +########################################### +# Now we can substitute with the real value of TARGET_COPY_OUT_SYSTEM_DLKM +ifeq ($(TARGET_COPY_OUT_SYSTEM_DLKM),$(_system_dlkm_path_placeholder)) + TARGET_COPY_OUT_SYSTEM_DLKM := $(TARGET_COPY_OUT_SYSTEM)/system_dlkm +else ifeq ($(filter system_dlkm system/system_dlkm,$(TARGET_COPY_OUT_SYSTEM_DLKM)),) + $(error TARGET_COPY_OUT_SYSTEM_DLKM must be either 'system_dlkm' or 'system/system_dlkm', seeing '$(TARGET_COPY_OUT_ODM_DLKM)'.) +endif +PRODUCT_COPY_FILES := $(subst $(_system_dlkm_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_DLKM),$(PRODUCT_COPY_FILES)) + +BOARD_USES_SYSTEM_DLKMIMAGE := +ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE + BOARD_USES_SYSTEM_DLKMIMAGE := true +endif +ifdef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE + BOARD_USES_SYSTEM_DLKMIMAGE := true +endif +$(call check_image_config,system_dlkm) + +BUILDING_SYSTEM_DLKM_IMAGE := +ifeq ($(PRODUCT_BUILD_SYSTEM_DLKM_IMAGE),) + ifdef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE + BUILDING_SYSTEM_DLKM_IMAGE := true + endif +else ifeq ($(PRODUCT_BUILD_SYSTEM_DLKM_IMAGE),true) + BUILDING_SYSTEM_DLKM_IMAGE := true + ifndef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE + $(error PRODUCT_BUILD_SYSTEM_DLKM_IMAGE set to true, but BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE not defined) + endif +endif +ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE + BUILDING_SYSTEM_DLKM_IMAGE := +endif +.KATI_READONLY := BUILDING_SYSTEM_DLKM_IMAGE + +BOARD_USES_PVMFWIMAGE := +ifdef BOARD_PREBUILT_PVMFWIMAGE + BOARD_USES_PVMFWIMAGE := true +endif +ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true) + BOARD_USES_PVMFWIMAGE := true +endif +.KATI_READONLY := BOARD_USES_PVMFWIMAGE + +BUILDING_PVMFW_IMAGE := +ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true) + BUILDING_PVMFW_IMAGE := true +endif +ifdef BOARD_PREBUILT_PVMFWIMAGE + BUILDING_PVMFW_IMAGE := +endif +.KATI_READONLY := BUILDING_PVMFW_IMAGE + +########################################### +# Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE. +TARGET_RECOVERY_UPDATER_LIBS ?= +AB_OTA_UPDATER ?= +.KATI_READONLY := TARGET_RECOVERY_UPDATER_LIBS AB_OTA_UPDATER + +# Ensure that if PRODUCT_OTA_FORCE_NON_AB_PACKAGE == true, then AB_OTA_UPDATER must be true +ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true) + ifneq ($(AB_OTA_UPDATER),true) + $(error AB_OTA_UPDATER must be set to true when PRODUCT_OTA_FORCE_NON_AB_PACKAGE is true) + endif +endif + +# In some configurations, A/B and non-A/B may coexist. Check TARGET_OTA_ALLOW_NON_AB +# to see if non-A/B is supported. +TARGET_OTA_ALLOW_NON_AB := false +ifneq ($(AB_OTA_UPDATER),true) + TARGET_OTA_ALLOW_NON_AB := true +else ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true) + TARGET_OTA_ALLOW_NON_AB := true +endif +.KATI_READONLY := TARGET_OTA_ALLOW_NON_AB + +ifneq ($(TARGET_OTA_ALLOW_NON_AB),true) + ifneq ($(strip $(TARGET_RECOVERY_UPDATER_LIBS)),) + $(error Do not use TARGET_RECOVERY_UPDATER_LIBS when using TARGET_OTA_ALLOW_NON_AB) + endif +endif + +# Quick check for building generic OTA packages. Currently it only supports A/B OTAs. +ifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true) + ifneq ($(AB_OTA_UPDATER),true) + $(error PRODUCT_BUILD_GENERIC_OTA_PACKAGE with 'AB_OTA_UPDATER != true' is not supported) + endif +endif + +ifdef BOARD_PREBUILT_DTBIMAGE_DIR + ifneq ($(BOARD_INCLUDE_DTB_IN_BOOTIMG),true) + $(error BOARD_PREBUILT_DTBIMAGE_DIR with 'BOARD_INCLUDE_DTB_IN_BOOTIMG != true' is not supported) + endif +endif + +# Check BOARD_VNDK_VERSION +define check_vndk_version + $(eval vndk_path := prebuilts/vndk/v$(1)) \ + $(if $(wildcard $(vndk_path)/*/Android.bp),,$(error VNDK version $(1) not found)) +endef + +ifdef BOARD_VNDK_VERSION + ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION)) + $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current) + endif + ifneq ($(BOARD_VNDK_VERSION),current) + $(call check_vndk_version,$(BOARD_VNDK_VERSION)) + endif + TARGET_VENDOR_TEST_SUFFIX := /vendor +else + TARGET_VENDOR_TEST_SUFFIX := +endif + +# If PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY is set, +# BOARD_VNDK_VERSION must be set because PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY +# is a enforcement of inter-partition dependency, and it doesn't have any meaning +# when BOARD_VNDK_VERSION isn't set. +ifeq ($(PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY),true) + ifeq ($(BOARD_VNDK_VERSION),) + $(error BOARD_VNDK_VERSION must be set when PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY is true) + endif +endif + +########################################### +# APEXes are by default flattened, i.e. non-updatable, if not building unbundled +# apps. It can be unflattened (and updatable) by inheriting from +# updatable_apex.mk +# +# APEX flattening can also be forcibly enabled (resp. disabled) by +# setting OVERRIDE_TARGET_FLATTEN_APEX to true (resp. false), e.g. by +# setting the OVERRIDE_TARGET_FLATTEN_APEX environment variable. +ifdef OVERRIDE_TARGET_FLATTEN_APEX + TARGET_FLATTEN_APEX := $(OVERRIDE_TARGET_FLATTEN_APEX) +else + ifeq (,$(TARGET_BUILD_APPS)$(TARGET_FLATTEN_APEX)) + TARGET_FLATTEN_APEX := true + endif +endif + +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +ifdef PRODUCT_EXTRA_VNDK_VERSIONS + $(foreach v,$(PRODUCT_EXTRA_VNDK_VERSIONS),$(call check_vndk_version,$(v))) +endif +endif + +# Ensure that BOARD_SYSTEMSDK_VERSIONS are all within PLATFORM_SYSTEMSDK_VERSIONS +_unsupported_systemsdk_versions := $(filter-out $(PLATFORM_SYSTEMSDK_VERSIONS),$(BOARD_SYSTEMSDK_VERSIONS)) +ifneq (,$(_unsupported_systemsdk_versions)) + $(error System SDK versions '$(_unsupported_systemsdk_versions)' in BOARD_SYSTEMSDK_VERSIONS are not supported.\ + Supported versions are $(PLATFORM_SYSTEMSDK_VERSIONS)) +endif + +########################################### +# Handle BUILD_BROKEN_USES_BUILD_* + +$(foreach m,$(DEFAULT_WARNING_BUILD_MODULE_TYPES),\ + $(if $(filter false,$(BUILD_BROKEN_USES_$(m))),\ + $(KATI_obsolete_var $(m),Please convert to Soong),\ + $(KATI_deprecated_var $(m),Please convert to Soong))) + +$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ + $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers),\ + $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers)) + +$(foreach m,$(filter-out BUILD_COPY_HEADERS,$(DEFAULT_ERROR_BUILD_MODULE_TYPES)),\ + $(if $(filter true,$(BUILD_BROKEN_USES_$(m))),\ + $(KATI_deprecated_var $(m),Please convert to Soong),\ + $(KATI_obsolete_var $(m),Please convert to Soong))) + +ifndef BUILDING_RECOVERY_IMAGE + ifeq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) + $(error Should not set BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE if not building recovery image) + endif +endif + +ifndef BUILDING_VENDOR_BOOT_IMAGE + ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) + $(error Should not set BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT if not building vendor_boot image) + endif + ifdef BOARD_VENDOR_RAMDISK_FRAGMENTS + $(error Should not set BOARD_VENDOR_RAMDISK_FRAGMENTS if not building vendor_boot image) + endif +else # BUILDING_VENDOR_BOOT_IMAGE + ifneq (,$(call math_lt,$(BOARD_BOOT_HEADER_VERSION),4)) + ifdef BOARD_VENDOR_RAMDISK_FRAGMENTS + $(error Should not set BOARD_VENDOR_RAMDISK_FRAGMENTS if \ + BOARD_BOOT_HEADER_VERSION is less than 4) + endif + ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) + $(error Should not set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT if \ + BOARD_BOOT_HEADER_VERSION is less than 4) + endif + endif +endif # BUILDING_VENDOR_BOOT_IMAGE + +ifneq ($(words $(BOARD_VENDOR_RAMDISK_FRAGMENTS)),$(words $(sort $(BOARD_VENDOR_RAMDISK_FRAGMENTS)))) + $(error BOARD_VENDOR_RAMDISK_FRAGMENTS has duplicate entries: $(BOARD_VENDOR_RAMDISK_FRAGMENTS)) +endif + +ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) + ifneq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) + $(error Should not set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT if \ + BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is not set) + endif +endif + +# If BOARD_USES_GENERIC_KERNEL_IMAGE is set, BOARD_USES_RECOVERY_AS_BOOT must not be set. +# Devices without a dedicated recovery partition uses BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT to +# build recovery into vendor_boot. +ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) + ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) + $(error BOARD_USES_RECOVERY_AS_BOOT cannot be true if BOARD_USES_GENERIC_KERNEL_IMAGE is true. \ + Use BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT instead) + endif +endif + +ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) + ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) + $(error BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT and BOARD_USES_RECOVERY_AS_BOOT cannot be \ + both true. Recovery resources should be installed to either boot or vendor_boot, but not both) + endif +endif diff --git a/make/core/build-system.html b/make/core/build-system.html new file mode 100644 index 0000000..181e939 --- /dev/null +++ b/make/core/build-system.html @@ -0,0 +1,988 @@ + + + + + + + + Android Build System + + + + + + + + + + + + + + + +

Android Build System

+ + +

+ Status: Draft   + (as of May 18, 2006) +

+ +

Contents

+ + + +

Objective

+

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

+ +

Principles and Use Cases and Policy

+

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

+

Multiple Targets

+

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

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

Non-Recursive Make

+

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

Rapid Compile-Test Cycles

+

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

+

Both Environment and Config File Based Settings

+

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

+

Object File Directory / make clean

+

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

+ +

SDK

+

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

+ +

Dependecies

+

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

+ +

Wildcard source files

+

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

+ +

Multiple targets in one directory

+

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

+ +

Makefile fragments for modules

+

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

+ +

Use shared libraries

+

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

+ + +

Nice to Have

+ +

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

+ +

Simultaneous Builds

+

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

+ +

Deleting headers (or other dependecies)

+

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

+

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

+ +

Multiple builds

+

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

+ +

Aftermarket Locales and Carrier

+

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

+ + +

Usage

+

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

+ +

Your first build

+

The Building document describes how do do +builds.

+ +

build/envsetup.sh functions

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

Build flavors/types

+

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

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

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

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

+ The same as user, except: +

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

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

+ + +

More pseudotargets

+

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

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

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

+

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

+

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

+ +

Apps

+

Use the templates/apps file.

+

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

+ +

Java Libraries

+

Use the templates/java_library file.

+

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

+ +

C/C++ Executables

+

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

+

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

+

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

+ +

Shared Libraries

+

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

+

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

+ +

Static Libraries

+

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

+

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

+ +

Using Custom Tools

+

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

+ +

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

Example 1
+

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

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

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

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

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

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

Unbundled build

+

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

+

TARGET_BUILD_UNBUNDLED

+

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

+

TARGET_BUILD_USE_PREBUILT_SDKS

+

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

+

DISABLE_PREOPT

+

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

+

TARGET_BUILD_APPS

+

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

+

TARGET_BUILD_UNBUNDLED_IMAGE

+

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

+ +

Platform specific conditionals

+

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

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

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

+ +

Some Examples

+
ifeq ($(TARGET_BUILD_TYPE),release)
+LOCAL_CFLAGS += -DNDEBUG=1
+endif
+
+LOCAL_CFLAGS_arm += -DTARGET_IS_ARM
+
+LOCAL_CFLAGS_64 += -DBIG_POINTER
+
+# from libutils
+# Use the futex based mutex and condition variable
+# implementation from android-arm because it's shared mem safe
+LOCAL_SRC_FILES_linux += futex_synchro.c
+LOCAL_LDLIBS_linux += -lrt -ldl
+
+
+ + +

Putting modules elsewhere

+

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

+

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

+
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+

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

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

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

+

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

+ + +

Android.mk variables

+

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

+

But first, a note on variable naming: +

+

+ +

LOCAL_ANNOTATION_PROCESSORS

+

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

+ +

LOCAL_ANNOTATION_PROCESSOR_CLASSES

+

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

+ +

LOCAL_ASSET_FILES

+

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

+

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

+

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

+ +

LOCAL_CC

+

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

+ +

LOCAL_CXX

+

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

+ +

LOCAL_CFLAGS

+

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

+

LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1

+ +

LOCAL_CPPFLAGS

+

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

+

LOCAL_CPPFLAGS += -ffriend-injection

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

LOCAL_CPP_EXTENSION

+

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

+

LOCAL_CPP_EXTENSION := .cc

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

LOCAL_NO_DEFAULT_COMPILER_FLAGS

+

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

LOCAL_COPY_HEADERS

+

This will be going away.

+

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

+

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

+ +

LOCAL_COPY_HEADERS_TO

+

This will be going away.

+

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

+

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

+ +

LOCAL_C_INCLUDES

+

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

+

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

+

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

+

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

+

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

+ +

LOCAL_MODULE_TAGS

+

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

+ +

LOCAL_REQUIRED_MODULES

+

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

LOCAL_FORCE_STATIC_EXECUTABLE

+

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

+ +

LOCAL_GENERATED_SOURCES

+

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

+ +

LOCAL_JAVACFLAGS

+

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

+

LOCAL_JAVACFLAGS += -Xlint:deprecation

+ +

LOCAL_ERROR_PRONE_FLAGS

+

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

+

LOCAL_ERROR_PRONE_FLAGS += -Xep:ClassCanBeStatic:ERROR

+ +

LOCAL_JAVA_LIBRARIES

+

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

+

LOCAL_JAVA_LIBRARIES := core framework

+

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

+ +

LOCAL_LDFLAGS

+

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

+ +

LOCAL_LDLIBS

+

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

+

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

+ +

LOCAL_NO_MANIFEST

+

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

+ +

LOCAL_PACKAGE_NAME

+

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

+ +

LOCAL_PATCH_MODULE (experimental option)

+

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

+

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

+ +

LOCAL_PATH

+

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

+

LOCAL_PATH := $(my-dir)

+

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

LOCAL_POST_PROCESS_COMMAND

+

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

+

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

+ +

LOCAL_PREBUILT_EXECUTABLES

+

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

+ +

LOCAL_PREBUILT_LIBS

+

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

+ +

LOCAL_SHARED_LIBRARIES

+

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

+

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

+ +

LOCAL_SRC_FILES

+

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

+

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

+ +

LOCAL_STATIC_LIBRARIES

+

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

LOCAL_STATIC_LIBRARIES := \
+     libutils \
+     libtinyxml +

+ +

LOCAL_MODULE

+

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

+ +

LOCAL_MODULE_PATH

+

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

+

See Putting modules elsewhere for more.

+ +

LOCAL_MODULE_RELATIVE_PATH

+

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

+

See Putting modules elsewhere for more.

+ +

LOCAL_MODULE_HOST_OS

+

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

+

LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_HOST_OS := darwin linux windows

+ +

LOCAL_UNSTRIPPED_PATH

+

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

+

See Putting modules elsewhere for more.

+ +

LOCAL_WHOLE_STATIC_LIBRARIES

+

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

LOCAL_WHOLE_STATIC_LIBRARIES := \
+     libsqlite3_android
+

+ +

LOCAL_YACCFLAGS

+

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

+

LOCAL_YACCFLAGS := -p kjsyy

+ + + +

Implementation Details

+ +

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

+ +

Environment Setup / buildspec.mk Versioning

+

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

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

+ +

Additional makefile variables

+

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

+ +

LOCAL_ADDITIONAL_DEPENDENCIES

+

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

+ +

LOCAL_BUILT_MODULE

+

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

+

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

+ +

LOCAL_IS_HOST_MODULE

+

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

+ +

LOCAL_INSTALLED_MODULE

+

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

+

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

+ +

LOCAL_MODULE_CLASS

+

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

+ +

LOCAL_MODULE_SUFFIX

+

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

+ +

LOCAL_STRIP_MODULE

+

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

+ +

LOCAL_SYSTEM_SHARED_LIBRARIES

+

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

+ + + + diff --git a/make/core/build_id.mk b/make/core/build_id.mk new file mode 100644 index 0000000..eb00bb4 --- /dev/null +++ b/make/core/build_id.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# BUILD_ID is usually used to specify the branch name +# (like "MAIN") or a branch name and a release candidate +# (like "CRB01"). It must be a single word, and is +# capitalized by convention. + +BUILD_ID=TQ3C.230805.001.B2 diff --git a/make/core/build_rro_package.mk b/make/core/build_rro_package.mk new file mode 100644 index 0000000..ae528bd --- /dev/null +++ b/make/core/build_rro_package.mk @@ -0,0 +1,43 @@ +############################################################################# +## Standard rules for installing runtime resouce overlay APKs. +## +## Set LOCAL_RRO_THEME to the theme name if the package should apply only to +## a particular theme as set by ro.boot.vendor.overlay.theme system property. +## +## If LOCAL_RRO_THEME is not set, the package will apply always, independent +## of themes. +## +############################################################################# + +LOCAL_IS_RUNTIME_RESOURCE_OVERLAY := true + +ifneq ($(LOCAL_SRC_FILES),) + $(error runtime resource overlay package should not contain sources) +endif + +partition := +ifeq ($(strip $(LOCAL_ODM_MODULE)),true) + partition := $(TARGET_OUT_ODM) +else ifeq ($(strip $(LOCAL_VENDOR_MODULE)),true) + partition := $(TARGET_OUT_VENDOR) +else ifeq ($(strip $(LOCAL_SYSTEM_EXT_MODULE)),true) + partition := $(TARGET_OUT_SYSTEM_EXT) +else + partition := $(TARGET_OUT_PRODUCT) +endif + +ifeq ($(LOCAL_RRO_THEME),) + LOCAL_MODULE_PATH := $(partition)/overlay +else + LOCAL_MODULE_PATH := $(partition)/overlay/$(LOCAL_RRO_THEME) +endif + +# Do not remove resources without default values nor dedupe resource +# configurations with the same value +LOCAL_AAPT_FLAGS += \ + --no-resource-deduping \ + --no-resource-removal + +partition := + +include $(BUILD_SYSTEM)/package.mk diff --git a/make/core/cc_prebuilt_internal.mk b/make/core/cc_prebuilt_internal.mk new file mode 100644 index 0000000..e8e01d8 --- /dev/null +++ b/make/core/cc_prebuilt_internal.mk @@ -0,0 +1,198 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################ +# Internal build rules for native prebuilt modules +############################################################ + +prebuilt_module_classes := STATIC_LIBRARIES SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS +ifeq ($(filter $(prebuilt_module_classes),$(LOCAL_MODULE_CLASS)),) +$(call pretty-error,cc_prebuilt_internal.mk is for $(prebuilt_module_classes) modules only) +endif + +my_strip_module := $(firstword \ + $(LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \ + $(LOCAL_STRIP_MODULE)) + +ifeq (SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)) + ifeq ($(LOCAL_IS_HOST_MODULE)$(my_strip_module),) + # Strip but not try to add debuglink + my_strip_module := no_debuglink + endif +endif + +ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),) + prebuilt_module_is_a_library := true +else + prebuilt_module_is_a_library := +endif + +# Don't install static libraries by default. +ifndef LOCAL_UNINSTALLABLE_MODULE +ifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS)) + LOCAL_UNINSTALLABLE_MODULE := true +endif +endif + +my_check_elf_file_shared_lib_files := + +ifneq ($(filter true keep_symbols no_debuglink mini-debug-info,$(my_strip_module)),) + ifdef LOCAL_IS_HOST_MODULE + $(call pretty-error,Cannot strip/pack host module) + endif + ifeq ($(filter SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + $(call pretty-error,Can strip/pack only shared libraries or executables) + endif + ifneq ($(LOCAL_PREBUILT_STRIP_COMMENTS),) + $(call pretty-error,Cannot strip/pack scripts) + endif + # Set the arch-specific variables to set up the strip rules + LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) := $(my_strip_module) + include $(BUILD_SYSTEM)/dynamic_binary.mk + built_module := $(linked_module) + + ifneq ($(LOCAL_SDK_VERSION),) + # binary.mk filters out NDK_KNOWN_LIBS from my_shared_libs, thus those NDK libs are not added + # to DEPENDENCIES_ON_SHARED_LIBRARIES. Assign $(my_ndk_shared_libraries_fullpath) to + # my_check_elf_file_shared_lib_files so that check_elf_file.py can see those NDK stub libs. + my_check_elf_file_shared_lib_files := $(my_ndk_shared_libraries_fullpath) + endif +else # my_strip_module not true + include $(BUILD_SYSTEM)/base_rules.mk + built_module := $(LOCAL_BUILT_MODULE) + +ifdef prebuilt_module_is_a_library +EXPORTS_LIST += $(intermediates) +EXPORTS.$(intermediates).FLAGS := $(foreach d,$(LOCAL_EXPORT_C_INCLUDE_DIRS),-I $(d)) +EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS) + +include $(BUILD_SYSTEM)/allowed_ndk_types.mk + +ifdef LOCAL_SDK_VERSION +my_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type) +else ifdef LOCAL_USE_VNDK + _name := $(patsubst %.vendor,%,$(LOCAL_MODULE)) + _name := $(patsubst %.product,%,$(LOCAL_MODULE)) + ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),) + ifeq ($(filter $(_name),$(VNDK_PRIVATE_LIBRARIES)),) + my_link_type := native:vndk + else + my_link_type := native:vndk_private + endif + else + ifeq ($(LOCAL_USE_VNDK_PRODUCT),true) + my_link_type := native:product + else + my_link_type := native:vendor + endif + endif +else ifneq ($(filter $(TARGET_RECOVERY_OUT)/%,$(LOCAL_MODULE_PATH)),) +my_link_type := native:recovery +else +my_link_type := native:platform +endif + +# TODO: check dependencies of prebuilt files +my_link_deps := + +my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) +my_common := +include $(BUILD_SYSTEM)/link_type.mk +endif # prebuilt_module_is_a_library + +# The real dependency will be added after all Android.mks are loaded and the install paths +# of the shared libraries are determined. +ifdef LOCAL_INSTALLED_MODULE +ifdef LOCAL_IS_HOST_MODULE + ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) + my_system_shared_libraries := + else + my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) + endif +else + ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) + my_system_shared_libraries := libc libm libdl + else + my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) + my_system_shared_libraries := $(patsubst libc,libc libdl,$(my_system_shared_libraries)) + endif +endif + +my_shared_libraries := $(strip \ + $(filter-out $(my_system_shared_libraries),$(LOCAL_SHARED_LIBRARIES)) \ + $(my_system_shared_libraries)) + +# Extra shared libraries introduced by LOCAL_CXX_STL (may append some libraries to +# my_shared_libraries). +include $(BUILD_SYSTEM)/cxx_stl_setup.mk + +ifdef my_shared_libraries +ifdef LOCAL_USE_VNDK + ifeq ($(LOCAL_USE_VNDK_PRODUCT),true) + my_shared_libraries := $(foreach l,$(my_shared_libraries),\ + $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) + else + my_shared_libraries := $(foreach l,$(my_shared_libraries),\ + $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) + endif +endif +$(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ + $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries)) +endif # my_shared_libraries +endif # LOCAL_INSTALLED_MODULE + +# We need to enclose the above export_includes and my_built_shared_libraries in +# "my_strip_module not true" because otherwise the rules are defined in dynamic_binary.mk. +endif # my_strip_module not true + + +# Check prebuilt ELF binaries. +include $(BUILD_SYSTEM)/check_elf_file.mk + +ifeq ($(NATIVE_COVERAGE),true) +ifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE))) + $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).gcnodir)) + ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true) + ifdef LOCAL_IS_HOST_MODULE + my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) + else + my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) + endif + my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).gcnodir + $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path))) + $(LOCAL_BUILT_MODULE): $(my_coverage_path) + endif +else +# Coverage information is needed when static lib is a dependency of another +# coverage-enabled module. +ifeq (STATIC_LIBRARIES, $(LOCAL_MODULE_CLASS)) +GCNO_ARCHIVE := $(LOCAL_MODULE).gcnodir +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_PREFIX := $(my_prefix) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX) +$(intermediates)/$(GCNO_ARCHIVE) : + $(transform-o-to-static-lib) +endif +endif +endif + +$(built_module) : $(my_prebuilt_src_file) + $(transform-prebuilt-to-target) +ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + $(hide) chmod +x $@ +endif + diff --git a/make/core/ccache.mk b/make/core/ccache.mk new file mode 100644 index 0000000..d10aceb --- /dev/null +++ b/make/core/ccache.mk @@ -0,0 +1,61 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# We no longer provide a ccache prebuilt. +# +# Ours was old, and had a number of issues that triggered non-reproducible +# results and other failures. Newer ccache versions may fix some of those +# issues, but at the large scale of our build servers, we weren't seeing +# significant performance gains from using ccache -- you end up needing very +# good locality and/or very large caches if you're building many different +# configurations. +# +# Local no-change full rebuilds were showing better results, but why not just +# use incremental builds at that point? +# +# So if you still want to use ccache, continue setting USE_CCACHE, but also set +# the CCACHE_EXEC environment variable to the path to your ccache executable. +ifneq ($(CCACHE_EXEC),) +ifneq ($(filter-out false,$(USE_CCACHE)),) + # The default check uses size and modification time, causing false misses + # since the mtime depends when the repo was checked out + CCACHE_COMPILERCHECK ?= content + + # See man page, optimizations to get more cache hits + # implies that __DATE__ and __TIME__ are not critical for functionality. + # Ignore include file modification time since it will depend on when + # the repo was checked out + CCACHE_SLOPPINESS := time_macros,include_file_mtime,file_macro + + # Turn all preprocessor absolute paths into relative paths. + # Fixes absolute paths in preprocessed source due to use of -g. + # We don't really use system headers much so the rootdir is + # fine; ensures these paths are relative for all Android trees + # on a workstation. + CCACHE_BASEDIR := / + + # Workaround for ccache with clang. + # See http://petereisentraut.blogspot.com/2011/09/ccache-and-clang-part-2.html + CCACHE_CPP2 := true + + ifndef CC_WRAPPER + CC_WRAPPER := $(CCACHE_EXEC) + endif + ifndef CXX_WRAPPER + CXX_WRAPPER := $(CCACHE_EXEC) + endif +endif +endif diff --git a/make/core/check_elf_file.mk b/make/core/check_elf_file.mk new file mode 100644 index 0000000..b5be81f --- /dev/null +++ b/make/core/check_elf_file.mk @@ -0,0 +1,54 @@ +# Check the correctness of the prebuilt ELF files +# +# This check ensures that DT_SONAME matches with the filename, DT_NEEDED +# matches the shared libraries specified in LOCAL_SHARED_LIBRARIES, and all +# undefined symbols in the prebuilt binary can be found in one of the shared +# libraries specified in LOCAL_SHARED_LIBRARIES. +# +# Inputs: +# - LOCAL_ALLOW_UNDEFINED_SYMBOLS +# - LOCAL_BUILT_MODULE +# - LOCAL_IS_HOST_MODULE +# - LOCAL_MODULE_CLASS +# - intermediates +# - my_installed_module_stem +# - my_prebuilt_src_file +# - my_check_elf_file_shared_lib_files +# - my_system_shared_libraries + +ifndef LOCAL_IS_HOST_MODULE +ifneq ($(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS),) +check_elf_files_stamp := $(intermediates)/check_elf_files.timestamp +$(check_elf_files_stamp): PRIVATE_SONAME := $(if $(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES),$(my_installed_module_stem)) +$(check_elf_files_stamp): PRIVATE_ALLOW_UNDEFINED_SYMBOLS := $(LOCAL_ALLOW_UNDEFINED_SYMBOLS) +$(check_elf_files_stamp): PRIVATE_SYSTEM_SHARED_LIBRARIES := $(my_system_shared_libraries) +# PRIVATE_SHARED_LIBRARY_FILES are file paths to built shared libraries. +# In addition to $(my_check_elf_file_shared_lib_files), some file paths are +# added by `resolve-shared-libs-for-elf-file-check` from `core/main.mk`. +$(check_elf_files_stamp): PRIVATE_SHARED_LIBRARY_FILES := $(my_check_elf_file_shared_lib_files) +$(check_elf_files_stamp): $(my_prebuilt_src_file) $(my_check_elf_file_shared_lib_files) $(CHECK_ELF_FILE) $(LLVM_READOBJ) + @echo Check prebuilt ELF binary: $< + $(hide) mkdir -p $(dir $@) + $(hide) rm -f $@ + $(hide) $(CHECK_ELF_FILE) \ + --skip-bad-elf-magic \ + --skip-unknown-elf-machine \ + $(if $(PRIVATE_SONAME),--soname $(PRIVATE_SONAME)) \ + $(foreach l,$(PRIVATE_SHARED_LIBRARY_FILES),--shared-lib $(l)) \ + $(foreach l,$(PRIVATE_SYSTEM_SHARED_LIBRARIES),--system-shared-lib $(l)) \ + $(if $(PRIVATE_ALLOW_UNDEFINED_SYMBOLS),--allow-undefined-symbols) \ + --llvm-readobj=$(LLVM_READOBJ) \ + $< + $(hide) touch $@ + +CHECK_ELF_FILES.$(check_elf_files_stamp) := 1 + +ifneq ($(strip $(LOCAL_CHECK_ELF_FILES)),false) +ifneq ($(strip $(BUILD_BROKEN_PREBUILT_ELF_FILES)),true) +$(LOCAL_BUILT_MODULE): $(check_elf_files_stamp) +check-elf-files: $(check_elf_files_stamp) +endif # BUILD_BROKEN_PREBUILT_ELF_FILES +endif # LOCAL_CHECK_ELF_FILES + +endif # SHARED_LIBRARIES, EXECUTABLES, NATIVE_TESTS +endif # !LOCAL_IS_HOST_MODULE diff --git a/make/core/checktree b/make/core/checktree new file mode 100755 index 0000000..b0b9cfa --- /dev/null +++ b/make/core/checktree @@ -0,0 +1,113 @@ +#!/usr/bin/python -E + +import sys, os, re + +excludes = [r'.*?/\.obj.*?', + r'.*?~', + r'.*?\/.DS_Store', + r'.*?\/.gdb_history', + r'.*?\/buildspec.mk', + r'.*?/\..*?\.swp', + r'.*?/out/.*?', + r'.*?/install/.*?'] + +excludes_compiled = map(re.compile, excludes) + +def filter_excludes(str): + for e in excludes_compiled: + if e.match(str): + return False + return True + +def split_perforce_parts(s): + spaces = ((s.count(" ") + 1) / 3) * 2 + pos = 0 + while spaces > 0: + pos = s.find(" ", pos) + 1 + spaces = spaces - 1 + return s[pos:] + +def quotate(s): + return '"' + s + '"' + +class PerforceError(Exception): + def __init__(self,value): + self.value = value + def __str__(self): + return repr(self.value) + + +def run(command, regex, filt): + def matchit(s): + m = regex_compiled.match(s) + if m: + return m.group(1) + else: + return "" + def filterit(s): + if filt_compiled.match(s): + return True + else: + return False + + fd = os.popen(command); + lines = fd.readlines() + status = fd.close() + if status: + raise PerforceError("error calling " + command) + + regex_compiled = re.compile(regex) + filt_compiled = re.compile(filt) + + if len(lines) >= 1: + lines = filter(filterit, lines) + if len(lines) >= 1: + return map(matchit, lines) + return None + +try: + if len(sys.argv) == 1: + do_exclude = True + elif len(sys.argv) == 2 and sys.argv[1] == "-a": + do_exclude = False + else: + print "usage: checktree [-a]" + print " -a don't filter common crud in the tree" + sys.exit(1) + + have = run("p4 have ...", r'[^#]+#[0-9]+ - (.*)', r'.*') + + cwd = os.getcwd() + files = run("find . -not -type d", r'.(.*)', r'.*') + files = map(lambda s: cwd+s, files) + + added_depot_path = run("p4 opened ...", r'([^#]+)#.*', r'.*?#[0-9]+ - add .*'); + added = [] + if added_depot_path: + added_depot_path = map(quotate, added_depot_path) + + where = "p4 where " + " ".join(added_depot_path) + added = run(where, r'(.*)', r'.*') + added = map(split_perforce_parts, added) + + extras = [] + + # Python 2.3 -- still default on Mac OS X -- does not have set() + # Make dict's here to support the "in" operations below + have = dict().fromkeys(have, 1) + added = dict().fromkeys(added, 1) + + for file in files: + if not file in have: + if not file in added: + extras.append(file) + + if do_exclude: + extras = filter(filter_excludes, extras) + + for s in extras: + print s.replace(" ", "\\ ") + +except PerforceError, e: + sys.exit(2) + diff --git a/make/core/clang/HOST_x86.mk b/make/core/clang/HOST_x86.mk new file mode 100644 index 0000000..2e0865b --- /dev/null +++ b/make/core/clang/HOST_x86.mk @@ -0,0 +1,2 @@ +$(clang_2nd_arch_prefix)HOST_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i386.a +$(clang_2nd_arch_prefix)HOST_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i386.a diff --git a/make/core/clang/HOST_x86_64.mk b/make/core/clang/HOST_x86_64.mk new file mode 100644 index 0000000..3fd0541 --- /dev/null +++ b/make/core/clang/HOST_x86_64.mk @@ -0,0 +1,2 @@ +HOST_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64.a +HOST_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64.a diff --git a/make/core/clang/OWNERS b/make/core/clang/OWNERS new file mode 100644 index 0000000..d41d3fc --- /dev/null +++ b/make/core/clang/OWNERS @@ -0,0 +1,4 @@ +chh@google.com +pirama@google.com +srhines@google.com +yikong@google.com diff --git a/make/core/clang/TARGET_arm.mk b/make/core/clang/TARGET_arm.mk new file mode 100644 index 0000000..f18747a --- /dev/null +++ b/make/core/clang/TARGET_arm.mk @@ -0,0 +1,10 @@ +$(clang_2nd_arch_prefix)RS_TRIPLE := renderscript32-linux-androideabi +$(clang_2nd_arch_prefix)RS_TRIPLE_CFLAGS := +$(clang_2nd_arch_prefix)RS_COMPAT_TRIPLE := armv7-none-linux-gnueabi + +$(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-arm-android.a +$(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-arm-android.a + +# Address sanitizer clang config +$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan +$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan diff --git a/make/core/clang/TARGET_arm64.mk b/make/core/clang/TARGET_arm64.mk new file mode 100644 index 0000000..42bed0a --- /dev/null +++ b/make/core/clang/TARGET_arm64.mk @@ -0,0 +1,10 @@ +RS_TRIPLE := renderscript64-linux-android +RS_TRIPLE_CFLAGS := +RS_COMPAT_TRIPLE := aarch64-linux-android + +TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-aarch64-android.a +TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-aarch64-android.a + +# Address sanitizer clang config +ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64 +ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64 diff --git a/make/core/clang/TARGET_x86.mk b/make/core/clang/TARGET_x86.mk new file mode 100644 index 0000000..5491a05 --- /dev/null +++ b/make/core/clang/TARGET_x86.mk @@ -0,0 +1,10 @@ +$(clang_2nd_arch_prefix)RS_TRIPLE := renderscript32-linux-androideabi +$(clang_2nd_arch_prefix)RS_TRIPLE_CFLAGS := -D__i386__ +$(clang_2nd_arch_prefix)RS_COMPAT_TRIPLE := i686-linux-android + +$(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i686-android.a +$(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i686-android.a + +# Address sanitizer clang config +$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan +$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan diff --git a/make/core/clang/TARGET_x86_64.mk b/make/core/clang/TARGET_x86_64.mk new file mode 100644 index 0000000..167db72 --- /dev/null +++ b/make/core/clang/TARGET_x86_64.mk @@ -0,0 +1,10 @@ +RS_TRIPLE := renderscript64-linux-android +RS_TRIPLE_CFLAGS := -D__x86_64__ +RS_COMPAT_TRIPLE := x86_64-linux-android + +TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64-android.a +TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64-android.a + +# Address sanitizer clang config +ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64 +ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64 diff --git a/make/core/clang/config.mk b/make/core/clang/config.mk new file mode 100644 index 0000000..28a75ec --- /dev/null +++ b/make/core/clang/config.mk @@ -0,0 +1,57 @@ +## Clang configurations. + +LLVM_READOBJ := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-readobj + +LLVM_RTLIB_PATH := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib64/clang/$(LLVM_RELEASE_VERSION)/lib/linux/ + +define convert-to-clang-flags +$(strip $(filter-out $(CLANG_CONFIG_UNKNOWN_CFLAGS),$(1))) +endef + +CLANG_DEFAULT_UB_CHECKS := \ + bool \ + integer-divide-by-zero \ + return \ + returns-nonnull-attribute \ + shift-exponent \ + unreachable \ + vla-bound \ + +# TODO(danalbert): The following checks currently have compiler performance +# issues. +# CLANG_DEFAULT_UB_CHECKS += alignment +# CLANG_DEFAULT_UB_CHECKS += bounds +# CLANG_DEFAULT_UB_CHECKS += enum +# CLANG_DEFAULT_UB_CHECKS += float-cast-overflow +# CLANG_DEFAULT_UB_CHECKS += float-divide-by-zero +# CLANG_DEFAULT_UB_CHECKS += nonnull-attribute +# CLANG_DEFAULT_UB_CHECKS += null +# CLANG_DEFAULT_UB_CHECKS += shift-base +# CLANG_DEFAULT_UB_CHECKS += signed-integer-overflow + +# TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on. +# https://llvm.org/PR19302 +# http://reviews.llvm.org/D6974 +# CLANG_DEFAULT_UB_CHECKS += object-size + +# HOST config +clang_2nd_arch_prefix := +include $(BUILD_SYSTEM)/clang/HOST_$(HOST_ARCH).mk + +# HOST_2ND_ARCH config +ifdef HOST_2ND_ARCH +clang_2nd_arch_prefix := $(HOST_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/clang/HOST_$(HOST_2ND_ARCH).mk +endif + +# TARGET config +clang_2nd_arch_prefix := +include $(BUILD_SYSTEM)/clang/TARGET_$(TARGET_ARCH).mk + +# TARGET_2ND_ARCH config +ifdef TARGET_2ND_ARCH +clang_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/clang/TARGET_$(TARGET_2ND_ARCH).mk +endif + +include $(BUILD_SYSTEM)/clang/tidy.mk diff --git a/make/core/clang/tidy.mk b/make/core/clang/tidy.mk new file mode 100644 index 0000000..8a40878 --- /dev/null +++ b/make/core/clang/tidy.mk @@ -0,0 +1,42 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Returns 2nd word of $(1) if $(2) has prefix of the 1st word of $(1). +define find_default_local_tidy_check2 +$(if $(filter $(word 1,$(1))%,$(2)/),$(word 2,$(1))) +endef + +# Returns 2nd part of $(1) if $(2) has prefix of the 1st part of $(1). +define find_default_local_tidy_check +$(call find_default_local_tidy_check2,$(subst :,$(space),$(1)),$(2)) +endef + +# Returns the default tidy check list for local project path $(1). +# Match $(1) with all patterns in DEFAULT_LOCAL_TIDY_CHECKS and use the last +# most specific pattern. +define default_global_tidy_checks +$(lastword \ + $(DEFAULT_GLOBAL_TIDY_CHECKS) \ + $(foreach pattern,$(DEFAULT_LOCAL_TIDY_CHECKS), \ + $(call find_default_local_tidy_check,$(pattern),$(1)) \ + ) \ +) +endef + +# Default filter contains current directory $1 and optional DEFAULT_TIDY_HEADER_DIRS. +define default_tidy_header_filter + -header-filter=$(if $(DEFAULT_TIDY_HEADER_DIRS),"($1/|$(DEFAULT_TIDY_HEADER_DIRS))",$1/) +endef diff --git a/make/core/cleanbuild.mk b/make/core/cleanbuild.mk new file mode 100644 index 0000000..5576785 --- /dev/null +++ b/make/core/cleanbuild.mk @@ -0,0 +1,152 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Absolute path of the present working direcotry. +# This overrides the shell variable $PWD, which does not necessarily points to +# the top of the source tree, for example when "make -C" is used in m/mm/mmm. +PWD := $(shell pwd) + +TOP := . +TOPDIR := + +BUILD_SYSTEM := $(TOPDIR)build/make/core + +# Set up various standard variables based on configuration +# and host information. +include $(BUILD_SYSTEM)/config.mk + +include $(SOONG_MAKEVARS_MK) + +include $(BUILD_SYSTEM)/clang/config.mk + +# CTS-specific config. +-include cts/build/config.mk +# VTS-specific config. +-include test/vts/tools/vts-tradefed/build/config.mk +# device-tests-specific-config. +-include tools/tradefederation/build/suites/device-tests/config.mk +# general-tests-specific-config. +-include tools/tradefederation/build/suites/general-tests/config.mk + +INTERNAL_CLEAN_STEPS := + +# Builds up a list of clean steps. Creates a unique +# id for each step by taking makefile path, INTERNAL_CLEAN_BUILD_VERSION +# and appending an increasing number of '@' characters. +# +# $(1): shell command to run +# $(2): indicate to not use makefile path as part of step id if not empty. +# $(2) should only be used in build/make/core/cleanspec.mk: just for compatibility. +define _add-clean-step + $(if $(strip $(INTERNAL_CLEAN_BUILD_VERSION)),, \ + $(error INTERNAL_CLEAN_BUILD_VERSION not set)) + $(eval _acs_makefile_prefix := $(lastword $(MAKEFILE_LIST))) + $(eval _acs_makefile_prefix := $(subst /,_,$(_acs_makefile_prefix))) + $(eval _acs_makefile_prefix := $(subst .,-,$(_acs_makefile_prefix))) + $(eval _acs_makefile_prefix := $(_acs_makefile_prefix)_acs) + $(if $($(_acs_makefile_prefix)),,\ + $(eval $(_acs_makefile_prefix) := $(INTERNAL_CLEAN_BUILD_VERSION))) + $(eval $(_acs_makefile_prefix) := $($(_acs_makefile_prefix))@) + $(if $(strip $(2)),$(eval _acs_id := $($(_acs_makefile_prefix))),\ + $(eval _acs_id := $(_acs_makefile_prefix)$($(_acs_makefile_prefix)))) + $(eval INTERNAL_CLEAN_STEPS += $(_acs_id)) + $(eval INTERNAL_CLEAN_STEP.$(_acs_id) := $(1)) + $(eval _acs_id :=) + $(eval _acs_makefile_prefix :=) +endef +define add-clean-step +$(eval # for build/make/core/cleanspec.mk, dont use makefile path as part of step id) \ +$(if $(filter %/cleanspec.mk,$(lastword $(MAKEFILE_LIST))),\ + $(eval $(call _add-clean-step,$(1),true)),\ + $(eval $(call _add-clean-step,$(1)))) +endef + +# Defines INTERNAL_CLEAN_BUILD_VERSION and the individual clean steps. +# cleanspec.mk is outside of the core directory so that more people +# can have permission to touch it. +include $(BUILD_SYSTEM)/cleanspec.mk +INTERNAL_CLEAN_BUILD_VERSION := $(strip $(INTERNAL_CLEAN_BUILD_VERSION)) +INTERNAL_CLEAN_STEPS := $(strip $(INTERNAL_CLEAN_STEPS)) + +# If the clean_steps.mk file is missing (usually after a clean build) +# then we won't do anything. +CURRENT_CLEAN_BUILD_VERSION := MISSING +CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS) + +# Read the current state from the file, if present. +# Will set CURRENT_CLEAN_BUILD_VERSION and CURRENT_CLEAN_STEPS. +# +clean_steps_file := $(PRODUCT_OUT)/clean_steps.mk +-include $(clean_steps_file) + +ifeq ($(CURRENT_CLEAN_BUILD_VERSION),MISSING) + # Do nothing +else ifneq ($(CURRENT_CLEAN_BUILD_VERSION),$(INTERNAL_CLEAN_BUILD_VERSION)) + # The major clean version is out-of-date. Do a full clean, and + # don't even bother with the clean steps. + $(info *** A clean build is required because of a recent change.) + $(shell rm -rf $(OUT_DIR)) + $(info *** Done with the cleaning, now starting the real build.) +else + # The major clean version is correct. Find the list of clean steps + # that we need to execute to get up-to-date. + steps := \ + $(filter-out $(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_STEPS)) + $(foreach step,$(steps), \ + $(info Clean step: $(INTERNAL_CLEAN_STEP.$(step))) \ + $(shell $(INTERNAL_CLEAN_STEP.$(step))) \ + ) + + # Rewrite the clean step for the second arch. + ifdef TARGET_2ND_ARCH + # $(1): the clean step cmd + # $(2): the prefix to search for + # $(3): the prefix to replace with + define -cs-rewrite-cleanstep + $(if $(filter $(2)/%,$(1)),\ + $(eval _crs_new_cmd := $(patsubst $(2)/%,$(3)/%,$(1)))\ + $(info Clean step: $(_crs_new_cmd))\ + $(shell $(_crs_new_cmd))) + endef + $(foreach step,$(steps), \ + $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_INTERMEDIATES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES))\ + $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_SHARED_LIBRARIES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES))\ + $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_VENDOR_SHARED_LIBRARIES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES))\ + $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES),$(TARGET_OUT_INTERMEDIATES))\ + $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES),$(TARGET_OUT_SHARED_LIBRARIES))\ + $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES),$(TARGET_OUT_VENDOR_SHARED_LIBRARIES))\ + ) + endif + _crs_new_cmd := + steps := +endif + +# Write the new state to the file. +# +ifneq ($(CURRENT_CLEAN_BUILD_VERSION)-$(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_BUILD_VERSION)-$(INTERNAL_CLEAN_STEPS)) +$(shell mkdir -p $(dir $(clean_steps_file))) +$(file >$(clean_steps_file).tmp,CURRENT_CLEAN_BUILD_VERSION := $(INTERNAL_CLEAN_BUILD_VERSION)$(newline)CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)$(newline)) +$(shell if ! cmp -s $(clean_steps_file).tmp $(clean_steps_file); then \ + mv $(clean_steps_file).tmp $(clean_steps_file); \ + else \ + rm $(clean_steps_file).tmp; \ + fi) +endif + +CURRENT_CLEAN_BUILD_VERSION := +CURRENT_CLEAN_STEPS := +clean_steps_file := +INTERNAL_CLEAN_STEPS := +INTERNAL_CLEAN_BUILD_VERSION := diff --git a/make/core/cleanspec.mk b/make/core/cleanspec.mk new file mode 100644 index 0000000..af28954 --- /dev/null +++ b/make/core/cleanspec.mk @@ -0,0 +1,69 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Just bump this if you want to force a clean build. +# ********************************************************************** +# WHEN DOING SO +# 1. DELETE ANY "add-clean-step" ENTRIES THAT HAVE PILED UP IN THIS FILE. +# 2. REMOVE ALL FILES NAMED CleanSpec.mk. +# 3. BUMP THE VERSION. +# IDEALLY, THOSE STEPS SHOULD BE DONE ATOMICALLY. +# ********************************************************************** +# +INTERNAL_CLEAN_BUILD_VERSION := 6 +# +# *********************************************************************** +# Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step! +# *********************************************************************** + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +subdir_cleanspecs := \ + $(file <$(OUT_DIR)/.module_paths/CleanSpec.mk.list) +include $(subdir_cleanspecs) +subdir_cleanspecs := diff --git a/make/core/clear_vars.mk b/make/core/clear_vars.mk new file mode 100644 index 0000000..b5b371c --- /dev/null +++ b/make/core/clear_vars.mk @@ -0,0 +1,519 @@ +########################################################### +## Clear out values of all variables used by rule templates. +########################################################### + +# '',true +LOCAL_2ND_ARCH_VAR_PREFIX:= +LOCAL_32_BIT_ONLY:= +LOCAL_AAPT2_ONLY:= +LOCAL_AAPT_FLAGS:= +LOCAL_AAPT_INCLUDE_ALL_RESOURCES:= +LOCAL_AAPT_NAMESPACES:= +LOCAL_ADDITIONAL_CERTIFICATES:= +LOCAL_ADDITIONAL_CHECKED_MODULE:= +LOCAL_ADDITIONAL_DEPENDENCIES:= +LOCAL_ADDITIONAL_HTML_DIR:= +LOCAL_ADDITIONAL_JAVA_DIR:= +LOCAL_AIDL_INCLUDES:= +LOCAL_ALLOW_UNDEFINED_SYMBOLS:= +LOCAL_ANNOTATION_PROCESSORS:= +LOCAL_ANNOTATION_PROCESSOR_CLASSES:= +LOCAL_APIDIFF_NEWAPI:= +LOCAL_APIDIFF_OLDAPI:= +LOCAL_APK_LIBRARIES:= +LOCAL_APK_SET_INSTALL_FILE:= +LOCAL_APKCERTS_FILE:= +LOCAL_ARM_MODE:= +LOCAL_ASFLAGS:= +LOCAL_ASSET_DIR:= +LOCAL_BUILT_MODULE:= +LOCAL_BUILT_MODULE_STEM:= +LOCAL_CC:= +LOCAL_CERTIFICATE:= +LOCAL_CFLAGS:= +LOCAL_CHECK_SAME_VNDK_VARIANTS:= +LOCAL_CHECKED_MODULE:= +LOCAL_C_INCLUDES:= +LOCAL_CLANG:= +LOCAL_CLANG_ASFLAGS:= +LOCAL_CLANG_CFLAGS:= +LOCAL_CLANG_CONLYFLAGS:= +LOCAL_CLANG_CPPFLAGS:= +LOCAL_CLANG_LDFLAGS:= +LOCAL_CLASSPATH:= +LOCAL_COMPATIBILITY_SUITE:= +LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY:= +LOCAL_COMPATIBILITY_SUPPORT_FILES:= +LOCAL_COMPRESSED_MODULE:= +LOCAL_CONLYFLAGS:= +LOCAL_COPY_HEADERS:= +LOCAL_COPY_HEADERS_TO:= +LOCAL_CPP_EXTENSION:= +LOCAL_CPPFLAGS:= +LOCAL_CPP_STD:= +LOCAL_C_STD:= +LOCAL_CTS_TEST_PACKAGE:= +LOCAL_CTS_TEST_RUNNER:= +LOCAL_CXX:= +LOCAL_CXX_STL := default +LOCAL_DEX_PREOPT_APP_IMAGE:= +LOCAL_DEX_PREOPT_FLAGS:= +LOCAL_DEX_PREOPT_GENERATE_PROFILE:= +LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING:= +LOCAL_DEX_PREOPT:= # '',true,false +LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG:= +LOCAL_DISABLE_TEST_CONFIG:= +LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES:= +LOCAL_DONT_CHECK_MODULE:= +# Don't delete the META_INF dir when merging static Java libraries. +LOCAL_DONT_DELETE_JAR_META_INF:= +LOCAL_DONT_MERGE_MANIFESTS:= +LOCAL_DPI_FILE_STEM:= +LOCAL_DPI_VARIANTS:= +LOCAL_DROIDDOC_ANNOTATIONS_ZIP := +LOCAL_DROIDDOC_API_VERSIONS_XML := +LOCAL_DROIDDOC_ASSET_DIR:= +LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:= +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:= +LOCAL_DROIDDOC_DOC_ZIP := +LOCAL_DROIDDOC_HTML_DIR:= +LOCAL_DROIDDOC_METADATA_ZIP:= +LOCAL_DROIDDOC_OPTIONS:= +LOCAL_DROIDDOC_SOURCE_PATH:= +LOCAL_DROIDDOC_STUB_OUT_DIR:= +LOCAL_DROIDDOC_STUBS_SRCJAR := +LOCAL_DROIDDOC_TEMPLATE_DIR:= +LOCAL_DROIDDOC_USE_STANDARD_DOCLET:= +LOCAL_DX_FLAGS:= +LOCAL_DYLIB_LIBRARIES:= +LOCAL_EMMA_COVERAGE_FILTER:= +LOCAL_EMMA_INSTRUMENT:= +LOCAL_ENFORCE_USES_LIBRARIES:= +LOCAL_ERROR_PRONE_FLAGS:= +LOCAL_EXPORT_CFLAGS:= +LOCAL_EXPORT_C_INCLUDE_DEPS:= +LOCAL_EXPORT_C_INCLUDE_DIRS:= +LOCAL_EXPORT_HEADER_LIBRARY_HEADERS:= +LOCAL_EXPORT_PACKAGE_RESOURCES:= +LOCAL_EXPORT_PROGUARD_FLAG_FILES:= +LOCAL_EXPORT_SDK_LIBRARIES:= +LOCAL_EXPORT_SHARED_LIBRARY_HEADERS:= +LOCAL_EXPORT_STATIC_LIBRARY_HEADERS:= +LOCAL_EXTRA_FULL_TEST_CONFIGS:= +LOCAL_EXTRACT_APK:= +LOCAL_EXTRACT_DPI_APK:= +LOCAL_FILE_CONTEXTS:= +LOCAL_FINDBUGS_FLAGS:= +LOCAL_FORCE_STATIC_EXECUTABLE:= +LOCAL_FULL_CLASSES_JACOCO_JAR:= +LOCAL_FULL_CLASSES_PRE_JACOCO_JAR:= +LOCAL_FULL_INIT_RC:= +LOCAL_FULL_LIBS_MANIFEST_FILES:= +LOCAL_FULL_MANIFEST_FILE:= +LOCAL_FULL_TEST_CONFIG:= +LOCAL_FULL_VINTF_FRAGMENTS:= +LOCAL_FUZZ_ENGINE:= +LOCAL_FUZZ_INSTALLED_SHARED_DEPS:= +LOCAL_GCNO_FILES:= +LOCAL_GENERATED_SOURCES:= +# Group static libraries with "-Wl,--start-group" and "-Wl,--end-group" when linking. +LOCAL_GROUP_STATIC_LIBRARIES:= +LOCAL_GTEST:=true +LOCAL_HEADER_LIBRARIES:= +LOCAL_HOST_PREFIX:= +LOCAL_HOST_REQUIRED_MODULES:= +LOCAL_INIT_RC:= +LOCAL_INJECT_BSSL_HASH:= +LOCAL_INSTALLED_MODULE:= +LOCAL_INSTALLED_MODULE_STEM:= +LOCAL_INSTRUMENTATION_FOR:= +LOCAL_INTERMEDIATE_SOURCE_DIR:= +LOCAL_INTERMEDIATE_SOURCES:= +LOCAL_INTERMEDIATE_TARGETS:= +LOCAL_IS_FUZZ_TARGET:= +LOCAL_IS_HOST_MODULE:= +LOCAL_IS_RUNTIME_RESOURCE_OVERLAY:= +LOCAL_IS_UNIT_TEST:= +LOCAL_JACK_CLASSPATH:= +LOCAL_JACK_COVERAGE_EXCLUDE_FILTER:= +LOCAL_JACK_COVERAGE_INCLUDE_FILTER:= +# '' (ie disabled), disabled, full, incremental, javac_frontend +LOCAL_JACK_ENABLED:=$(DEFAULT_JACK_ENABLED) +LOCAL_JACK_FLAGS:= +LOCAL_JACK_PLUGIN:= +LOCAL_JACK_PLUGIN_PATH:= +LOCAL_JACK_PROGUARD_FLAGS:= +LOCAL_JAR_EXCLUDE_FILES:= +LOCAL_JAR_EXCLUDE_PACKAGES:= +LOCAL_JARJAR_RULES:= +LOCAL_JAR_MANIFEST:= +LOCAL_JAR_PACKAGES:= +LOCAL_JAR_PROCESSOR:= +LOCAL_JAR_PROCESSOR_ARGS:= +LOCAL_JAVACFLAGS:= +LOCAL_JAVA_LANGUAGE_VERSION:= +LOCAL_JAVA_LAYERS_FILE:= +LOCAL_JAVA_LIBRARIES:= +LOCAL_JAVA_RESOURCE_DIRS:= +LOCAL_JAVA_RESOURCE_FILES:= +LOCAL_JETIFIER_ENABLED:= +LOCAL_JNI_SHARED_LIBRARIES:= +LOCAL_JNI_SHARED_LIBRARIES_ABI:= +LOCAL_CERTIFICATE_LINEAGE:= +LOCAL_LDFLAGS:= +LOCAL_LDLIBS:= +LOCAL_LICENSE_CONDITIONS:= +LOCAL_LICENSE_KINDS:= +LOCAL_LICENSE_INSTALL_MAP:= +LOCAL_LICENSE_PACKAGE_NAME:= +LOCAL_LOGTAGS_FILES:= +LOCAL_MANIFEST_FILE:= +LOCAL_MANIFEST_INSTRUMENTATION_FOR:= +LOCAL_MANIFEST_PACKAGE_NAME:= +LOCAL_MIN_SDK_VERSION:= +LOCAL_MODULE:= +LOCAL_MODULE_CLASS:= +LOCAL_MODULE_HOST_ARCH:= +LOCAL_MODULE_HOST_ARCH_WARN:= +LOCAL_MODULE_HOST_CROSS_ARCH:= +LOCAL_MODULE_HOST_OS:= +LOCAL_MODULE_IS_CONTAINER:= +LOCAL_MODULE_OWNER:= +LOCAL_MODULE_PATH:= +LOCAL_MODULE_RELATIVE_PATH := +LOCAL_MODULE_STEM:= +LOCAL_MODULE_SUFFIX:= +LOCAL_MODULE_SYMLINKS:= +LOCAL_MODULE_TAGS:= +LOCAL_MODULE_TARGET_ARCH:= +LOCAL_MODULE_TARGET_ARCH_WARN:= +LOCAL_MODULE_TYPE:= +LOCAL_MODULE_UNSUPPORTED_HOST_ARCH:= +LOCAL_MODULE_UNSUPPORTED_HOST_ARCH_WARN:= +LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH:= +LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH_WARN:= +LOCAL_MULTILIB:= +LOCAL_NATIVE_BENCHMARK:= +LOCAL_NDK_STL_VARIANT:= +LOCAL_NDK_VERSION:=current +LOCAL_NO_CRT:= +LOCAL_NO_DEFAULT_COMPILER_FLAGS:= +LOCAL_NO_FPIE := +LOCAL_NO_LIBCRT_BUILTINS:= +LOCAL_NO_NOTICE_FILE:= +LOCAL_NO_PIC:= +LOCAL_NOSANITIZE:= +LOCAL_NO_STANDARD_LIBRARIES:= +LOCAL_NO_STATIC_ANALYZER:= +LOCAL_NOT_AVAILABLE_FOR_PLATFORM:= +LOCAL_NOTICE_FILE:= +LOCAL_ODM_MODULE:= +LOCAL_OEM_MODULE:= +LOCAL_OPTIONAL_USES_LIBRARIES:= +LOCAL_OVERRIDES_PACKAGES:= +LOCAL_OVERRIDES_MODULES:= +LOCAL_PACKAGE_NAME:= +LOCAL_PACKAGE_SPLITS:= +LOCAL_PACK_MODULE_RELOCATIONS:= +LOCAL_PATCH_MODULE:= +LOCAL_PICKUP_FILES:= +LOCAL_POST_INSTALL_CMD:= +LOCAL_POST_LINK_CMD:= +LOCAL_PREBUILT_COVERAGE_ARCHIVE:= +LOCAL_PREBUILT_EXECUTABLES:= +LOCAL_PREBUILT_JAVA_LIBRARIES:= +LOCAL_PREBUILT_JNI_LIBS:= +LOCAL_PREBUILT_LIBS:= +LOCAL_PREBUILT_MODULE_FILE:= +LOCAL_PREBUILT_OBJ_FILES:= +LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:= +LOCAL_PREBUILT_STRIP_COMMENTS:= +LOCAL_USE_EMBEDDED_DEX:= +LOCAL_USE_EMBEDDED_NATIVE_LIBS:= +LOCAL_PRESUBMIT_DISABLED:= +LOCAL_PRIVATE_PLATFORM_APIS:= +LOCAL_PRIVILEGED_MODULE:= +LOCAL_PROC_MACRO_LIBRARIES:= +# '',full,custom,disabled,obfuscation,optimization +LOCAL_PRODUCT_MODULE:= +# TODO(b/135957588) Remove LOCAL_PRODUCT_SERVICES_MODULE +LOCAL_PRODUCT_SERVICES_MODULE := +LOCAL_PROGUARD_ENABLED:= +LOCAL_PROGUARD_FLAG_FILES:= +LOCAL_PROGUARD_FLAGS:= +LOCAL_PROGUARD_FLAGS_DEPS:= +LOCAL_PROPRIETARY_MODULE:= +LOCAL_PROTOC_FLAGS:= +# lite(default),micro,nano,stream,full,nanopb-c,nanopb-c-enable_malloc,nanopb-c-16bit,nanopb-c-enable_malloc-16bit,nanopb-c-32bit,nanopb-c-enable_malloc-32bit +LOCAL_PROTOC_OPTIMIZE_TYPE:= +LOCAL_PROTO_JAVA_OUTPUT_PARAMS:= +LOCAL_PROVIDES_USES_LIBRARY:= +LOCAL_R8_FLAG_FILES:= +LOCAL_RECORDED_MODULE_TYPE:= +LOCAL_RENDERSCRIPT_CC:= +LOCAL_RENDERSCRIPT_COMPATIBILITY:= +LOCAL_RENDERSCRIPT_FLAGS:= +LOCAL_RENDERSCRIPT_INCLUDES:= +LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE:= +LOCAL_RENDERSCRIPT_TARGET_API:= +# Used to replace the installed file of a presigned prebuilt apk in PDK fusion build, +# to avoid installing the presigned apks with classes.dex unstripped. +LOCAL_REPLACE_PREBUILT_APK_INSTALLED:= +LOCAL_REQUIRED_MODULES:= +LOCAL_RES_LIBRARIES:= +LOCAL_RESOURCE_DIR:= +LOCAL_RLIB_LIBRARIES:= +LOCAL_RMTYPEDEFS:= +LOCAL_ROTATION_MIN_SDK_VERSION:= +LOCAL_RUNTIME_LIBRARIES:= +LOCAL_RRO_THEME:= +LOCAL_RTTI_FLAG:= +LOCAL_SANITIZE:= +LOCAL_SANITIZE_DIAG:= +LOCAL_SANITIZE_RECOVER:= +LOCAL_SANITIZE_NO_RECOVER:= +LOCAL_SANITIZE_BLOCKLIST := +LOCAL_SDK_LIBRARIES := +LOCAL_SDK_RES_VERSION:= +LOCAL_SDK_VERSION:= +LOCAL_SHARED_ANDROID_LIBRARIES:= +LOCAL_SHARED_LIBRARIES:= +LOCAL_SOONG_AAR := +LOCAL_SOONG_BUILT_INSTALLED := +LOCAL_SOONG_BUNDLE := +LOCAL_SOONG_CLASSES_JAR := +LOCAL_SOONG_DEX_JAR := +LOCAL_SOONG_DEXPREOPT_CONFIG := +LOCAL_SOONG_EXPORT_PROGUARD_FLAGS := +LOCAL_SOONG_HEADER_JAR := +LOCAL_SOONG_INSTALL_PAIRS := +LOCAL_SOONG_INSTALL_SYMLINKS := +LOCAL_SOONG_INSTALLED_MODULE := +LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR := +LOCAL_SOONG_LICENSE_METADATA := +LOCAL_SOONG_LINK_TYPE := +LOCAL_SOONG_LINT_REPORTS := +LOCAL_SOONG_PROGUARD_DICT := +LOCAL_SOONG_PROGUARD_USAGE_ZIP := +LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE := +LOCAL_SOONG_DEVICE_RRO_DIRS := +LOCAL_SOONG_PRODUCT_RRO_DIRS := +LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES := +LOCAL_SOONG_SYMBOL_PATH := +LOCAL_SOONG_TOC := +LOCAL_SOONG_UNSTRIPPED_BINARY := +LOCAL_SOONG_VNDK_VERSION := +# '',true +LOCAL_SOURCE_FILES_ALL_GENERATED:= +LOCAL_SRC_FILES:= +LOCAL_SRC_FILES_EXCLUDE:= +LOCAL_SRCJARS:= +LOCAL_STATIC_ANDROID_LIBRARIES:= +LOCAL_STATIC_JAVA_AAR_LIBRARIES:= +LOCAL_STATIC_JAVA_LIBRARIES:= +LOCAL_STATIC_LIBRARIES:= +LOCAL_SYSTEM_EXT_MODULE:= +LOCAL_STRIP_MODULE:= +LOCAL_SYSTEM_SHARED_LIBRARIES:=none +LOCAL_TARGET_REQUIRED_MODULES:= +LOCAL_TEST_CONFIG:= +LOCAL_TEST_DATA:= +LOCAL_TEST_DATA_BINS:= +LOCAL_TEST_MAINLINE_MODULES:= +LOCAL_TEST_MODULE_TO_PROGUARD_WITH:= +LOCAL_TIDY:= +LOCAL_TIDY_CHECKS:= +LOCAL_TIDY_FLAGS:= +LOCAL_UNCOMPRESS_DEX:= +LOCAL_UNINSTALLABLE_MODULE:= +LOCAL_UNSTRIPPED_PATH:= +LOCAL_USE_AAPT2:= +LOCAL_USE_CLANG_LLD:= +LOCAL_USE_VNDK:= +LOCAL_USE_VNDK_PRODUCT:= +LOCAL_USES_LIBRARIES:= +LOCAL_VENDOR_MODULE:= +LOCAL_VINTF_FRAGMENTS:= +LOCAL_VNDK_DEPEND_ON_CORE_VARIANT:= +LOCAL_VTSC_FLAGS:= +LOCAL_VTS_INCLUDES:= +LOCAL_VTS_MODE:= +LOCAL_WARNINGS_ENABLE:= +LOCAL_WHOLE_STATIC_LIBRARIES:= +LOCAL_YACCFLAGS:= +LOCAL_CHECK_ELF_FILES:= +# TODO: deprecate, it does nothing +OVERRIDE_BUILT_MODULE_PATH:= + +# arch specific variables +LOCAL_ASFLAGS_$(TARGET_ARCH):= +LOCAL_CFLAGS_$(TARGET_ARCH):= +LOCAL_C_INCLUDES_$(TARGET_ARCH):= +LOCAL_CLANG_ASFLAGS_$(TARGET_ARCH):= +LOCAL_CLANG_CFLAGS_$(TARGET_ARCH):= +LOCAL_CLANG_CPPFLAGS_$(TARGET_ARCH):= +LOCAL_CLANG_LDFLAGS_$(TARGET_ARCH):= +LOCAL_CLANG_$(TARGET_ARCH):= +LOCAL_CPPFLAGS_$(TARGET_ARCH):= +LOCAL_GENERATED_SOURCES_$(TARGET_ARCH):= +LOCAL_HEADER_LIBRARIES_$(TARGET_ARCH):= +LOCAL_LDFLAGS_$(TARGET_ARCH):= +LOCAL_PACK_MODULE_RELOCATIONS_$(TARGET_ARCH):= +LOCAL_PREBUILT_JNI_LIBS_$(TARGET_ARCH):= +LOCAL_REQUIRED_MODULES_$(TARGET_ARCH):= +LOCAL_RUNTIME_LIBRARIES_$(TARGET_ARCH):= +LOCAL_SHARED_LIBRARIES_$(TARGET_ARCH):= +LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH):= +LOCAL_SOONG_JNI_LIBS_SYMBOLS:= +LOCAL_SRC_FILES_EXCLUDE_$(TARGET_ARCH):= +LOCAL_SRC_FILES_$(TARGET_ARCH):= +LOCAL_STATIC_LIBRARIES_$(TARGET_ARCH):= +LOCAL_STRIP_MODULE_$(TARGET_ARCH):= +LOCAL_WHOLE_STATIC_LIBRARIES_$(TARGET_ARCH):= +ifdef TARGET_2ND_ARCH +LOCAL_ASFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_CFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_C_INCLUDES_$(TARGET_2ND_ARCH):= +LOCAL_CLANG_ASFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_CLANG_CFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_CLANG_CPPFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_CLANG_LDFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_CLANG_$(TARGET_2ND_ARCH):= +LOCAL_CPPFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_GENERATED_SOURCES_$(TARGET_2ND_ARCH):= +LOCAL_HEADER_LIBRARIES_$(TARGET_2ND_ARCH):= +LOCAL_LDFLAGS_$(TARGET_2ND_ARCH):= +LOCAL_PACK_MODULE_RELOCATIONS_$(TARGET_2ND_ARCH):= +LOCAL_PREBUILT_JNI_LIBS_$(TARGET_2ND_ARCH):= +LOCAL_REQUIRED_MODULES_$(TARGET_2ND_ARCH):= +LOCAL_RUNTIME_LIBRARIES_$(TARGET_2ND_ARCH):= +LOCAL_SHARED_LIBRARIES_$(TARGET_2ND_ARCH):= +LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH):= +LOCAL_SRC_FILES_EXCLUDE_$(TARGET_2ND_ARCH):= +LOCAL_SRC_FILES_$(TARGET_2ND_ARCH):= +LOCAL_STATIC_LIBRARIES_$(TARGET_2ND_ARCH):= +LOCAL_STRIP_MODULE_$(TARGET_2ND_ARCH):= +LOCAL_WHOLE_STATIC_LIBRARIES_$(TARGET_2ND_ARCH):= +endif +LOCAL_ASFLAGS_$(HOST_ARCH):= +LOCAL_CFLAGS_$(HOST_ARCH):= +LOCAL_C_INCLUDES_$(HOST_ARCH):= +LOCAL_CLANG_ASFLAGS_$(HOST_ARCH):= +LOCAL_CLANG_CFLAGS_$(HOST_ARCH):= +LOCAL_CLANG_CPPFLAGS_$(HOST_ARCH):= +LOCAL_CLANG_$(HOST_ARCH):= +LOCAL_CLANG_LDFLAGS_$(HOST_ARCH):= +LOCAL_CPPFLAGS_$(HOST_ARCH):= +LOCAL_GENERATED_SOURCES_$(HOST_ARCH):= +LOCAL_HEADER_LIBRARIES_$(HOST_ARCH):= +LOCAL_LDFLAGS_$(HOST_ARCH):= +LOCAL_REQUIRED_MODULES_$(HOST_ARCH):= +LOCAL_RUNTIME_LIBRARIES_$(HOST_ARCH):= +LOCAL_SHARED_LIBRARIES_$(HOST_ARCH):= +LOCAL_SRC_FILES_EXCLUDE_$(HOST_ARCH):= +LOCAL_SRC_FILES_$(HOST_ARCH):= +LOCAL_STATIC_LIBRARIES_$(HOST_ARCH):= +LOCAL_WHOLE_STATIC_LIBRARIES_$(HOST_ARCH):= +ifdef HOST_2ND_ARCH +LOCAL_ASFLAGS_$(HOST_2ND_ARCH):= +LOCAL_CFLAGS_$(HOST_2ND_ARCH):= +LOCAL_C_INCLUDES_$(HOST_2ND_ARCH):= +LOCAL_CLANG_ASFLAGS_$(HOST_2ND_ARCH):= +LOCAL_CLANG_CFLAGS_$(HOST_2ND_ARCH):= +LOCAL_CLANG_CPPFLAGS_$(HOST_2ND_ARCH):= +LOCAL_CLANG_$(HOST_2ND_ARCH):= +LOCAL_CLANG_LDFLAGS_$(HOST_2ND_ARCH):= +LOCAL_CPPFLAGS_$(HOST_2ND_ARCH):= +LOCAL_GENERATED_SOURCES_$(HOST_2ND_ARCH):= +LOCAL_HEADER_LIBRARIES_$(HOST_2ND_ARCH):= +LOCAL_LDFLAGS_$(HOST_2ND_ARCH):= +LOCAL_REQUIRED_MODULES_$(HOST_2ND_ARCH):= +LOCAL_RUNTIME_LIBRARIES_$(HOST_2ND_ARCH):= +LOCAL_SHARED_LIBRARIES_$(HOST_2ND_ARCH):= +LOCAL_SRC_FILES_EXCLUDE_$(HOST_2ND_ARCH):= +LOCAL_SRC_FILES_$(HOST_2ND_ARCH):= +LOCAL_STATIC_LIBRARIES_$(HOST_2ND_ARCH):= +LOCAL_WHOLE_STATIC_LIBRARIES_$(HOST_2ND_ARCH):= +endif + +LOCAL_ASFLAGS_$(HOST_OS):= +LOCAL_CFLAGS_$(HOST_OS):= +LOCAL_C_INCLUDES_$(HOST_OS):= +LOCAL_CPPFLAGS_$(HOST_OS):= +LOCAL_GENERATED_SOURCES_$(HOST_OS):= +LOCAL_HEADER_LIBRARIES_$(HOST_OS):= +LOCAL_LDFLAGS_$(HOST_OS):= +LOCAL_LDLIBS_$(HOST_OS):= +LOCAL_REQUIRED_MODULES_$(HOST_OS):= +LOCAL_RUNTIME_LIBRARIES_$(HOST_OS):= +LOCAL_SHARED_LIBRARIES_$(HOST_OS):= +LOCAL_SRC_FILES_$(HOST_OS):= +LOCAL_STATIC_LIBRARIES_$(HOST_OS):= + +LOCAL_SRC_FILES_$(HOST_OS)_$(HOST_ARCH):= +ifdef HOST_2ND_ARCH +LOCAL_SRC_FILES_$(HOST_OS)_$(HOST_2ND_ARCH):= +endif + +LOCAL_ASFLAGS_32:= +LOCAL_ASFLAGS_64:= +LOCAL_CFLAGS_32:= +LOCAL_CFLAGS_64:= +LOCAL_C_INCLUDES_32:= +LOCAL_C_INCLUDES_64:= +LOCAL_CLANG_32:= +LOCAL_CLANG_64:= +LOCAL_CLANG_ASFLAGS_32:= +LOCAL_CLANG_ASFLAGS_64:= +LOCAL_CLANG_CFLAGS_32:= +LOCAL_CLANG_CFLAGS_64:= +LOCAL_CLANG_CPPFLAGS_32:= +LOCAL_CLANG_CPPFLAGS_64:= +LOCAL_CLANG_LDFLAGS_32:= +LOCAL_CLANG_LDFLAGS_64:= +LOCAL_CPPFLAGS_32:= +LOCAL_CPPFLAGS_64:= +LOCAL_GENERATED_SOURCES_32:= +LOCAL_GENERATED_SOURCES_64:= +LOCAL_HEADER_LIBRARIES_32:= +LOCAL_HEADER_LIBRARIES_64:= +LOCAL_INIT_RC_32:= +LOCAL_INIT_RC_64:= +LOCAL_LDFLAGS_32:= +LOCAL_LDFLAGS_64:= +LOCAL_MODULE_PATH_32:= +LOCAL_MODULE_PATH_64:= +LOCAL_MODULE_STEM_32:= +LOCAL_MODULE_STEM_64:= +LOCAL_MODULE_SYMLINKS_32:= +LOCAL_MODULE_SYMLINKS_64:= +LOCAL_RUNTIME_LIBRARIES_32:= +LOCAL_RUNTIME_LIBRARIES_64:= +LOCAL_SHARED_LIBRARIES_32:= +LOCAL_SHARED_LIBRARIES_64:= +LOCAL_SRC_FILES_32:= +LOCAL_SRC_FILES_64:= +LOCAL_SRC_FILES_EXCLUDE_32:= +LOCAL_SRC_FILES_EXCLUDE_64:= +LOCAL_STATIC_LIBRARIES_32:= +LOCAL_STATIC_LIBRARIES_64:= +LOCAL_WHOLE_STATIC_LIBRARIES_32:= +LOCAL_WHOLE_STATIC_LIBRARIES_64:= + +# Robolectric variables +LOCAL_INSTRUMENT_SOURCE_DIRS := +LOCAL_ROBOTEST_FAILURE_FATAL := +LOCAL_ROBOTEST_FILES := +LOCAL_ROBOTEST_TIMEOUT := +LOCAL_TEST_PACKAGE := + +full_android_manifest := +non_system_module := + +module_license_metadata := + +# Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to +# iterate over thousands of entries every time. +# Leave the current makefile to make sure we don't break anything +# that expects to be able to find the name of the current makefile. +MAKEFILE_LIST := $(lastword $(MAKEFILE_LIST)) diff --git a/make/core/combo/HOST_darwin-x86_64.mk b/make/core/combo/HOST_darwin-x86_64.mk new file mode 100644 index 0000000..dac3bbf --- /dev/null +++ b/make/core/combo/HOST_darwin-x86_64.mk @@ -0,0 +1,61 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for Darwin (Mac OS X) on x86_64. +# Included by combo/select.mk + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_macho,$(1),$(2)) +endef + +HOST_GLOBAL_ARFLAGS := cqs + +HOST_CUSTOM_LD_COMMAND := true + +define transform-host-o-to-shared-lib-inner +$(hide) $(PRIVATE_CXX) \ + -dynamiclib -single_module -read_only_relocs suppress \ + $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ + ) \ + $(PRIVATE_ALL_OBJECTS) \ + $(addprefix -force_load , $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \ + $(PRIVATE_ALL_SHARED_LIBRARIES) \ + $(PRIVATE_ALL_STATIC_LIBRARIES) \ + $(PRIVATE_LDLIBS) \ + -o $@ \ + -install_name @rpath/$(notdir $@) \ + -Wl,-rpath,@loader_path/../$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES)) \ + -Wl,-rpath,@loader_path/$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES)) \ + $(PRIVATE_LDFLAGS) +endef + +define transform-host-o-to-executable-inner +$(hide) $(PRIVATE_CXX) \ + $(foreach path,$(PRIVATE_RPATHS), \ + -Wl,-rpath,@loader_path/$(path)) \ + -o $@ \ + -Wl,-headerpad_max_install_names \ + $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ + ) \ + $(PRIVATE_ALL_SHARED_LIBRARIES) \ + $(PRIVATE_ALL_OBJECTS) \ + $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ + $(PRIVATE_ALL_STATIC_LIBRARIES) \ + $(PRIVATE_LDFLAGS) \ + $(PRIVATE_LDLIBS) +endef diff --git a/make/core/combo/HOST_linux-x86.mk b/make/core/combo/HOST_linux-x86.mk new file mode 100644 index 0000000..3f4ec0a --- /dev/null +++ b/make/core/combo/HOST_linux-x86.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for builds hosted on linux-x86. +# Included by combo/select.mk + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_elf,$(1),$(2)) +endef + +############################################################ +## Macros after this line are shared by the 64-bit config. diff --git a/make/core/combo/HOST_linux-x86_64.mk b/make/core/combo/HOST_linux-x86_64.mk new file mode 100644 index 0000000..845733f --- /dev/null +++ b/make/core/combo/HOST_linux-x86_64.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for builds hosted on linux-x86_64. +# Included by combo/select.mk + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_elf,$(1),$(2)) +endef diff --git a/make/core/combo/TARGET_linux-arm.mk b/make/core/combo/TARGET_linux-arm.mk new file mode 100644 index 0000000..11c1944 --- /dev/null +++ b/make/core/combo/TARGET_linux-arm.mk @@ -0,0 +1,74 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for Linux on ARM. +# Included by combo/select.mk + +# You can set TARGET_ARCH_VARIANT to use an arch version other +# than ARMv5TE. Each value should correspond to a file named +# $(BUILD_COMBOS)/arch/.mk which must contain +# makefile variable definitions. Their +# purpose is to allow module Android.mk files to selectively compile +# different versions of code based upon the funtionality and +# instructions available in a given architecture version. +# +# The blocks also define specific arch_variant_cflags, which +# include defines, and compiler settings for the given architecture +# version. +# + +KNOWN_ARMv8_CORES := cortex-a53 cortex-a53.a57 cortex-a55 cortex-a73 cortex-a75 cortex-a76 +KNOWN_ARMv8_CORES += kryo kryo385 exynos-m1 exynos-m2 + +KNOWN_ARMv82a_CORES := cortex-a55 cortex-a75 kryo385 + +ifeq (,$(strip $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT))) + TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT := generic +endif + +# This quickly checks TARGET_2ND_ARCH_VARIANT against the lists above. +ifneq (,$(filter $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT), $(KNOWN_ARMv82a_CORES))) + ifeq (,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) + TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := armv8-2a + else ifneq (armv8-2a,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) + $(error Incorrect TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT, $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT). Use armv8-2a instead.) + endif +else ifneq (,$(filter $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT), $(KNOWN_ARMv8_CORES))) + ifeq (,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) + TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := armv8-a + else ifneq ($(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT),armv8-a) + $(error Incorrect TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT, $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT). Use armv8-a instead.) + endif +endif + +ifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)),) + $(error TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT must be set) +endif + +TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_$(combo_2nd_arch_prefix)ARCH)/$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT).mk +ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),) + $(error Unknown ARM architecture version: $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) +endif + +include $(TARGET_ARCH_SPECIFIC_MAKEFILE) + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_elf,$(1),$(2)) +endef + +$(combo_2nd_arch_prefix)TARGET_PACK_MODULE_RELOCATIONS := true + +$(combo_2nd_arch_prefix)TARGET_LINKER := /system/bin/linker diff --git a/make/core/combo/TARGET_linux-arm64.mk b/make/core/combo/TARGET_linux-arm64.mk new file mode 100644 index 0000000..5d481cb --- /dev/null +++ b/make/core/combo/TARGET_linux-arm64.mk @@ -0,0 +1,49 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for Linux on ARM. +# Included by combo/select.mk + +# You can set TARGET_ARCH_VARIANT to use an arch version other +# than ARMv5TE. Each value should correspond to a file named +# $(BUILD_COMBOS)/arch/.mk which must contain +# makefile variable definitions. Their +# purpose is to allow module Android.mk files to selectively compile +# different versions of code based upon the funtionality and +# instructions available in a given architecture version. +# +# The blocks also define specific arch_variant_cflags, which +# include defines, and compiler settings for the given architecture +# version. +# +ifeq ($(strip $(TARGET_ARCH_VARIANT)),) +TARGET_ARCH_VARIANT := armv8 +endif + +TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_ARCH)/$(TARGET_ARCH_VARIANT).mk +ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),) +$(error Unknown ARM architecture version: $(TARGET_ARCH_VARIANT)) +endif + +include $(TARGET_ARCH_SPECIFIC_MAKEFILE) + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_elf,$(1),$(2)) +endef + +TARGET_PACK_MODULE_RELOCATIONS := true + +TARGET_LINKER := /system/bin/linker64 diff --git a/make/core/combo/TARGET_linux-x86.mk b/make/core/combo/TARGET_linux-x86.mk new file mode 100644 index 0000000..acbae51 --- /dev/null +++ b/make/core/combo/TARGET_linux-x86.mk @@ -0,0 +1,44 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for Linux on x86 as a target. +# Included by combo/select.mk + +# Provide a default variant. +ifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)),) +TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := x86 +endif + +# Include the arch-variant-specific configuration file. +# Its role is to define various ARCH_X86_HAVE_XXX feature macros, +# plus initial values for TARGET_GLOBAL_CFLAGS +# +TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_$(combo_2nd_arch_prefix)ARCH)/$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT).mk +ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),) +$(error Unknown $(TARGET_$(combo_2nd_arch_prefix)ARCH) architecture version: $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) +endif + +include $(TARGET_ARCH_SPECIFIC_MAKEFILE) + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_elf,$(1),$(2)) +endef + +$(combo_2nd_arch_prefix)TARGET_PACK_MODULE_RELOCATIONS := true + +$(combo_2nd_arch_prefix)TARGET_LINKER := /system/bin/linker + +$(combo_2nd_arch_prefix)TARGET_GLOBAL_YASM_FLAGS := -f elf32 -m x86 diff --git a/make/core/combo/TARGET_linux-x86_64.mk b/make/core/combo/TARGET_linux-x86_64.mk new file mode 100644 index 0000000..9e7e363 --- /dev/null +++ b/make/core/combo/TARGET_linux-x86_64.mk @@ -0,0 +1,42 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configuration for Linux on x86_64 as a target. +# Included by combo/select.mk + +# Provide a default variant. +ifeq ($(strip $(TARGET_ARCH_VARIANT)),) +TARGET_ARCH_VARIANT := x86_64 +endif + +# Include the arch-variant-specific configuration file. +# Its role is to define various ARCH_X86_HAVE_XXX feature macros, +# plus initial values for TARGET_GLOBAL_CFLAGS +# +TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_ARCH)/$(TARGET_ARCH_VARIANT).mk +ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),) +$(error Unknown $(TARGET_ARCH) architecture version: $(TARGET_ARCH_VARIANT)) +endif + +include $(TARGET_ARCH_SPECIFIC_MAKEFILE) + +define $(combo_var_prefix)transform-shared-lib-to-toc +$(call _gen_toc_command_for_elf,$(1),$(2)) +endef + +TARGET_LINKER := /system/bin/linker64 + +TARGET_GLOBAL_YASM_FLAGS := -f elf64 -m amd64 diff --git a/make/core/combo/arch/arm/armv7-a-neon.mk b/make/core/combo/arch/arm/armv7-a-neon.mk new file mode 100644 index 0000000..0c01ac3 --- /dev/null +++ b/make/core/combo/arch/arm/armv7-a-neon.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on ARM. +# Generating binaries for the ARMv7-a architecture and higher with NEON +# +ARCH_ARM_HAVE_VFP := true +ARCH_ARM_HAVE_VFP_D32 := true +ARCH_ARM_HAVE_NEON := true diff --git a/make/core/combo/arch/arm/armv8-2a.mk b/make/core/combo/arch/arm/armv8-2a.mk new file mode 100644 index 0000000..7e2ca18 --- /dev/null +++ b/make/core/combo/arch/arm/armv8-2a.mk @@ -0,0 +1,8 @@ +# Configuration for Linux on ARM. +# Generating binaries for the ARMv8-2a architecture +# +# Many libraries are not aware of armv8-2a, and AArch32 is (almost) a superset +# of armv7-a-neon. So just let them think we are just like v7. +ARCH_ARM_HAVE_VFP := true +ARCH_ARM_HAVE_VFP_D32 := true +ARCH_ARM_HAVE_NEON := true diff --git a/make/core/combo/arch/arm/armv8-a.mk b/make/core/combo/arch/arm/armv8-a.mk new file mode 100644 index 0000000..19bc014 --- /dev/null +++ b/make/core/combo/arch/arm/armv8-a.mk @@ -0,0 +1,8 @@ +# Configuration for Linux on ARM. +# Generating binaries for the ARMv8-a architecture +# +# Many libraries are not aware of armv8-a, and AArch32 is (almost) a superset +# of armv7-a-neon. So just let them think we are just like v7. +ARCH_ARM_HAVE_VFP := true +ARCH_ARM_HAVE_VFP_D32 := true +ARCH_ARM_HAVE_NEON := true diff --git a/make/core/combo/arch/arm64/armv8-2a-dotprod.mk b/make/core/combo/arch/arm64/armv8-2a-dotprod.mk new file mode 100644 index 0000000..c775cf7 --- /dev/null +++ b/make/core/combo/arch/arm64/armv8-2a-dotprod.mk @@ -0,0 +1,19 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# .mk file required to support build for the new armv8-2a-dotprod Arm64 arch +# variant. The file just needs to be present but does not require to contain +# anything diff --git a/make/core/combo/arch/arm64/armv8-2a.mk b/make/core/combo/arch/arm64/armv8-2a.mk new file mode 100644 index 0000000..e69de29 diff --git a/make/core/combo/arch/arm64/armv8-a-branchprot.mk b/make/core/combo/arch/arm64/armv8-a-branchprot.mk new file mode 100644 index 0000000..77f3535 --- /dev/null +++ b/make/core/combo/arch/arm64/armv8-a-branchprot.mk @@ -0,0 +1,19 @@ +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# .mk file required to support build for the new armv8-a-branchprot Arm64 arch +# variant. The file just needs to be present but does not require to contain +# anything diff --git a/make/core/combo/arch/arm64/armv8-a.mk b/make/core/combo/arch/arm64/armv8-a.mk new file mode 100644 index 0000000..e69de29 diff --git a/make/core/combo/arch/x86/amberlake.mk b/make/core/combo/arch/x86/amberlake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86/amberlake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/atom.mk b/make/core/combo/arch/x86/atom.mk new file mode 100644 index 0000000..bae7946 --- /dev/null +++ b/make/core/combo/arch/x86/atom.mk @@ -0,0 +1,6 @@ +# This file contains feature macro definitions specific to the +# 'x86-atom' arch variant. This is an extension of the 'x86' base variant +# that adds Atom-specific features. +# +# See build/make/core/combo/arch/x86/x86.mk for differences. +# diff --git a/make/core/combo/arch/x86/broadwell.mk b/make/core/combo/arch/x86/broadwell.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86/broadwell.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/haswell.mk b/make/core/combo/arch/x86/haswell.mk new file mode 100644 index 0000000..ffa3bac --- /dev/null +++ b/make/core/combo/arch/x86/haswell.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86. +# Generating binaries for Haswell processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/icelake.mk b/make/core/combo/arch/x86/icelake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86/icelake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/ivybridge.mk b/make/core/combo/arch/x86/ivybridge.mk new file mode 100644 index 0000000..a1358e6 --- /dev/null +++ b/make/core/combo/arch/x86/ivybridge.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86. +# Generating binaries for Ivy Bridge processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/kabylake.mk b/make/core/combo/arch/x86/kabylake.mk new file mode 100644 index 0000000..9906259 --- /dev/null +++ b/make/core/combo/arch/x86/kabylake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors. +# that support AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/sandybridge.mk b/make/core/combo/arch/x86/sandybridge.mk new file mode 100644 index 0000000..d6552ab --- /dev/null +++ b/make/core/combo/arch/x86/sandybridge.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86. +# Generating binaries for SandyBridge processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/silvermont.mk b/make/core/combo/arch/x86/silvermont.mk new file mode 100644 index 0000000..8ac2b98 --- /dev/null +++ b/make/core/combo/arch/x86/silvermont.mk @@ -0,0 +1,7 @@ +# This file contains feature macro definitions specific to the +# silvermont arch variant. +# +# See build/make/core/combo/arch/x86/x86-atom.mk for differences. +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/skylake.mk b/make/core/combo/arch/x86/skylake.mk new file mode 100644 index 0000000..9906259 --- /dev/null +++ b/make/core/combo/arch/x86/skylake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors. +# that support AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/stoneyridge.mk b/make/core/combo/arch/x86/stoneyridge.mk new file mode 100644 index 0000000..05ff77a --- /dev/null +++ b/make/core/combo/arch/x86/stoneyridge.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86. +# Generating binaries for Stoney Ridge processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/tigerlake.mk b/make/core/combo/arch/x86/tigerlake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86/tigerlake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/whiskeylake.mk b/make/core/combo/arch/x86/whiskeylake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86/whiskeylake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86/x86.mk b/make/core/combo/arch/x86/x86.mk new file mode 100644 index 0000000..066f66a --- /dev/null +++ b/make/core/combo/arch/x86/x86.mk @@ -0,0 +1,10 @@ +# This file contains feature macro definitions specific to the +# base 'x86' platform ABI. +# +# It is also used to build full_x86-eng / sdk_x86-eng platform images that +# are run in the emulator under KVM emulation (i.e. running directly on +# the host development machine's CPU). + +# These features are optional and shall not be included in the base platform +# Otherwise, sdk_x86-eng system images might fail to run on some +# developer machines. diff --git a/make/core/combo/arch/x86/x86_64.mk b/make/core/combo/arch/x86/x86_64.mk new file mode 100644 index 0000000..eff406b --- /dev/null +++ b/make/core/combo/arch/x86/x86_64.mk @@ -0,0 +1,7 @@ +# This file is used as the second (32-bit) architecture when building a generic +# x86_64 64-bit platform image. (full_x86_64-eng / sdk_x86_64-eng) +# +# The generic 'x86' variant cannot be used, since it resets some flags used +# by the 'x86_64' variant. + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/amberlake.mk b/make/core/combo/arch/x86_64/amberlake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/amberlake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/broadwell.mk b/make/core/combo/arch/x86_64/broadwell.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/broadwell.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/haswell.mk b/make/core/combo/arch/x86_64/haswell.mk new file mode 100644 index 0000000..faf12fa --- /dev/null +++ b/make/core/combo/arch/x86_64/haswell.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86_64. +# Generating binaries for Haswell processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/icelake.mk b/make/core/combo/arch/x86_64/icelake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/icelake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/ivybridge.mk b/make/core/combo/arch/x86_64/ivybridge.mk new file mode 100644 index 0000000..464fa98 --- /dev/null +++ b/make/core/combo/arch/x86_64/ivybridge.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86_64. +# Generating binaries for Ivy Bridge processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/kabylake.mk b/make/core/combo/arch/x86_64/kabylake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/kabylake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/sandybridge.mk b/make/core/combo/arch/x86_64/sandybridge.mk new file mode 100644 index 0000000..a09db2a --- /dev/null +++ b/make/core/combo/arch/x86_64/sandybridge.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86_64. +# Generating binaries for SandyBridge processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/silvermont.mk b/make/core/combo/arch/x86_64/silvermont.mk new file mode 100644 index 0000000..8ac2b98 --- /dev/null +++ b/make/core/combo/arch/x86_64/silvermont.mk @@ -0,0 +1,7 @@ +# This file contains feature macro definitions specific to the +# silvermont arch variant. +# +# See build/make/core/combo/arch/x86/x86-atom.mk for differences. +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/skylake.mk b/make/core/combo/arch/x86_64/skylake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/skylake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/stoneyridge.mk b/make/core/combo/arch/x86_64/stoneyridge.mk new file mode 100644 index 0000000..5950d9a --- /dev/null +++ b/make/core/combo/arch/x86_64/stoneyridge.mk @@ -0,0 +1,4 @@ +# Configuration for Linux on x86_64. +# Generating binaries for Stoney Ridge processors. +# +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/tigerlake.mk b/make/core/combo/arch/x86_64/tigerlake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/tigerlake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/whiskeylake.mk b/make/core/combo/arch/x86_64/whiskeylake.mk new file mode 100644 index 0000000..a7ae6ed --- /dev/null +++ b/make/core/combo/arch/x86_64/whiskeylake.mk @@ -0,0 +1,6 @@ +# Configuration for Linux on x86. +# Generating binaries for processors +# that have AVX2 feature flag +# + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/arch/x86_64/x86_64.mk b/make/core/combo/arch/x86_64/x86_64.mk new file mode 100755 index 0000000..17413c7 --- /dev/null +++ b/make/core/combo/arch/x86_64/x86_64.mk @@ -0,0 +1,8 @@ +# This file contains feature macro definitions specific to the +# base 'x86_64' platform ABI. +# +# It is also used to build full_x86_64-eng / sdk_x86_64-eng platform images +# that are run in the emulator under KVM emulation (i.e. running directly on +# the host development machine's CPU). + +ARCH_X86_HAVE_SSE4_1 := true diff --git a/make/core/combo/javac.mk b/make/core/combo/javac.mk new file mode 100644 index 0000000..32a5c9e --- /dev/null +++ b/make/core/combo/javac.mk @@ -0,0 +1,20 @@ +# Selects a Java compiler. +# +# Outputs: +# ANDROID_JAVA_TOOLCHAIN -- Directory that contains javac and other java tools +# + +ANDROID_COMPILE_WITH_JACK := false + +ifdef TARGET_BUILD_APPS + ifndef TURBINE_ENABLED + TURBINE_ENABLED := false + endif +endif + +ANDROID_JAVA_TOOLCHAIN := $(ANDROID_JAVA_HOME)/bin + +# TODO(ccross): remove this, it is needed for now because it is used by +# config.mk before makevars from soong are loaded +JAVA := $(ANDROID_JAVA_TOOLCHAIN)/java -XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads + diff --git a/make/core/combo/select.mk b/make/core/combo/select.mk new file mode 100644 index 0000000..9c7e69e --- /dev/null +++ b/make/core/combo/select.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2006 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Select a combo based on the compiler being used. +# +# Inputs: +# combo_target -- prefix for final variables (HOST_ or TARGET_) +# combo_2nd_arch_prefix -- it's defined if this is loaded for the 2nd arch. +# + +# Build a target string like "linux-arm" or "darwin-x86". +combo_os_arch := $($(combo_target)OS)-$($(combo_target)$(combo_2nd_arch_prefix)ARCH) + +combo_var_prefix := $(combo_2nd_arch_prefix)$(combo_target) + +# Set reasonable defaults for the various variables +ifeq ($(combo_target),HOST_CROSS_) +$(KATI_obsolete_var \ + $(combo_var_prefix)GLOBAL_ARFLAGS \ + $(combo_var_prefix)STATIC_LIB_SUFFIX \ + $(combo_var_prefix)transform-shared-lib-to-toc \ + ,HOST_CROSS builds are not supported in Make) +else + +$(combo_var_prefix)GLOBAL_ARFLAGS := crsPD --format=gnu + +$(combo_var_prefix)STATIC_LIB_SUFFIX := .a + +# Now include the combo for this specific target. +include $(BUILD_COMBOS)/$(combo_target)$(combo_os_arch).mk + +endif diff --git a/make/core/config.mk b/make/core/config.mk new file mode 100644 index 0000000..7f0e98e --- /dev/null +++ b/make/core/config.mk @@ -0,0 +1,1272 @@ +# This is included by the top-level Makefile. +# It sets up standard variables based on the +# current configuration and platform, which +# are not specific to what is being built. + +ifndef KATI +$(warning Directly using config.mk from make is no longer supported.) +$(warning ) +$(warning If you are just attempting to build, you probably need to re-source envsetup.sh:) +$(warning ) +$(warning $$ source build/envsetup.sh) +$(warning ) +$(warning If you are attempting to emulate get_build_var, use one of the following:) +$(warning $$ build/soong/soong_ui.bash --dumpvar-mode) +$(warning $$ build/soong/soong_ui.bash --dumpvars-mode) +$(warning ) +$(error done) +endif + +BUILD_SYSTEM :=$= build/make/core +BUILD_SYSTEM_COMMON :=$= build/make/common + +include $(BUILD_SYSTEM_COMMON)/core.mk + +# ----------------------------------------------------------------- +# Rules and functions to help copy important files to DIST_DIR +# when requested. This must be included once only, and must be included before +# soong_config (as soong_config calls make_vars-$(TARGET).mk, and soong may +# propagate calls to dist-for-goals there). +include $(BUILD_SYSTEM)/distdir.mk + +# Mark variables that should be coming as environment variables from soong_ui +# as readonly +.KATI_READONLY := OUT_DIR TMPDIR BUILD_DATETIME_FILE +ifdef CALLED_FROM_SETUP + .KATI_READONLY := CALLED_FROM_SETUP +endif +ifdef KATI_PACKAGE_MK_DIR + .KATI_READONLY := KATI_PACKAGE_MK_DIR +endif + +# Mark variables deprecated/obsolete +CHANGES_URL := https://android.googlesource.com/platform/build/+/master/Changes.md +.KATI_READONLY := CHANGES_URL +$(KATI_obsolete_var PATH,Do not use PATH directly. See $(CHANGES_URL)#PATH) +$(KATI_obsolete_var PYTHONPATH,Do not use PYTHONPATH directly. See $(CHANGES_URL)#PYTHONPATH) +$(KATI_obsolete_var OUT,Use OUT_DIR instead. See $(CHANGES_URL)#OUT) +$(KATI_obsolete_var ANDROID_HOST_OUT,Use HOST_OUT instead. See $(CHANGES_URL)#ANDROID_HOST_OUT) +$(KATI_obsolete_var ANDROID_PRODUCT_OUT,Use PRODUCT_OUT instead. See $(CHANGES_URL)#ANDROID_PRODUCT_OUT) +$(KATI_obsolete_var ANDROID_HOST_OUT_TESTCASES,Use HOST_OUT_TESTCASES instead. See $(CHANGES_URL)#ANDROID_HOST_OUT_TESTCASES) +$(KATI_obsolete_var ANDROID_TARGET_OUT_TESTCASES,Use TARGET_OUT_TESTCASES instead. See $(CHANGES_URL)#ANDROID_TARGET_OUT_TESTCASES) +$(KATI_obsolete_var ANDROID_BUILD_TOP,Use '.' instead. See $(CHANGES_URL)#ANDROID_BUILD_TOP) +$(KATI_obsolete_var \ + ANDROID_TOOLCHAIN \ + ANDROID_TOOLCHAIN_2ND_ARCH \ + ANDROID_DEV_SCRIPTS \ + ANDROID_EMULATOR_PREBUILTS \ + ANDROID_PRE_BUILD_PATHS \ + ,See $(CHANGES_URL)#other_envsetup_variables) +$(KATI_obsolete_var PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE,Set FCM Version in device manifest instead. See $(CHANGES_URL)#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE) +$(KATI_obsolete_var USE_CLANG_PLATFORM_BUILD,Clang is the only supported Android compiler. See $(CHANGES_URL)#USE_CLANG_PLATFORM_BUILD) +$(KATI_obsolete_var BUILD_DROIDDOC,Droiddoc is only supported in Soong. See details on build/soong/java/droiddoc.go) +$(KATI_obsolete_var BUILD_APIDIFF,Apidiff is only supported in Soong. See details on build/soong/java/droiddoc.go) +$(KATI_obsolete_var \ + DEFAULT_GCC_CPP_STD_VERSION \ + HOST_GLOBAL_CFLAGS 2ND_HOST_GLOBAL_CFLAGS \ + HOST_GLOBAL_CONLYFLAGS 2ND_HOST_GLOBAL_CONLYFLAGS \ + HOST_GLOBAL_CPPFLAGS 2ND_HOST_GLOBAL_CPPFLAGS \ + HOST_GLOBAL_LDFLAGS 2ND_HOST_GLOBAL_LDFLAGS \ + HOST_GLOBAL_LLDFLAGS 2ND_HOST_GLOBAL_LLDFLAGS \ + HOST_CLANG_SUPPORTED 2ND_HOST_CLANG_SUPPORTED \ + HOST_CC 2ND_HOST_CC \ + HOST_CXX 2ND_HOST_CXX \ + HOST_CROSS_GLOBAL_CFLAGS 2ND_HOST_CROSS_GLOBAL_CFLAGS \ + HOST_CROSS_GLOBAL_CONLYFLAGS 2ND_HOST_CROSS_GLOBAL_CONLYFLAGS \ + HOST_CROSS_GLOBAL_CPPFLAGS 2ND_HOST_CROSS_GLOBAL_CPPFLAGS \ + HOST_CROSS_GLOBAL_LDFLAGS 2ND_HOST_CROSS_GLOBAL_LDFLAGS \ + HOST_CROSS_GLOBAL_LLDFLAGS 2ND_HOST_CROSS_GLOBAL_LLDFLAGS \ + HOST_CROSS_CLANG_SUPPORTED 2ND_HOST_CROSS_CLANG_SUPPORTED \ + HOST_CROSS_CC 2ND_HOST_CROSS_CC \ + HOST_CROSS_CXX 2ND_HOST_CROSS_CXX \ + TARGET_GLOBAL_CFLAGS 2ND_TARGET_GLOBAL_CFLAGS \ + TARGET_GLOBAL_CONLYFLAGS 2ND_TARGET_GLOBAL_CONLYFLAGS \ + TARGET_GLOBAL_CPPFLAGS 2ND_TARGET_GLOBAL_CPPFLAGS \ + TARGET_GLOBAL_LDFLAGS 2ND_TARGET_GLOBAL_LDFLAGS \ + TARGET_GLOBAL_LLDFLAGS 2ND_TARGET_GLOBAL_LLDFLAGS \ + TARGET_CLANG_SUPPORTED 2ND_TARGET_CLANG_SUPPORTED \ + TARGET_CC 2ND_TARGET_CC \ + TARGET_CXX 2ND_TARGET_CXX \ + TARGET_TOOLCHAIN_ROOT 2ND_TARGET_TOOLCHAIN_ROOT \ + HOST_TOOLCHAIN_ROOT 2ND_HOST_TOOLCHAIN_ROOT \ + HOST_CROSS_TOOLCHAIN_ROOT 2ND_HOST_CROSS_TOOLCHAIN_ROOT \ + HOST_TOOLS_PREFIX 2ND_HOST_TOOLS_PREFIX \ + HOST_CROSS_TOOLS_PREFIX 2ND_HOST_CROSS_TOOLS_PREFIX \ + HOST_GCC_VERSION 2ND_HOST_GCC_VERSION \ + HOST_CROSS_GCC_VERSION 2ND_HOST_CROSS_GCC_VERSION \ + TARGET_NDK_GCC_VERSION 2ND_TARGET_NDK_GCC_VERSION \ + GLOBAL_CFLAGS_NO_OVERRIDE GLOBAL_CPPFLAGS_NO_OVERRIDE \ + ,GCC support has been removed. Use Clang instead) +$(KATI_obsolete_var DIST_DIR dist_goal,Use dist-for-goals instead. See $(CHANGES_URL)#dist) +$(KATI_obsolete_var TARGET_ANDROID_FILESYSTEM_CONFIG_H,Use TARGET_FS_CONFIG_GEN instead) +$(KATI_deprecated_var USER,Use BUILD_USERNAME instead. See $(CHANGES_URL)#USER) +$(KATI_obsolete_var TARGET_ROOT_OUT_SBIN,/sbin has been removed, use /system/bin instead) +$(KATI_obsolete_var TARGET_ROOT_OUT_SBIN_UNSTRIPPED,/sbin has been removed, use /system/bin instead) +$(KATI_obsolete_var BUILD_BROKEN_PHONY_TARGETS) +$(KATI_obsolete_var BUILD_BROKEN_DUP_COPY_HEADERS) +$(KATI_obsolete_var BUILD_BROKEN_ENG_DEBUG_TAGS) +$(KATI_obsolete_export It is a global setting. See $(CHANGES_URL)#export_keyword) +$(KATI_obsolete_var BUILD_BROKEN_ANDROIDMK_EXPORTS) +$(KATI_obsolete_var PRODUCT_STATIC_BOOT_CONTROL_HAL,Use shared library module instead. See $(CHANGES_URL)#PRODUCT_STATIC_BOOT_CONTROL_HAL) +$(KATI_obsolete_var \ + ARCH_ARM_HAVE_ARMV7A \ + ARCH_DSP_REV \ + ARCH_HAVE_ALIGNED_DOUBLES \ + ARCH_MIPS_HAS_DSP \ + ARCH_MIPS_HAS_FPU \ + ARCH_MIPS_REV6 \ + ARCH_X86_HAVE_AES_NI \ + ARCH_X86_HAVE_AVX \ + ARCH_X86_HAVE_AVX2 \ + ARCH_X86_HAVE_AVX512 \ + ARCH_X86_HAVE_MOVBE \ + ARCH_X86_HAVE_POPCNT \ + ARCH_X86_HAVE_SSE4 \ + ARCH_X86_HAVE_SSE4_2 \ + ARCH_X86_HAVE_SSSE3 \ +) +$(KATI_obsolete_var PRODUCT_IOT) +$(KATI_obsolete_var MD5SUM) +$(KATI_obsolete_var BOARD_HAL_STATIC_LIBRARIES, See $(CHANGES_URL)#BOARD_HAL_STATIC_LIBRARIES) +$(KATI_obsolete_var LOCAL_HAL_STATIC_LIBRARIES, See $(CHANGES_URL)#BOARD_HAL_STATIC_LIBRARIES) +$(KATI_obsolete_var \ + TARGET_AUX_OS_VARIANT_LIST \ + LOCAL_AUX_ARCH \ + LOCAL_AUX_CPU \ + LOCAL_AUX_OS \ + LOCAL_AUX_OS_VARIANT \ + LOCAL_AUX_SUBARCH \ + LOCAL_AUX_TOOLCHAIN \ + LOCAL_CUSTOM_BUILD_STEP_INPUT \ + LOCAL_CUSTOM_BUILD_STEP_OUTPUT \ + LOCAL_IS_AUX_MODULE \ + ,AUX support has been removed) +$(KATI_obsolete_var HOST_OUT_TEST_CONFIG TARGET_OUT_TEST_CONFIG LOCAL_TEST_CONFIG_OPTIONS) +$(KATI_obsolete_var \ + TARGET_PROJECT_INCLUDES \ + 2ND_TARGET_PROJECT_INCLUDES \ + TARGET_PROJECT_SYSTEM_INCLUDES \ + 2ND_TARGET_PROJECT_SYSTEM_INCLUDES \ + ,Project include variables have been removed) +$(KATI_obsolete_var TARGET_PREFER_32_BIT TARGET_PREFER_32_BIT_APPS TARGET_PREFER_32_BIT_EXECUTABLES) +$(KATI_obsolete_var PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST) +$(KATI_obsolete_var PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST) +$(KATI_obsolete_var COVERAGE_PATHS,Use NATIVE_COVERAGE_PATHS instead) +$(KATI_obsolete_var COVERAGE_EXCLUDE_PATHS,Use NATIVE_COVERAGE_EXCLUDE_PATHS instead) +$(KATI_obsolete_var BOARD_VNDK_RUNTIME_DISABLE,VNDK-Lite is no longer supported) +$(KATI_obsolete_var LOCAL_SANITIZE_BLACKLIST,Use LOCAL_SANITIZE_BLOCKLIST instead) +$(KATI_deprecated_var BOARD_PLAT_PUBLIC_SEPOLICY_DIR,Use SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS instead) +$(KATI_deprecated_var BOARD_PLAT_PRIVATE_SEPOLICY_DIR,Use SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS instead) +$(KATI_obsolete_var TARGET_NO_VENDOR_BOOT,Use PRODUCT_BUILD_VENDOR_BOOT_IMAGE instead) +$(KATI_obsolete_var PRODUCT_CHECK_ELF_FILES,Use BUILD_BROKEN_PREBUILT_ELF_FILES instead) +$(KATI_obsolete_var ALL_GENERATED_SOURCES,ALL_GENERATED_SOURCES is no longer used) +$(KATI_obsolete_var ALL_ORIGINAL_DYNAMIC_BINARIES,ALL_ORIGINAL_DYNAMIC_BINARIES is no longer used) + +# Used to force goals to build. Only use for conditionally defined goals. +.PHONY: FORCE +FORCE: + +ORIGINAL_MAKECMDGOALS := $(MAKECMDGOALS) + +UNAME := $(shell uname -sm) + +SRC_TARGET_DIR := $(TOPDIR)build/make/target + +# Some specific paths to tools +SRC_DROIDDOC_DIR := $(TOPDIR)build/make/tools/droiddoc + +# Mark some inputs as readonly +ifdef TARGET_DEVICE_DIR + .KATI_READONLY := TARGET_DEVICE_DIR +endif + +ONE_SHOT_MAKEFILE := +.KATI_READONLY := ONE_SHOT_MAKEFILE + +# Set up efficient math functions which are used in make. +# Here since this file is included by envsetup as well as during build. +include $(BUILD_SYSTEM_COMMON)/math.mk + +include $(BUILD_SYSTEM_COMMON)/strings.mk + +include $(BUILD_SYSTEM_COMMON)/json.mk + +# Various mappings to avoid hard-coding paths all over the place +include $(BUILD_SYSTEM)/pathmap.mk + +# Allow projects to define their own globally-available variables +include $(BUILD_SYSTEM)/project_definitions.mk + +# ############################################################### +# Build system internal files +# ############################################################### + +BUILD_COMBOS :=$= $(BUILD_SYSTEM)/combo + +CLEAR_VARS :=$= $(BUILD_SYSTEM)/clear_vars.mk + +BUILD_HOST_STATIC_LIBRARY :=$= $(BUILD_SYSTEM)/host_static_library.mk +BUILD_HOST_SHARED_LIBRARY :=$= $(BUILD_SYSTEM)/host_shared_library.mk +BUILD_STATIC_LIBRARY :=$= $(BUILD_SYSTEM)/static_library.mk +BUILD_HEADER_LIBRARY :=$= $(BUILD_SYSTEM)/header_library.mk +BUILD_SHARED_LIBRARY :=$= $(BUILD_SYSTEM)/shared_library.mk +BUILD_EXECUTABLE :=$= $(BUILD_SYSTEM)/executable.mk +BUILD_HOST_EXECUTABLE :=$= $(BUILD_SYSTEM)/host_executable.mk +BUILD_PACKAGE :=$= $(BUILD_SYSTEM)/package.mk +BUILD_PHONY_PACKAGE :=$= $(BUILD_SYSTEM)/phony_package.mk +BUILD_RRO_PACKAGE :=$= $(BUILD_SYSTEM)/build_rro_package.mk +BUILD_HOST_PREBUILT :=$= $(BUILD_SYSTEM)/host_prebuilt.mk +BUILD_PREBUILT :=$= $(BUILD_SYSTEM)/prebuilt.mk +BUILD_MULTI_PREBUILT :=$= $(BUILD_SYSTEM)/multi_prebuilt.mk +BUILD_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/java_library.mk +BUILD_STATIC_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/static_java_library.mk +BUILD_HOST_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/host_java_library.mk +BUILD_COPY_HEADERS :=$= $(BUILD_SYSTEM)/copy_headers.mk +BUILD_NATIVE_TEST :=$= $(BUILD_SYSTEM)/native_test.mk +BUILD_FUZZ_TEST :=$= $(BUILD_SYSTEM)/fuzz_test.mk + +BUILD_NOTICE_FILE :=$= $(BUILD_SYSTEM)/notice_files.mk +BUILD_HOST_DALVIK_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/host_dalvik_java_library.mk +BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/host_dalvik_static_java_library.mk + +include $(BUILD_SYSTEM)/deprecation.mk + +# ############################################################### +# Parse out any modifier targets. +# ############################################################### + +hide := @ + +################################################################ +# Tools needed in product configuration makefiles. +################################################################ +NORMALIZE_PATH := build/make/tools/normalize_path.py + +# $(1): the paths to be normalized +define normalize-paths +$(if $(1),$(shell $(NORMALIZE_PATH) $(1))) +endef + +# ############################################################### +# Set common values +# ############################################################### + +# Initialize SOONG_CONFIG_NAMESPACES so that it isn't recursive. +SOONG_CONFIG_NAMESPACES := + +# TODO(asmundak): remove add_soong_config_namespace, add_soong_config_var, +# and add_soong_config_var_value once all their usages are replaced with +# soong_config_set/soong_config_append. + +# The add_soong_config_namespace function adds a namespace and initializes it +# to be empty. +# $1 is the namespace. +# Ex: $(call add_soong_config_namespace,acme) + +define add_soong_config_namespace +$(eval SOONG_CONFIG_NAMESPACES += $1) \ +$(eval SOONG_CONFIG_$(strip $1) :=) +endef + +# The add_soong_config_var function adds a a list of soong config variables to +# SOONG_CONFIG_*. The variables and their values are then available to a +# soong_config_module_type in an Android.bp file. +# $1 is the namespace. $2 is the list of variables. +# Ex: $(call add_soong_config_var,acme,COOL_FEATURE_A COOL_FEATURE_B) +define add_soong_config_var +$(eval SOONG_CONFIG_$(strip $1) += $2) \ +$(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $($v))) +endef + +# The add_soong_config_var_value function defines a make variable and also adds +# the variable to SOONG_CONFIG_*. +# $1 is the namespace. $2 is the variable name. $3 is the variable value. +# Ex: $(call add_soong_config_var_value,acme,COOL_FEATURE,true) + +define add_soong_config_var_value +$(eval $2 := $3) \ +$(call add_soong_config_var,$1,$2) +endef + +# Soong config namespace variables manipulation. +# +# internal utility to define a namespace and a variable in it. +define soong_config_define_internal +$(if $(filter $1,$(SOONG_CONFIG_NAMESPACES)),,$(eval SOONG_CONFIG_NAMESPACES:=$(SOONG_CONFIG_NAMESPACES) $1)) \ +$(if $(filter $2,$(SOONG_CONFIG_$(strip $1))),,$(eval SOONG_CONFIG_$(strip $1):=$(SOONG_CONFIG_$(strip $1)) $2)) +endef + +# soong_config_set defines the variable in the given Soong config namespace +# and sets its value. If the namespace does not exist, it will be defined. +# $1 is the namespace. $2 is the variable name. $3 is the variable value. +# Ex: $(call soong_config_set,acme,COOL_FEATURE,true) +define soong_config_set +$(call soong_config_define_internal,$1,$2) \ +$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$3) +endef + +# soong_config_append appends to the value of the variable in the given Soong +# config namespace. If the variable does not exist, it will be defined. If the +# namespace does not exist, it will be defined. +# $1 is the namespace, $2 is the variable name, $3 is the value +define soong_config_append +$(call soong_config_define_internal,$1,$2) \ +$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(SOONG_CONFIG_$(strip $1)_$(strip $2)) $3) +endef + +# soong_config_append gets to the value of the variable in the given Soong +# config namespace. If the namespace or variables does not exist, an +# empty string will be returned. +# $1 is the namespace, $2 is the variable name +define soong_config_get +$(SOONG_CONFIG_$(strip $1)_$(strip $2)) +endef + +# Set the extensions used for various packages +COMMON_PACKAGE_SUFFIX := .zip +COMMON_JAVA_PACKAGE_SUFFIX := .jar +COMMON_ANDROID_PACKAGE_SUFFIX := .apk + +ifdef TMPDIR +JAVA_TMPDIR_ARG := -Djava.io.tmpdir=$(TMPDIR) +else +JAVA_TMPDIR_ARG := +endif + +# ############################################################### +# Include sub-configuration files +# ############################################################### + +# --------------------------------------------------------------- +# Try to include buildspec.mk, which will try to set stuff up. +# If this file doesn't exist, the environment variables will +# be used, and if that doesn't work, then the default is an +# arm build +ifndef ANDROID_BUILDSPEC +ANDROID_BUILDSPEC := $(TOPDIR)buildspec.mk +endif +-include $(ANDROID_BUILDSPEC) + +# --------------------------------------------------------------- +# Define most of the global variables. These are the ones that +# are specific to the user's build configuration. +include $(BUILD_SYSTEM)/envsetup.mk + +# Pruned directory options used when using findleaves.py +# See envsetup.mk for a description of SCAN_EXCLUDE_DIRS +FIND_LEAVES_EXCLUDES := $(addprefix --prune=, $(SCAN_EXCLUDE_DIRS) .repo .git) + +# The build system exposes several variables for where to find the kernel +# headers: +# TARGET_DEVICE_KERNEL_HEADERS is automatically created for the current +# device being built. It is set as $(TARGET_DEVICE_DIR)/kernel-headers, +# e.g. device/samsung/tuna/kernel-headers. This directory is not +# explicitly set by anyone, the build system always adds this subdir. +# +# TARGET_BOARD_KERNEL_HEADERS is specified by the BoardConfig.mk file +# to allow other directories to be included. This is useful if there's +# some common place where a few headers are being kept for a group +# of devices. For example, device//common/kernel-headers could +# contain some headers for several of 's devices. +# +# TARGET_PRODUCT_KERNEL_HEADERS is generated by the product inheritance +# graph. This allows architecture products to provide headers for the +# devices using that architecture. For example, +# hardware/ti/omap4xxx/omap4.mk will specify +# PRODUCT_VENDOR_KERNEL_HEADERS variable that specify where the omap4 +# specific headers are, e.g. hardware/ti/omap4xxx/kernel-headers. +# The build system then combines all the values specified by all the +# PRODUCT_VENDOR_KERNEL_HEADERS directives in the product inheritance +# tree and then exports a TARGET_PRODUCT_KERNEL_HEADERS variable. +# +# The layout of subdirs in any of the kernel-headers dir should mirror the +# layout of the kernel include/ directory. For example, +# device/samsung/tuna/kernel-headers/linux/, +# hardware/ti/omap4xxx/kernel-headers/media/, +# etc. +# +# NOTE: These directories MUST contain post-processed headers using the +# bionic/libc/kernel/tools/clean_header.py tool. Additionally, the original +# kernel headers must also be checked in, but in a different subdirectory. By +# convention, the originals should be checked into original-kernel-headers +# directory of the same parent dir. For example, +# device/samsung/tuna/kernel-headers <----- post-processed +# device/samsung/tuna/original-kernel-headers <----- originals +# +TARGET_DEVICE_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_DEVICE_DIR)/kernel-headers)) + +define validate-kernel-headers +$(if $(firstword $(foreach hdr_dir,$(1),\ + $(filter-out kernel-headers,$(notdir $(hdr_dir))))),\ + $(error Kernel header dirs must be end in kernel-headers: $(1))) +endef +# also allow the board config to provide additional directories since +# there could be device/oem/base_hw and device/oem/derived_hw +# that both are valid devices but derived_hw needs to use kernel headers +# from base_hw. +TARGET_BOARD_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_BOARD_KERNEL_HEADERS))) +TARGET_BOARD_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_BOARD_KERNEL_HEADERS)) +$(call validate-kernel-headers,$(TARGET_BOARD_KERNEL_HEADERS)) + +# then add product-inherited includes, to allow for +# hardware/sivendor/chip/chip.mk to include their own headers +TARGET_PRODUCT_KERNEL_HEADERS := $(strip $(wildcard $(PRODUCT_VENDOR_KERNEL_HEADERS))) +TARGET_PRODUCT_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_PRODUCT_KERNEL_HEADERS)) +$(call validate-kernel-headers,$(TARGET_PRODUCT_KERNEL_HEADERS)) +.KATI_READONLY := TARGET_DEVICE_KERNEL_HEADERS TARGET_BOARD_KERNEL_HEADERS TARGET_PRODUCT_KERNEL_HEADERS + +# Commands to generate .toc file common to ELF .so files. +define _gen_toc_command_for_elf +$(hide) ($($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)READELF) -d $(1) | grep SONAME || echo "No SONAME for $1") > $(2) +$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)READELF) --dyn-syms $(1) | awk '{$$2=""; $$3=""; print}' >> $(2) +endef + +# Commands to generate .toc file from Darwin dynamic library. +define _gen_toc_command_for_macho +$(hide) $(HOST_OTOOL) -l $(1) | grep LC_ID_DYLIB -A 5 > $(2) +$(hide) $(HOST_NM) -gP $(1) | cut -f1-2 -d" " | (grep -v U$$ >> $(2) || true) +endef + +ifeq ($(CALLED_FROM_SETUP),true) +include $(BUILD_SYSTEM)/ccache.mk +include $(BUILD_SYSTEM)/goma.mk +include $(BUILD_SYSTEM)/rbe.mk +endif + +# GCC version selection +TARGET_GCC_VERSION := 4.9 +ifdef TARGET_2ND_ARCH +2ND_TARGET_GCC_VERSION := 4.9 +endif + +# Normalize WITH_STATIC_ANALYZER +ifeq ($(strip $(WITH_STATIC_ANALYZER)),0) + WITH_STATIC_ANALYZER := +endif + +# Unset WITH_TIDY_ONLY if global WITH_TIDY_ONLY is not true nor 1. +ifeq (,$(filter 1 true,$(WITH_TIDY_ONLY))) + WITH_TIDY_ONLY := +endif + +# Pick a Java compiler. +include $(BUILD_SYSTEM)/combo/javac.mk + +# --------------------------------------------------------------- +# Check that the configuration is current. We check that +# BUILD_ENV_SEQUENCE_NUMBER is current against this value. +# Don't fail if we're called from envsetup, so they have a +# chance to update their environment. + +ifeq (,$(strip $(CALLED_FROM_SETUP))) +ifneq (,$(strip $(BUILD_ENV_SEQUENCE_NUMBER))) +ifneq ($(BUILD_ENV_SEQUENCE_NUMBER),$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER)) +$(warning BUILD_ENV_SEQUENCE_NUMBER is set incorrectly.) +$(info *** If you use envsetup/lunch/choosecombo:) +$(info *** - Re-execute envsetup (". envsetup.sh")) +$(info *** - Re-run lunch or choosecombo) +$(info *** If you use buildspec.mk:) +$(info *** - Look at buildspec.mk.default to see what has changed) +$(info *** - Update BUILD_ENV_SEQUENCE_NUMBER to "$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER)") +$(error bailing..) +endif +endif +endif + +# --------------------------------------------------------------- +# Whether we can expect a full build graph +ALLOW_MISSING_DEPENDENCIES := $(filter true,$(ALLOW_MISSING_DEPENDENCIES)) +ifneq ($(TARGET_BUILD_APPS),) +ALLOW_MISSING_DEPENDENCIES := true +endif +ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true) +ALLOW_MISSING_DEPENDENCIES := true +endif +ifneq ($(filter true,$(SOONG_ALLOW_MISSING_DEPENDENCIES)),) +ALLOW_MISSING_DEPENDENCIES := true +endif +# Mac builds default to ALLOW_MISSING_DEPENDENCIES, at least until the host +# tools aren't enabled by default for Mac. +ifeq ($(HOST_OS),darwin) + ALLOW_MISSING_DEPENDENCIES := true +endif +.KATI_READONLY := ALLOW_MISSING_DEPENDENCIES + +TARGET_BUILD_USE_PREBUILT_SDKS := +DISABLE_PREOPT := +ifneq (,$(TARGET_BUILD_APPS)$(TARGET_BUILD_UNBUNDLED_IMAGE)) + DISABLE_PREOPT := true +endif +ifeq (true,$(TARGET_BUILD_UNBUNDLED)) + ifneq (true,$(UNBUNDLED_BUILD_SDKS_FROM_SOURCE)) + TARGET_BUILD_USE_PREBUILT_SDKS := true + endif +endif + +.KATI_READONLY := \ + TARGET_BUILD_USE_PREBUILT_SDKS \ + DISABLE_PREOPT \ + +prebuilt_sdk_tools := prebuilts/sdk/tools +prebuilt_sdk_tools_bin := $(prebuilt_sdk_tools)/$(HOST_OS)/bin + +prebuilt_build_tools := prebuilts/build-tools +prebuilt_build_tools_wrappers := prebuilts/build-tools/common/bin +prebuilt_build_tools_jars := prebuilts/build-tools/common/framework +prebuilt_build_tools_bin_noasan := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/bin +ifeq ($(filter address,$(SANITIZE_HOST)),) +prebuilt_build_tools_bin := $(prebuilt_build_tools_bin_noasan) +else +prebuilt_build_tools_bin := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/asan/bin +endif + +USE_PREBUILT_SDK_TOOLS_IN_PLACE := true + +# Work around for b/68406220 +# This should match the soong version. +USE_D8 := true +.KATI_READONLY := USE_D8 + +# +# Tools that are prebuilts for TARGET_BUILD_USE_PREBUILT_SDKS +# +ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)) + AAPT := $(HOST_OUT_EXECUTABLES)/aapt +else # TARGET_BUILD_USE_PREBUILT_SDKS + AAPT := $(prebuilt_sdk_tools_bin)/aapt +endif # TARGET_BUILD_USE_PREBUILT_SDKS + +ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)) + # Use RenderScript prebuilts for unbundled builds + LLVM_RS_CC := $(HOST_OUT_EXECUTABLES)/llvm-rs-cc + BCC_COMPAT := $(HOST_OUT_EXECUTABLES)/bcc_compat +else + LLVM_RS_CC := $(prebuilt_sdk_tools_bin)/llvm-rs-cc + BCC_COMPAT := $(prebuilt_sdk_tools_bin)/bcc_compat +endif + +prebuilt_sdk_tools := +prebuilt_sdk_tools_bin := + +ACP := $(prebuilt_build_tools_bin)/acp +CKATI := $(prebuilt_build_tools_bin)/ckati +DEPMOD := $(HOST_OUT_EXECUTABLES)/depmod +FILESLIST := $(HOST_OUT_EXECUTABLES)/fileslist +FILESLIST_UTIL :=$= build/make/tools/fileslist_util.py +HOST_INIT_VERIFIER := $(HOST_OUT_EXECUTABLES)/host_init_verifier +XMLLINT := $(HOST_OUT_EXECUTABLES)/xmllint + +# SOONG_ZIP is exported by Soong, but needs to be defined early for +# $OUT/dexpreopt.global. It will be verified against the Soong version. +SOONG_ZIP := $(HOST_OUT_EXECUTABLES)/soong_zip + +# --------------------------------------------------------------- +# Generic tools. + +# These dependencies are now handled via dependencies on prebuilt_build_tool +BISON_DATA :=$= + +YASM := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/yasm/yasm + +DOXYGEN:= doxygen +ifeq ($(HOST_OS),linux) +BREAKPAD_DUMP_SYMS := $(HOST_OUT_EXECUTABLES)/dump_syms +else +# For non-supported hosts, do not generate breakpad symbols. +BREAKPAD_GENERATE_SYMBOLS := false +endif +PROTOC := $(HOST_OUT_EXECUTABLES)/aprotoc$(HOST_EXECUTABLE_SUFFIX) +NANOPB_SRCS := $(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb +VTSC := $(HOST_OUT_EXECUTABLES)/vtsc$(HOST_EXECUTABLE_SUFFIX) +MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX) +MINIGZIP := $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX) +LZ4 := $(HOST_OUT_EXECUTABLES)/lz4$(HOST_EXECUTABLE_SUFFIX) +GENERATE_GKI_CERTIFICATE := $(HOST_OUT_EXECUTABLES)/generate_gki_certificate$(HOST_EXECUTABLE_SUFFIX) +ifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG))) +MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX) +else +MKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG) +endif +ifeq (,$(strip $(BOARD_CUSTOM_BPTTOOL))) +BPTTOOL := $(HOST_OUT_EXECUTABLES)/bpttool$(HOST_EXECUTABLE_SUFFIX) +else +BPTTOOL := $(BOARD_CUSTOM_BPTTOOL) +endif +ifeq (,$(strip $(BOARD_CUSTOM_AVBTOOL))) +AVBTOOL := $(HOST_OUT_EXECUTABLES)/avbtool$(HOST_EXECUTABLE_SUFFIX) +else +AVBTOOL := $(BOARD_CUSTOM_AVBTOOL) +endif +APICHECK := $(HOST_OUT_JAVA_LIBRARIES)/metalava$(COMMON_JAVA_PACKAGE_SUFFIX) +FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX) +MKEXTUSERIMG := $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs +MKE2FS_CONF := system/extras/ext4_utils/mke2fs.conf +MKEROFS := $(HOST_OUT_EXECUTABLES)/mkfs.erofs +MKSQUASHFSUSERIMG := $(HOST_OUT_EXECUTABLES)/mksquashfsimage.sh +MKF2FSUSERIMG := $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh +SIMG2IMG := $(HOST_OUT_EXECUTABLES)/simg2img$(HOST_EXECUTABLE_SUFFIX) +E2FSCK := $(HOST_OUT_EXECUTABLES)/e2fsck$(HOST_EXECUTABLE_SUFFIX) +TUNE2FS := $(HOST_OUT_EXECUTABLES)/tune2fs$(HOST_EXECUTABLE_SUFFIX) +JARJAR := $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar +DATA_BINDING_COMPILER := $(HOST_OUT_JAVA_LIBRARIES)/databinding-compiler.jar +FAT16COPY := build/make/tools/fat16copy.py +CHECK_ELF_FILE := build/make/tools/check_elf_file.py +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) +BUILD_SUPER_IMAGE := $(HOST_OUT_EXECUTABLES)/build_super_image$(HOST_EXECUTABLE_SUFFIX) +IMG_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/img_from_target_files$(HOST_EXECUTABLE_SUFFIX) +MAKE_RECOVERY_PATCH := $(HOST_OUT_EXECUTABLES)/make_recovery_patch$(HOST_EXECUTABLE_SUFFIX) +OTA_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/ota_from_target_files$(HOST_EXECUTABLE_SUFFIX) +SPARSE_IMG := $(HOST_OUT_EXECUTABLES)/sparse_img$(HOST_EXECUTABLE_SUFFIX) +CHECK_PARTITION_SIZES := $(HOST_OUT_EXECUTABLES)/check_partition_sizes$(HOST_EXECUTABLE_SUFFIX) +SYMBOLS_MAP := $(HOST_OUT_EXECUTABLES)/symbols_map + +PROGUARD_HOME := external/proguard +PROGUARD := $(PROGUARD_HOME)/bin/proguard.sh +PROGUARD_DEPS := $(PROGUARD) $(PROGUARD_HOME)/lib/proguard.jar +JAVATAGS := build/make/tools/java-event-log-tags.py +MERGETAGS := build/make/tools/merge-event-log-tags.py +APPEND2SIMG := $(HOST_OUT_EXECUTABLES)/append2simg +VERITY_SIGNER := $(HOST_OUT_EXECUTABLES)/verity_signer +BUILD_VERITY_METADATA := $(HOST_OUT_EXECUTABLES)/build_verity_metadata +BUILD_VERITY_TREE := $(HOST_OUT_EXECUTABLES)/build_verity_tree +BOOT_SIGNER := $(HOST_OUT_EXECUTABLES)/boot_signer +FUTILITY := $(HOST_OUT_EXECUTABLES)/futility-host +VBOOT_SIGNER := $(HOST_OUT_EXECUTABLES)/vboot_signer +FEC := $(HOST_OUT_EXECUTABLES)/fec + +DEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump$(BUILD_EXECUTABLE_SUFFIX) +PROFMAN := $(HOST_OUT_EXECUTABLES)/profman + +FINDBUGS_DIR := external/owasp/sanitizer/tools/findbugs/bin +FINDBUGS := $(FINDBUGS_DIR)/findbugs + +JETIFIER := prebuilts/sdk/tools/jetifier/jetifier-standalone/bin/jetifier-standalone + +EXTRACT_KERNEL := build/make/tools/extract_kernel.py + +# Path to tools.jar +HOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar + +APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner + +# Boolean variable determining if the allow list for compatible properties is enabled +PRODUCT_COMPATIBLE_PROPERTY := true +ifeq ($(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE),false) + $(error PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE is obsolete) +endif + +.KATI_READONLY := \ + PRODUCT_COMPATIBLE_PROPERTY + +# Boolean variable determining if Treble is fully enabled +PRODUCT_FULL_TREBLE := false +ifneq ($(PRODUCT_FULL_TREBLE_OVERRIDE),) + PRODUCT_FULL_TREBLE := $(PRODUCT_FULL_TREBLE_OVERRIDE) +else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),) + #$(warning no product shipping level defined) +else ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),26),) + PRODUCT_FULL_TREBLE := true +endif + +# TODO(b/69865032): Make PRODUCT_NOTICE_SPLIT the default behavior and remove +# references to it here and below. +ifdef PRODUCT_NOTICE_SPLIT_OVERRIDE + $(error PRODUCT_NOTICE_SPLIT_OVERRIDE cannot be set.) +endif + +requirements := \ + PRODUCT_TREBLE_LINKER_NAMESPACES \ + PRODUCT_SEPOLICY_SPLIT \ + PRODUCT_ENFORCE_VINTF_MANIFEST \ + PRODUCT_NOTICE_SPLIT + +# If it is overriden, then the requirement override is taken, otherwise it's +# PRODUCT_FULL_TREBLE +$(foreach req,$(requirements),$(eval \ + $(req) := $(if $($(req)_OVERRIDE),$($(req)_OVERRIDE),$(PRODUCT_FULL_TREBLE)))) +# If the requirement is false for any reason, then it's not PRODUCT_FULL_TREBLE +$(foreach req,$(requirements),$(eval \ + PRODUCT_FULL_TREBLE := $(if $(filter false,$($(req))),false,$(PRODUCT_FULL_TREBLE)))) + +PRODUCT_FULL_TREBLE_OVERRIDE ?= +$(foreach req,$(requirements),$(eval $(req)_OVERRIDE ?=)) + +# TODO(b/114488870): disallow PRODUCT_FULL_TREBLE_OVERRIDE from being used. +.KATI_READONLY := \ + PRODUCT_FULL_TREBLE_OVERRIDE \ + $(foreach req,$(requirements),$(req)_OVERRIDE) \ + $(requirements) \ + PRODUCT_FULL_TREBLE \ + +$(KATI_obsolete_var $(foreach req,$(requirements),$(req)_OVERRIDE) \ + ,This should be referenced without the _OVERRIDE suffix.) + +requirements := + +# BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED can be true only if early-mount of +# partitions is supported. But the early-mount must be supported for full +# treble products, and so BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED should be set +# by default for full treble products. +ifeq ($(PRODUCT_FULL_TREBLE),true) + BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED ?= true +endif + +# If PRODUCT_USE_VNDK is true and BOARD_VNDK_VERSION is not defined yet, +# BOARD_VNDK_VERSION will be set to "current" as default. +# PRODUCT_USE_VNDK will be true in Android-P or later launching devices. +PRODUCT_USE_VNDK := false +ifneq ($(PRODUCT_USE_VNDK_OVERRIDE),) + PRODUCT_USE_VNDK := $(PRODUCT_USE_VNDK_OVERRIDE) +else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),) + # No shipping level defined +else ifeq ($(call math_gt,$(PRODUCT_SHIPPING_API_LEVEL),27),true) + PRODUCT_USE_VNDK := $(PRODUCT_FULL_TREBLE) +endif + +ifeq ($(PRODUCT_USE_VNDK),true) + ifndef BOARD_VNDK_VERSION + BOARD_VNDK_VERSION := current + endif +endif + +$(KATI_obsolete_var PRODUCT_USE_VNDK,Use BOARD_VNDK_VERSION instead) +$(KATI_obsolete_var PRODUCT_USE_VNDK_OVERRIDE,Use BOARD_VNDK_VERSION instead) + +ifdef PRODUCT_PRODUCT_VNDK_VERSION + ifndef BOARD_VNDK_VERSION + # VNDK for product partition is not available unless BOARD_VNDK_VERSION + # defined. + $(error PRODUCT_PRODUCT_VNDK_VERSION cannot be defined without defining BOARD_VNDK_VERSION) + endif +endif + +# Set BOARD_SYSTEMSDK_VERSIONS to the latest SystemSDK version starting from P-launching +# devices if unset. +ifndef BOARD_SYSTEMSDK_VERSIONS + ifdef PRODUCT_SHIPPING_API_LEVEL + ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),28),) + ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + BOARD_SYSTEMSDK_VERSIONS := $(PLATFORM_SDK_VERSION) + else + BOARD_SYSTEMSDK_VERSIONS := $(PLATFORM_VERSION_CODENAME) + endif + endif + endif +endif + +ifndef BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES + BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES := current +else + ifdef PRODUCT_SHIPPING_API_LEVEL + ifneq ($(call math_lt,$(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES),$(PRODUCT_SHIPPING_API_LEVEL)),) + $(error BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES ($(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)) must be greater than or equal to PRODUCT_SHIPPING_API_LEVEL ($(PRODUCT_SHIPPING_API_LEVEL))) + endif + endif +endif +.KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES + +ifdef PRODUCT_SHIPPING_API_LEVEL + board_api_level := $(firstword $(BOARD_API_LEVEL) $(BOARD_SHIPPING_API_LEVEL)) + ifneq (,$(board_api_level)) + min_systemsdk_version := $(call math_min,$(board_api_level),$(PRODUCT_SHIPPING_API_LEVEL)) + else + min_systemsdk_version := $(PRODUCT_SHIPPING_API_LEVEL) + endif + ifneq ($(call numbers_less_than,$(min_systemsdk_version),$(BOARD_SYSTEMSDK_VERSIONS)),) + $(error BOARD_SYSTEMSDK_VERSIONS ($(BOARD_SYSTEMSDK_VERSIONS)) must all be greater than or equal to BOARD_API_LEVEL, BOARD_SHIPPING_API_LEVEL or PRODUCT_SHIPPING_API_LEVEL ($(min_systemsdk_version))) + endif + ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),28),) + ifneq ($(TARGET_IS_64_BIT), true) + ifneq ($(TARGET_USES_64_BIT_BINDER), true) + $(error When PRODUCT_SHIPPING_API_LEVEL >= 28, TARGET_USES_64_BIT_BINDER must be true) + endif + endif + endif + ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29),) + ifneq ($(BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE),) + $(error When PRODUCT_SHIPPING_API_LEVEL >= 29, BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE cannot be set) + endif + endif +endif + +# The default key if not set as LOCAL_CERTIFICATE +ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE + DEFAULT_SYSTEM_DEV_CERTIFICATE := $(PRODUCT_DEFAULT_DEV_CERTIFICATE) +else + DEFAULT_SYSTEM_DEV_CERTIFICATE := build/make/target/product/security/testkey +endif +.KATI_READONLY := DEFAULT_SYSTEM_DEV_CERTIFICATE + +# Certificate for the NetworkStack sepolicy context +ifdef PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES + MAINLINE_SEPOLICY_DEV_CERTIFICATES := $(PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES) +else + MAINLINE_SEPOLICY_DEV_CERTIFICATES := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE)) +endif + +BUILD_NUMBER_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_number.txt) +BUILD_DATETIME_FROM_FILE := $$(cat $(BUILD_DATETIME_FILE)) + +# SEPolicy versions + +# PLATFORM_SEPOLICY_VERSION is a number of the form "NN.m" with "NN" mapping to +# PLATFORM_SDK_VERSION and "m" as a minor number which allows for SELinux +# changes independent of PLATFORM_SDK_VERSION. This value will be set to +# 10000.0 to represent tip-of-tree development that is inherently unstable and +# thus designed not to work with any shipping vendor policy. This is similar in +# spirit to how DEFAULT_APP_TARGET_SDK is set. +# The minor version ('m' component) must be updated every time a platform release +# is made which breaks compatibility with the previous platform sepolicy version, +# not just on every increase in PLATFORM_SDK_VERSION. The minor version should +# be reset to 0 on every bump of the PLATFORM_SDK_VERSION. +sepolicy_major_vers := 33 +sepolicy_minor_vers := 0 + +ifneq ($(sepolicy_major_vers), $(PLATFORM_SDK_VERSION)) +$(error sepolicy_major_version does not match PLATFORM_SDK_VERSION, please update.) +endif + +TOT_SEPOLICY_VERSION := 10000.0 +ifneq (REL,$(PLATFORM_VERSION_CODENAME)) + PLATFORM_SEPOLICY_VERSION := $(TOT_SEPOLICY_VERSION) +else + PLATFORM_SEPOLICY_VERSION := $(join $(addsuffix .,$(sepolicy_major_vers)), $(sepolicy_minor_vers)) +endif +sepolicy_major_vers := +sepolicy_minor_vers := + +# BOARD_SEPOLICY_VERS must take the format "NN.m" and contain the sepolicy +# version identifier corresponding to the sepolicy on which the non-platform +# policy is to be based. If unspecified, this will build against the current +# public platform policy in tree +ifndef BOARD_SEPOLICY_VERS +# The default platform policy version. +BOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION) +endif + +ifeq ($(BOARD_SEPOLICY_VERS),$(PLATFORM_SEPOLICY_VERSION)) +IS_TARGET_MIXED_SEPOLICY := +else +IS_TARGET_MIXED_SEPOLICY := true +endif + +.KATI_READONLY := IS_TARGET_MIXED_SEPOLICY + +# A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports. +PLATFORM_SEPOLICY_COMPAT_VERSIONS := \ + 28.0 \ + 29.0 \ + 30.0 \ + 31.0 \ + 32.0 \ + +.KATI_READONLY := \ + PLATFORM_SEPOLICY_COMPAT_VERSIONS \ + PLATFORM_SEPOLICY_VERSION \ + TOT_SEPOLICY_VERSION \ + +ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true) + ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) + $(error PRODUCT_USE_DYNAMIC_PARTITIONS must be true when PRODUCT_RETROFIT_DYNAMIC_PARTITIONS \ + is set) + endif + ifdef PRODUCT_SHIPPING_API_LEVEL + ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29)) + $(error Devices with shipping API level $(PRODUCT_SHIPPING_API_LEVEL) must not set \ + PRODUCT_RETROFIT_DYNAMIC_PARTITIONS) + endif + endif +endif + +ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) + ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + $(error BOARD_BUILD_SYSTEM_ROOT_IMAGE cannot be true for devices with dynamic partitions) + endif + ifneq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true) + $(error PRODUCT_USE_DYNAMIC_PARTITION_SIZE must be true for devices with dynamic partitions) + endif +endif + +ifeq ($(PRODUCT_BUILD_SUPER_PARTITION),true) + ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) + $(error Can only build super partition for devices with dynamic partitions) + endif +endif + + +ifeq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true) + +ifneq ($(BOARD_SYSTEMIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_SYSTEMIMAGE_PARTITION_SIZE and \ + BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_VENDORIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_VENDORIMAGE_PARTITION_SIZE and \ + BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_ODMIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_ODMIMAGE_PARTITION_SIZE and \ + BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE and \ + BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_ODM_DLKMIMAGE_PARTITION_SIZE and \ + BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE and \ + BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_PRODUCTIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_PRODUCTIMAGE_PARTITION_SIZE and \ + BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +ifneq ($(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE),) +ifneq ($(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE),) +$(error Should not define BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE and \ + BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE together) +endif +endif + +endif # PRODUCT_USE_DYNAMIC_PARTITION_SIZE + +ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) + +# BOARD_SUPER_PARTITION_GROUPS defines a list of "updatable groups". Each updatable group is a +# group of partitions that share the same pool of free spaces. +# For each group in BOARD_SUPER_PARTITION_GROUPS, a BOARD_{GROUP}_SIZE and +# BOARD_{GROUP}_PARTITION_PARTITION_LIST may be defined. +# - BOARD_{GROUP}_SIZE: The maximum sum of sizes of all partitions in the group. +# Must not be empty. +# - BOARD_{GROUP}_PARTITION_PARTITION_LIST: the list of partitions that belongs to this group. +# If empty, no partitions belong to this group, and the sum of sizes is effectively 0. +$(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \ + $(eval BOARD_$(group)_SIZE := $(strip $(BOARD_$(group)_SIZE))) \ + $(if $(BOARD_$(group)_SIZE),,$(error BOARD_$(group)_SIZE must not be empty)) \ + $(eval .KATI_READONLY := BOARD_$(group)_SIZE) \ + $(eval BOARD_$(group)_PARTITION_LIST ?=) \ + $(eval .KATI_READONLY := BOARD_$(group)_PARTITION_LIST) \ +) + +# BOARD_*_PARTITION_LIST: a list of the following tokens +valid_super_partition_list := system vendor product system_ext odm vendor_dlkm odm_dlkm system_dlkm +$(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \ + $(if $(filter-out $(valid_super_partition_list),$(BOARD_$(group)_PARTITION_LIST)), \ + $(error BOARD_$(group)_PARTITION_LIST contains invalid partition name \ + $(filter-out $(valid_super_partition_list),$(BOARD_$(group)_PARTITION_LIST)). \ + Valid names are $(valid_super_partition_list)))) +valid_super_partition_list := + + +# Define BOARD_SUPER_PARTITION_PARTITION_LIST, the sum of all BOARD_*_PARTITION_LIST +ifdef BOARD_SUPER_PARTITION_PARTITION_LIST +$(error BOARD_SUPER_PARTITION_PARTITION_LIST should not be defined, but computed from \ + BOARD_SUPER_PARTITION_GROUPS and BOARD_*_PARTITION_LIST) +endif +BOARD_SUPER_PARTITION_PARTITION_LIST := \ + $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)),$(BOARD_$(group)_PARTITION_LIST)) +.KATI_READONLY := BOARD_SUPER_PARTITION_PARTITION_LIST + +ifneq ($(BOARD_SUPER_PARTITION_SIZE),) +ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true) + +# The metadata device must be specified manually for retrofitting. +ifeq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),) +$(error Must specify BOARD_SUPER_PARTITION_METADATA_DEVICE if PRODUCT_RETROFIT_DYNAMIC_PARTITIONS=true.) +endif + +# The super partition block device list must be specified manually for retrofitting. +ifeq ($(BOARD_SUPER_PARTITION_BLOCK_DEVICES),) +$(error Must specify BOARD_SUPER_PARTITION_BLOCK_DEVICES if PRODUCT_RETROFIT_DYNAMIC_PARTITIONS=true.) +endif + +# The metadata device must be included in the super partition block device list. +ifeq (,$(filter $(BOARD_SUPER_PARTITION_METADATA_DEVICE),$(BOARD_SUPER_PARTITION_BLOCK_DEVICES))) +$(error BOARD_SUPER_PARTITION_METADATA_DEVICE is not listed in BOARD_SUPER_PARTITION_BLOCK_DEVICES.) +endif + +# The metadata device must be supplied to init via the kernel command-line. +INTERNAL_KERNEL_CMDLINE += androidboot.super_partition=$(BOARD_SUPER_PARTITION_METADATA_DEVICE) + +BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := true + +# If "vendor" is listed as one of the dynamic partitions but without its image available (e.g. an +# AOSP target built without vendor image), don't build the retrofit full OTA package. Because we +# won't be able to build meaningful super_* images for retrofitting purpose. +ifneq (,$(filter vendor,$(BOARD_SUPER_PARTITION_PARTITION_LIST))) +ifndef BUILDING_VENDOR_IMAGE +ifndef BOARD_PREBUILT_VENDORIMAGE +BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := +endif # BOARD_PREBUILT_VENDORIMAGE +endif # BUILDING_VENDOR_IMAGE +endif # BOARD_SUPER_PARTITION_PARTITION_LIST + +else # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS + +# For normal devices, we populate BOARD_SUPER_PARTITION_BLOCK_DEVICES so the +# build can handle both cases consistently. +ifeq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),) +BOARD_SUPER_PARTITION_METADATA_DEVICE := super +endif + +ifeq ($(BOARD_SUPER_PARTITION_BLOCK_DEVICES),) +BOARD_SUPER_PARTITION_BLOCK_DEVICES := $(BOARD_SUPER_PARTITION_METADATA_DEVICE) +endif + +# If only one super block device, default to super partition size. +ifeq ($(word 2,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),) +BOARD_SUPER_PARTITION_$(call to-upper,$(strip $(BOARD_SUPER_PARTITION_BLOCK_DEVICES)))_DEVICE_SIZE ?= \ + $(BOARD_SUPER_PARTITION_SIZE) +endif + +ifneq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),super) +INTERNAL_KERNEL_CMDLINE += androidboot.super_partition=$(BOARD_SUPER_PARTITION_METADATA_DEVICE) +endif +BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := + +endif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS +endif # BOARD_SUPER_PARTITION_SIZE +BOARD_SUPER_PARTITION_BLOCK_DEVICES ?= +.KATI_READONLY := BOARD_SUPER_PARTITION_BLOCK_DEVICES +BOARD_SUPER_PARTITION_METADATA_DEVICE ?= +.KATI_READONLY := BOARD_SUPER_PARTITION_METADATA_DEVICE +BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE ?= +.KATI_READONLY := BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE + +$(foreach device,$(call to-upper,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)), \ + $(eval BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE := $(strip $(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE))) \ + $(if $(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE),, \ + $(error BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE must not be empty)) \ + $(eval .KATI_READONLY := BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE)) + +endif # PRODUCT_USE_DYNAMIC_PARTITIONS + +# By default, we build the hidden API csv files from source. You can use +# prebuilt hiddenapi files by setting BOARD_PREBUILT_HIDDENAPI_DIR to the name +# of a directory containing both prebuilt hiddenapi-flags.csv and +# hiddenapi-index.csv. +BOARD_PREBUILT_HIDDENAPI_DIR ?= +.KATI_READONLY := BOARD_PREBUILT_HIDDENAPI_DIR + +ifdef USE_HOST_MUSL + ifneq (,$(or $(BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE),\ + $(BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY),\ + $(BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY))) + $(error USE_HOST_MUSL can't be set when native host builds are enabled in Make with BUILD_BROKEN_USES_BUILD_HOST_*) + endif +endif + +# ############################################################### +# Set up final options. +# ############################################################### + +# We run gcc/clang with PWD=/proc/self/cwd to remove the $TOP +# from the debug output. That way two builds in two different +# directories will create the same output. +# /proc doesn't exist on Darwin. +ifeq ($(HOST_OS),linux) +RELATIVE_PWD := PWD=/proc/self/cwd +else +RELATIVE_PWD := +endif + +# Flags for DEX2OAT +first_non_empty_of_three = $(if $(1),$(1),$(if $(2),$(2),$(3))) +DEX2OAT_TARGET_ARCH := $(TARGET_ARCH) +DEX2OAT_TARGET_CPU_VARIANT := $(call first_non_empty_of_three,$(TARGET_CPU_VARIANT),$(TARGET_ARCH_VARIANT),default) +DEX2OAT_TARGET_CPU_VARIANT_RUNTIME := $(call first_non_empty_of_three,$(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_ARCH_VARIANT),default) +DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default + +ifdef TARGET_2ND_ARCH +$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH := $(TARGET_2ND_ARCH) +$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT := $(call first_non_empty_of_three,$(TARGET_2ND_CPU_VARIANT),$(TARGET_2ND_ARCH_VARIANT),default) +$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME := $(call first_non_empty_of_three,$(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_ARCH_VARIANT),default) +$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default +endif + +# ############################################################### +# Collect a list of the SDK versions that we could compile against +# For use with the LOCAL_SDK_VERSION variable for include $(BUILD_PACKAGE) +# ############################################################### + +HISTORICAL_SDK_VERSIONS_ROOT := $(TOPDIR)prebuilts/sdk +HISTORICAL_NDK_VERSIONS_ROOT := $(TOPDIR)prebuilts/ndk + +# The path where app can reference the support library resources. +ifdef TARGET_BUILD_USE_PREBUILT_SDKS +SUPPORT_LIBRARY_ROOT := $(HISTORICAL_SDK_VERSIONS_ROOT)/current/support +else +SUPPORT_LIBRARY_ROOT := frameworks/support +endif + +get-sdk-version = $(if $(findstring _,$(1)),$(subst core_,,$(subst system_,,$(subst test_,,$(1)))),$(1)) +get-sdk-api = $(if $(findstring _,$(1)),$(patsubst %_$(call get-sdk-version,$(1)),%,$(1)),public) +get-prebuilt-sdk-dir = $(HISTORICAL_SDK_VERSIONS_ROOT)/$(call get-sdk-version,$(1))/$(call get-sdk-api,$(1)) + +# Resolve LOCAL_SDK_VERSION to prebuilt module name, e.g.: +# 23 -> sdk_public_23_android +# system_current -> sdk_system_current_android +# $(1): An sdk version (LOCAL_SDK_VERSION) +# $(2): optional library name (default: android) +define resolve-prebuilt-sdk-module +$(if $(findstring _,$(1)),\ + sdk_$(1)_$(or $(2),android),\ + sdk_public_$(1)_$(or $(2),android)) +endef + +# Resolve LOCAL_SDK_VERSION to prebuilt android.jar +# $(1): LOCAL_SDK_VERSION +resolve-prebuilt-sdk-jar-path = $(call get-prebuilt-sdk-dir,$(1))/android.jar + +# Resolve LOCAL_SDK_VERSION to prebuilt framework.aidl +# $(1): An sdk version (LOCAL_SDK_VERSION) +resolve-prebuilt-sdk-aidl-path = $(call get-prebuilt-sdk-dir,$(call get-sdk-version,$(1)))/framework.aidl + +# Historical SDK version N is stored in $(HISTORICAL_SDK_VERSIONS_ROOT)/N. +# The 'current' version is whatever this source tree is. +# +# sgrax is the opposite of xargs. It takes the list of args and puts them +# on each line for sort to process. +# sort -g is a numeric sort, so 1 2 3 10 instead of 1 10 2 3. + +# Numerically sort a list of numbers +# $(1): the list of numbers to be sorted +define numerically_sort +$(shell function sgrax() { \ + while [ -n "$$1" ] ; do echo $$1 ; shift ; done \ + } ; \ + ( sgrax $(1) | sort -g ) ) +endef + +# This produces a list like "current/core current/public current/system 4/public" +TARGET_AVAILABLE_SDK_VERSIONS := $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/*/android.jar) +TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/android.jar,%,$(TARGET_AVAILABLE_SDK_VERSIONS)) +# Strips and reorganizes the "public", "core", "system" and "test" subdirs. +TARGET_AVAILABLE_SDK_VERSIONS := $(subst /public,,$(TARGET_AVAILABLE_SDK_VERSIONS)) +TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/core,core_%,$(TARGET_AVAILABLE_SDK_VERSIONS)) +TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/system,system_%,$(TARGET_AVAILABLE_SDK_VERSIONS)) +TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/test,test_%,$(TARGET_AVAILABLE_SDK_VERSIONS)) +# module-lib and system-server are not supported in Make. +TARGET_AVAILABLE_SDK_VERSIONS := $(filter-out %/module-lib %/system-server,$(TARGET_AVAILABLE_SDK_VERSIONS)) +TARGET_AVAIALBLE_SDK_VERSIONS := $(call numerically_sort,$(TARGET_AVAILABLE_SDK_VERSIONS)) + +TARGET_SDK_VERSIONS_WITHOUT_JAVA_18_SUPPORT := $(call numbers_less_than,24,$(TARGET_AVAILABLE_SDK_VERSIONS)) +TARGET_SDK_VERSIONS_WITHOUT_JAVA_19_SUPPORT := $(call numbers_less_than,30,$(TARGET_AVAILABLE_SDK_VERSIONS)) + +# Missing optional uses-libraries so that the platform doesn't create build rules that depend on +# them. +INTERNAL_PLATFORM_MISSING_USES_LIBRARIES := \ + com.google.android.ble \ + com.google.android.media.effects \ + com.google.android.wearable \ + +# This is the standard way to name a directory containing prebuilt target +# objects. E.g., prebuilt/$(TARGET_PREBUILT_TAG)/libc.so +TARGET_PREBUILT_TAG := android-$(TARGET_ARCH) +ifdef TARGET_2ND_ARCH +TARGET_2ND_PREBUILT_TAG := android-$(TARGET_2ND_ARCH) +endif + +# Set up RS prebuilt variables for compatibility library + +RS_PREBUILT_CLCORE := prebuilts/sdk/renderscript/lib/$(TARGET_ARCH)/librsrt_$(TARGET_ARCH).bc +RS_PREBUILT_COMPILER_RT := prebuilts/sdk/renderscript/lib/$(TARGET_ARCH)/libcompiler_rt.a + +# API Level lists for Renderscript Compat lib. +RSCOMPAT_32BIT_ONLY_API_LEVELS := 8 9 10 11 12 13 14 15 16 17 18 19 20 +RSCOMPAT_NO_USAGEIO_API_LEVELS := 8 9 10 11 12 13 + +# Add BUILD_NUMBER to apps default version name if it's unbundled build. +ifdef TARGET_BUILD_APPS +TARGET_BUILD_WITH_APPS_VERSION_NAME := true +endif + +ifdef TARGET_BUILD_WITH_APPS_VERSION_NAME +APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE) +else +APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION) +endif + +# ANDROID_WARNING_ALLOWED_PROJECTS is generated by build/soong. +define find_warning_allowed_projects + $(filter $(ANDROID_WARNING_ALLOWED_PROJECTS),$(1)/) +endef + +GOMA_POOL := +RBE_POOL := +GOMA_OR_RBE_POOL := +# When goma or RBE are enabled, kati will be passed --default_pool=local_pool to put +# most rules into the local pool. Explicitly set the pool to "none" for rules that +# should be run outside the local pool, i.e. with -j500. +ifneq (,$(filter-out false,$(USE_GOMA))) + GOMA_POOL := none + GOMA_OR_RBE_POOL := none +else ifneq (,$(filter-out false,$(USE_RBE))) + RBE_POOL := none + GOMA_OR_RBE_POOL := none +endif +.KATI_READONLY := GOMA_POOL RBE_POOL GOMA_OR_RBE_POOL + +JAVAC_NINJA_POOL := +R8_NINJA_POOL := +D8_NINJA_POOL := + +ifneq ($(filter-out false,$(USE_RBE)),) + ifdef RBE_JAVAC + JAVAC_NINJA_POOL := $(RBE_POOL) + endif + ifdef RBE_R8 + R8_NINJA_POOL := $(RBE_POOL) + endif + ifdef RBE_D8 + D8_NINJA_POOL := $(RBE_POOL) + endif +endif + +.KATI_READONLY := JAVAC_NINJA_POOL R8_NINJA_POOL D8_NINJA_POOL + +# These goals don't need to collect and include Android.mks/CleanSpec.mks +# in the source tree. +dont_bother_goals := out product-graph + +# Make ANDROID Soong config variables visible to Android.mk files, for +# consistency with those defined in BoardConfig.mk files. +include $(BUILD_SYSTEM)/android_soong_config_vars.mk + +ifeq ($(CALLED_FROM_SETUP),true) +include $(BUILD_SYSTEM)/ninja_config.mk +include $(BUILD_SYSTEM)/soong_config.mk +endif + +-include external/linux-kselftest/android/kselftest_test_list.mk +-include external/ltp/android/ltp_package_list.mk +DEFAULT_DATA_OUT_MODULES := ltp $(ltp_packages) $(kselftest_modules) +.KATI_READONLY := DEFAULT_DATA_OUT_MODULES + +include $(BUILD_SYSTEM)/dumpvar.mk diff --git a/make/core/config_sanitizers.mk b/make/core/config_sanitizers.mk new file mode 100644 index 0000000..a0ff119 --- /dev/null +++ b/make/core/config_sanitizers.mk @@ -0,0 +1,507 @@ +############################################## +## Perform configuration steps for sanitizers. +############################################## + +my_sanitize := $(strip $(LOCAL_SANITIZE)) +my_sanitize_diag := $(strip $(LOCAL_SANITIZE_DIAG)) + +my_global_sanitize := +my_global_sanitize_diag := +ifdef LOCAL_IS_HOST_MODULE + ifneq ($($(my_prefix)OS),windows) + my_global_sanitize := $(strip $(SANITIZE_HOST)) + + # SANITIZE_HOST=true is a deprecated way to say SANITIZE_HOST=address. + my_global_sanitize := $(subst true,address,$(my_global_sanitize)) + endif +else + my_global_sanitize := $(strip $(SANITIZE_TARGET)) + my_global_sanitize_diag := $(strip $(SANITIZE_TARGET_DIAG)) +endif + +# Disable global integer_overflow in excluded paths. +ifneq ($(filter integer_overflow, $(my_global_sanitize)),) + combined_exclude_paths := $(INTEGER_OVERFLOW_EXCLUDE_PATHS) \ + $(PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS) + + ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize)) + my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag)) + endif +endif + +# Global integer sanitization doesn't support static modules. +ifeq ($(filter SHARED_LIBRARIES EXECUTABLES,$(LOCAL_MODULE_CLASS)),) + my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize)) + my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag)) +endif +ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize)) + my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag)) +endif + +# Disable global CFI in excluded paths +ifneq ($(filter cfi, $(my_global_sanitize)),) + combined_exclude_paths := $(CFI_EXCLUDE_PATHS) \ + $(PRODUCT_CFI_EXCLUDE_PATHS) + + ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + my_global_sanitize := $(filter-out cfi,$(my_global_sanitize)) + my_global_sanitize_diag := $(filter-out cfi,$(my_global_sanitize_diag)) + endif +endif + +# Disable global memtag_heap in excluded paths +ifneq ($(filter memtag_heap, $(my_global_sanitize)),) + combined_exclude_paths := $(MEMTAG_HEAP_EXCLUDE_PATHS) \ + $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS) + + ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + my_global_sanitize := $(filter-out memtag_heap,$(my_global_sanitize)) + my_global_sanitize_diag := $(filter-out memtag_heap,$(my_global_sanitize_diag)) + endif +endif + +ifneq ($(my_global_sanitize),) + my_sanitize := $(my_global_sanitize) $(my_sanitize) +endif +ifneq ($(my_global_sanitize_diag),) + my_sanitize_diag := $(my_global_sanitize_diag) $(my_sanitize_diag) +endif + +# The sanitizer specified in the product configuration wins over the previous. +ifneq ($(SANITIZER.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG),) + my_sanitize := $(SANITIZER.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG) + ifeq ($(my_sanitize),never) + my_sanitize := + my_sanitize_diag := + endif +endif + +ifndef LOCAL_IS_HOST_MODULE + # Add a filter point for 32-bit vs 64-bit sanitization (to lighten the burden) + SANITIZE_TARGET_ARCH ?= $(TARGET_ARCH) $(TARGET_2ND_ARCH) + ifeq ($(filter $(SANITIZE_TARGET_ARCH),$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),) + my_sanitize := + my_sanitize_diag := + endif +endif + +# Add a filter point based on module owner (to lighten the burden). The format is a space- or +# colon-separated list of owner names. +ifneq (,$(SANITIZE_NEVER_BY_OWNER)) + ifneq (,$(LOCAL_MODULE_OWNER)) + ifneq (,$(filter $(LOCAL_MODULE_OWNER),$(subst :, ,$(SANITIZE_NEVER_BY_OWNER)))) + $(warning Not sanitizing $(LOCAL_MODULE) based on module owner.) + my_sanitize := + my_sanitize_diag := + endif + endif +endif + +# Don't apply sanitizers to NDK code. +ifdef LOCAL_SDK_VERSION + my_sanitize := + my_global_sanitize := + my_sanitize_diag := +endif + +# Never always wins. +ifeq ($(LOCAL_SANITIZE),never) + my_sanitize := + my_sanitize_diag := +endif + +# Enable CFI in included paths. +ifeq ($(filter cfi, $(my_sanitize)),) + combined_include_paths := $(CFI_INCLUDE_PATHS) \ + $(PRODUCT_CFI_INCLUDE_PATHS) + combined_exclude_paths := $(CFI_EXCLUDE_PATHS) \ + $(PRODUCT_CFI_EXCLUDE_PATHS) + + ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_include_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + ifeq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + my_sanitize := cfi $(my_sanitize) + endif + endif +endif + +# Enable memtag_heap in included paths (for Arm64 only). +ifeq ($(filter memtag_heap, $(my_sanitize)),) + ifneq ($(filter arm64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),) + combined_sync_include_paths := $(MEMTAG_HEAP_SYNC_INCLUDE_PATHS) \ + $(PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS) + combined_async_include_paths := $(MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) \ + $(PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) + combined_exclude_paths := $(MEMTAG_HEAP_EXCLUDE_PATHS) \ + $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS) + + ifeq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_sync_include_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + my_sanitize := memtag_heap $(my_sanitize) + my_sanitize_diag := memtag_heap $(my_sanitize_diag) + else ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_async_include_paths)),\ + $(filter $(dir)%,$(LOCAL_PATH)))),) + my_sanitize := memtag_heap $(my_sanitize) + endif + endif + endif +endif + +# If CFI is disabled globally, remove it from my_sanitize. +ifeq ($(strip $(ENABLE_CFI)),false) + my_sanitize := $(filter-out cfi,$(my_sanitize)) + my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag)) +endif + +# Also disable CFI if ASAN is enabled. +ifneq ($(filter address,$(my_sanitize)),) + my_sanitize := $(filter-out cfi,$(my_sanitize)) + my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag)) +endif + +# Disable memtag for host targets. Host executables in AndroidMk files are +# deprecated, but some partners still have them floating around. +ifdef LOCAL_IS_HOST_MODULE + my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) + my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag)) +endif + +# Disable sanitizers which need the UBSan runtime for host targets. +ifdef LOCAL_IS_HOST_MODULE + my_sanitize := $(filter-out cfi,$(my_sanitize)) + my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag)) + my_sanitize := $(filter-out signed-integer-overflow unsigned-integer-overflow integer_overflow,$(my_sanitize)) + my_sanitize_diag := $(filter-out signed-integer-overflow unsigned-integer-overflow integer_overflow,$(my_sanitize_diag)) +endif + +# Support for local sanitize blacklist paths. +ifneq ($(my_sanitize)$(my_global_sanitize),) + ifneq ($(LOCAL_SANITIZE_BLOCKLIST),) + my_cflags += -fsanitize-blacklist=$(LOCAL_PATH)/$(LOCAL_SANITIZE_BLOCKLIST) + endif +endif + +# Disable integer_overflow if LOCAL_NOSANITIZE=integer. +ifneq ($(filter integer_overflow, $(my_global_sanitize) $(my_sanitize)),) + ifneq ($(filter integer, $(strip $(LOCAL_NOSANITIZE))),) + my_sanitize := $(filter-out integer_overflow,$(my_sanitize)) + my_sanitize_diag := $(filter-out integer_overflow,$(my_sanitize_diag)) + endif +endif + +my_nosanitize = $(strip $(LOCAL_NOSANITIZE)) +ifneq ($(my_nosanitize),) + my_sanitize := $(filter-out $(my_nosanitize),$(my_sanitize)) +endif + +ifneq ($(filter arm x86 x86_64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),) + my_sanitize := $(filter-out hwaddress,$(my_sanitize)) + my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) +endif + +ifneq ($(filter hwaddress,$(my_sanitize)),) + my_sanitize := $(filter-out address,$(my_sanitize)) + my_sanitize := $(filter-out thread,$(my_sanitize)) + my_sanitize := $(filter-out cfi,$(my_sanitize)) +endif + +ifneq ($(filter hwaddress,$(my_sanitize)),) + my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)HWADDRESS_SANITIZER_RUNTIME_LIBRARY) + ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + my_static_libraries := $(my_static_libraries) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)HWADDRESS_SANITIZER_STATIC_LIBRARY) \ + libdl + endif + endif +endif + +ifneq ($(filter memtag_heap,$(my_sanitize)),) + # Add memtag ELF note. + ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + ifneq ($(filter memtag_heap,$(my_sanitize_diag)),) + my_whole_static_libraries += note_memtag_heap_sync + else + my_whole_static_libraries += note_memtag_heap_async + endif + endif + # This is all that memtag_heap does - it is not an actual -fsanitize argument. + # Remove it from the list. + my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) +endif + +my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag)) + +# TSAN is not supported on 32-bit architectures. For non-multilib cases, make +# its use an error. For multilib cases, don't use it for the 32-bit case. +ifneq ($(filter thread,$(my_sanitize)),) + ifeq ($(my_32_64_bit_suffix),32) + ifeq ($(my_module_multilib),both) + my_sanitize := $(filter-out thread,$(my_sanitize)) + else + $(error $(LOCAL_PATH): $(LOCAL_MODULE): TSAN cannot be used for 32-bit modules.) + endif + else + my_shared_libraries += $(TSAN_RUNTIME_LIBRARY) + endif +endif + +ifneq ($(filter safe-stack,$(my_sanitize)),) + ifeq ($(my_32_64_bit_suffix),32) + my_sanitize := $(filter-out safe-stack,$(my_sanitize)) + endif +endif + +# Disable Scudo if ASan or TSan is enabled. +ifneq ($(filter address thread hwaddress,$(my_sanitize)),) + my_sanitize := $(filter-out scudo,$(my_sanitize)) +endif + +# Or if disabled globally. +ifeq ($(PRODUCT_DISABLE_SCUDO),true) + my_sanitize := $(filter-out scudo,$(my_sanitize)) +endif + +# Undefined symbols can occur if a non-sanitized library links +# sanitized static libraries. That's OK, because the executable +# always depends on the ASan runtime library, which defines these +# symbols. +ifneq ($(filter address thread,$(strip $(SANITIZE_TARGET))),) + ifndef LOCAL_IS_HOST_MODULE + ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) + ifeq ($(my_sanitize),) + my_allow_undefined_symbols := true + endif + endif + endif +endif + +ifneq ($(filter default-ub,$(my_sanitize)),) + my_sanitize := $(CLANG_DEFAULT_UB_CHECKS) +endif + +ifneq ($(filter fuzzer,$(my_sanitize)),) + # SANITIZE_TARGET='fuzzer' actually means to create the fuzzer coverage + # information, not to link against the fuzzer main(). + my_sanitize := $(filter-out fuzzer,$(my_sanitize)) + my_sanitize += fuzzer-no-link + + # TODO(b/131771163): Disable LTO for fuzzer builds. Note that Cfi causes + # dependency on LTO. + my_sanitize := $(filter-out cfi,$(my_sanitize)) + my_cflags += -fno-lto + my_ldflags += -fno-lto +endif + +ifneq ($(filter integer_overflow,$(my_sanitize)),) + # Respect LOCAL_NOSANITIZE for integer-overflow flags. + ifeq ($(filter signed-integer-overflow, $(strip $(LOCAL_NOSANITIZE))),) + my_sanitize += signed-integer-overflow + endif + ifeq ($(filter unsigned-integer-overflow, $(strip $(LOCAL_NOSANITIZE))),) + my_sanitize += unsigned-integer-overflow + endif + my_cflags += $(INTEGER_OVERFLOW_EXTRA_CFLAGS) + + # Check for diagnostics mode. + ifneq ($(filter integer_overflow,$(my_sanitize_diag)),) + ifneq ($(filter SHARED_LIBRARIES EXECUTABLES,$(LOCAL_MODULE_CLASS)),) + ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + my_sanitize_diag += signed-integer-overflow + my_sanitize_diag += unsigned-integer-overflow + else + $(call pretty-error,Make cannot apply integer overflow diagnostics to static binary.) + endif + else + $(call pretty-error,Make cannot apply integer overflow diagnostics to static library.) + endif + endif + my_sanitize := $(filter-out integer_overflow,$(my_sanitize)) +endif + +# Makes sure integer_overflow diagnostics is removed from the diagnostics list +# even if integer_overflow is not set for some reason. +ifneq ($(filter integer_overflow,$(my_sanitize_diag)),) + my_sanitize_diag := $(filter-out integer_overflow,$(my_sanitize_diag)) +endif + +ifneq ($(my_sanitize),) + fsanitize_arg := $(subst $(space),$(comma),$(my_sanitize)) + my_cflags += -fsanitize=$(fsanitize_arg) + my_asflags += -fsanitize=$(fsanitize_arg) + + # When fuzzing, we wish to crash with diagnostics on any bug. + ifneq ($(filter fuzzer-no-link,$(my_sanitize)),) + my_cflags += -fno-sanitize-trap=all + my_cflags += -fno-sanitize-recover=all + my_ldflags += -fsanitize=fuzzer-no-link + else ifdef LOCAL_IS_HOST_MODULE + my_cflags += -fno-sanitize-recover=all + my_ldflags += -fsanitize=$(fsanitize_arg) + else + my_cflags += -fsanitize-trap=all + my_cflags += -ftrap-function=abort + ifneq ($(filter address thread,$(my_sanitize)),) + my_cflags += -fno-sanitize-trap=address,thread + my_shared_libraries += libdl + endif + endif +endif + +ifneq ($(filter cfi,$(my_sanitize)),) + # __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). + # LLVM is not set up to do this on a function basis, so force Thumb on the + # entire module. + LOCAL_ARM_MODE := thumb + my_cflags += $(CFI_EXTRA_CFLAGS) + my_asflags += $(CFI_EXTRA_ASFLAGS) + # Only append the default visibility flag if -fvisibility has not already been + # set to hidden. + ifeq ($(filter -fvisibility=hidden,$(LOCAL_CFLAGS)),) + my_cflags += -fvisibility=default + endif + my_ldflags += $(CFI_EXTRA_LDFLAGS) + my_arflags += --plugin $(LLVM_PREBUILTS_PATH)/../lib64/LLVMgold.so + + ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + my_ldflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_ldflags)) + my_cflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_cflags)) + else + # Apply the version script to non-static executables + my_ldflags += -Wl,--version-script,build/soong/cc/config/cfi_exports.map + LOCAL_ADDITIONAL_DEPENDENCIES += build/soong/cc/config/cfi_exports.map + endif +endif + +# If local or global modules need ASAN, add linker flags. +ifneq ($(filter address,$(my_global_sanitize) $(my_sanitize)),) + my_ldflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS) + ifdef LOCAL_IS_HOST_MODULE + # -nodefaultlibs (provided with libc++) prevents the driver from linking + # libraries needed with -fsanitize=address. http://b/18650275 (WAI) + my_ldflags += -Wl,--no-as-needed + else + # Add asan libraries unless LOCAL_MODULE is the asan library. + # ASan runtime library must be the first in the link order. + ifeq (,$(filter $(LOCAL_MODULE),$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_RUNTIME_LIBRARY))) + my_shared_libraries := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ + $(my_shared_libraries) + endif + + # Do not add unnecessary dependency in shared libraries. + ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) + my_ldflags += -Wl,--as-needed + endif + + ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER) + # Make sure linker_asan get installed. + $(LOCAL_INSTALLED_MODULE) : | $(PRODUCT_OUT)$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER_FILE) + endif + endif + endif +endif + +# If local module needs ASAN, add compiler flags. +ifneq ($(filter address,$(my_sanitize)),) + # Frame pointer based unwinder in ASan requires ARM frame setup. + LOCAL_ARM_MODE := arm + my_cflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS) + ifndef LOCAL_IS_HOST_MODULE + my_cflags += -mllvm -asan-globals=0 + endif +endif + +# If local module needs HWASAN, add compiler flags. +ifneq ($(filter hwaddress,$(my_sanitize)),) + my_cflags += $(HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS) +endif + +# Use minimal diagnostics when integer overflow is enabled; never do it for HOST modules +ifeq ($(LOCAL_IS_HOST_MODULE),) + # Pre-emptively add UBSAN minimal runtime incase a static library dependency requires it + ifeq ($(filter STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS)),) + ifndef LOCAL_SDK_VERSION + my_static_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_MINIMAL_RUNTIME_LIBRARY) + my_ldflags += -Wl,--exclude-libs,$($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_MINIMAL_RUNTIME_LIBRARY).a + endif + endif + ifneq ($(filter unsigned-integer-overflow signed-integer-overflow integer,$(my_sanitize)),) + ifeq ($(filter unsigned-integer-overflow signed-integer-overflow integer,$(my_sanitize_diag)),) + ifeq ($(filter cfi,$(my_sanitize_diag)),) + ifeq ($(filter address hwaddress fuzzer-no-link,$(my_sanitize)),) + my_cflags += -fsanitize-minimal-runtime + my_cflags += -fno-sanitize-trap=integer + my_cflags += -fno-sanitize-recover=integer + endif + endif + endif + endif +endif + +# For Scudo, we opt for the minimal runtime, unless some diagnostics are enabled. +ifneq ($(filter scudo,$(my_sanitize)),) + ifeq ($(filter unsigned-integer-overflow signed-integer-overflow integer cfi,$(my_sanitize_diag)),) + my_cflags += -fsanitize-minimal-runtime + endif + ifneq ($(filter -fsanitize-minimal-runtime,$(my_cflags)),) + my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)SCUDO_MINIMAL_RUNTIME_LIBRARY) + else + my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)SCUDO_RUNTIME_LIBRARY) + endif +endif + +ifneq ($(strip $(LOCAL_SANITIZE_RECOVER)),) + recover_arg := $(subst $(space),$(comma),$(LOCAL_SANITIZE_RECOVER)), + my_cflags += -fsanitize-recover=$(recover_arg) +endif + +ifneq ($(strip $(LOCAL_SANITIZE_NO_RECOVER)),) + no_recover_arg := $(subst $(space),$(comma),$(LOCAL_SANITIZE_NO_RECOVER)), + my_cflags += -fno-sanitize-recover=$(no_recover_arg) +endif + +ifneq ($(my_sanitize_diag),) + # TODO(vishwath): Add diagnostic support for static executables once + # we switch to clang-4393122 (which adds the static ubsan runtime + # that this depends on) + ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) + notrap_arg := $(subst $(space),$(comma),$(my_sanitize_diag)), + my_cflags += -fno-sanitize-trap=$(notrap_arg) + # Diagnostic requires a runtime library, unless ASan or TSan are also enabled. + ifeq ($(filter address thread scudo hwaddress,$(my_sanitize)),) + # Does not have to be the first DT_NEEDED unlike ASan. + my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_RUNTIME_LIBRARY) + endif + endif +endif + +# http://b/119329758, Android core does not boot up with this sanitizer yet. +# Previously sanitized modules might not pass new implicit-integer-sign-change check. +# Disable this check unless it has been explicitly specified. +ifneq ($(findstring fsanitize,$(my_cflags)),) + ifneq ($(findstring integer,$(my_cflags)),) + ifeq ($(findstring sanitize=implicit-integer-sign-change,$(my_cflags)),) + my_cflags += -fno-sanitize=implicit-integer-sign-change + endif + endif +endif + +# http://b/177566116, libc++ may crash with this sanitizer. +# Disable this check unless it has been explicitly specified. +ifneq ($(findstring fsanitize,$(my_cflags)),) + ifneq ($(findstring integer,$(my_cflags)),) + ifeq ($(findstring sanitize=unsigned-shift-base,$(my_cflags)),) + my_cflags += -fno-sanitize=unsigned-shift-base + endif + endif +endif diff --git a/make/core/configure_module_stem.mk b/make/core/configure_module_stem.mk new file mode 100644 index 0000000..30df8ea --- /dev/null +++ b/make/core/configure_module_stem.mk @@ -0,0 +1,26 @@ +my_multilib_stem := $(LOCAL_MODULE_STEM_$(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32)) +ifdef my_multilib_stem + my_module_stem := $(my_multilib_stem) + $(call verify-module-stem,my_multilib_stem) +else ifdef LOCAL_MODULE_STEM + my_module_stem := $(LOCAL_MODULE_STEM) + $(call verify-module-stem,LOCAL_MODULE_STEM) +else + my_module_stem := $(LOCAL_MODULE) +endif + +ifdef LOCAL_BUILT_MODULE_STEM + my_built_module_stem := $(LOCAL_BUILT_MODULE_STEM) + $(call verify-module-stem,LOCAL_BUILT_MODULE_STEM) +else + my_built_module_stem := $(my_module_stem)$(LOCAL_MODULE_SUFFIX) + $(call verify-module-stem,LOCAL_MODULE_SUFFIX) +endif + +ifdef LOCAL_INSTALLED_MODULE_STEM + my_installed_module_stem := $(LOCAL_INSTALLED_MODULE_STEM) + $(call verify-module-stem,LOCAL_INSTALLED_MODULE_STEM) +else + my_installed_module_stem := $(my_module_stem)$(LOCAL_MODULE_SUFFIX) + $(call verify-module-stem,LOCAL_MODULE_SUFFIX) +endif diff --git a/make/core/copy_headers.mk b/make/core/copy_headers.mk new file mode 100644 index 0000000..054d271 --- /dev/null +++ b/make/core/copy_headers.mk @@ -0,0 +1,56 @@ +ifneq (,$(strip $(LOCAL_COPY_HEADERS))) +########################################################### +## Copy headers to the install tree +########################################################### +$(call record-module-type,COPY_HEADERS) +ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),) + $(call pretty-error,LOCAL_COPY_HEADERS may not be used with host modules) +endif + +# Modules linking against the SDK do not have the include path to use +# COPY_HEADERS, so prevent them from exporting any either. +ifdef LOCAL_SDK_VERSION + $(call pretty-error,Modules using LOCAL_SDK_VERSION may not use LOCAL_COPY_HEADERS) +endif + +include $(BUILD_SYSTEM)/local_vndk.mk + +# If we're using the VNDK, only vendor modules using the VNDK may use +# LOCAL_COPY_HEADERS. Platform libraries will not have the include path +# present. +ifdef BOARD_VNDK_VERSION +ifndef LOCAL_USE_VNDK + $(call pretty-error,Only vendor modules using LOCAL_USE_VNDK may use LOCAL_COPY_HEADERS) +endif +endif + +# Clean up LOCAL_COPY_HEADERS_TO, since soong_ui will be comparing cleaned +# paths to figure out which headers are obsolete and should be removed. +LOCAL_COPY_HEADERS_TO := $(call clean-path,$(LOCAL_COPY_HEADERS_TO)) +ifneq ($(filter /% .. ../%,$(LOCAL_COPY_HEADERS_TO)),) + $(call pretty-error,LOCAL_COPY_HEADERS_TO may not start with / or ../ : $(LOCAL_COPY_HEADERS_TO)) +endif +ifeq ($(LOCAL_COPY_HEADERS_TO),.) + LOCAL_COPY_HEADERS_TO := +endif + +# Create a rule to copy each header, and make the +# all_copied_headers phony target depend on each +# destination header. copy-one-header defines the +# actual rule. +# +$(foreach header,$(LOCAL_COPY_HEADERS), \ + $(eval _chFrom := $(LOCAL_PATH)/$(header)) \ + $(eval _chTo := \ + $(if $(LOCAL_COPY_HEADERS_TO),\ + $(TARGET_OUT_HEADERS)/$(LOCAL_COPY_HEADERS_TO)/$(notdir $(header)),\ + $(TARGET_OUT_HEADERS)/$(notdir $(header)))) \ + $(eval ALL_COPIED_HEADERS.$(_chTo).MAKEFILE += $(LOCAL_MODULE_MAKEFILE)) \ + $(eval ALL_COPIED_HEADERS.$(_chTo).SRC += $(_chFrom)) \ + $(if $(filter $(_chTo),$(ALL_COPIED_HEADERS)),, \ + $(eval ALL_COPIED_HEADERS += $(_chTo))) \ + ) +_chFrom := +_chTo := + +endif # LOCAL_COPY_HEADERS diff --git a/make/core/cxx_stl_setup.mk b/make/core/cxx_stl_setup.mk new file mode 100644 index 0000000..0d557c7 --- /dev/null +++ b/make/core/cxx_stl_setup.mk @@ -0,0 +1,95 @@ +############################################################# +## Set up flags based on LOCAL_CXX_STL. +## Input variables: LOCAL_CXX_STL, my_prefix +## Output variables: My_cflags, my_c_includes, my_shared_libraries, etc. +############################################################# + +# Select the appropriate C++ STL +ifeq ($(strip $(LOCAL_CXX_STL)),default) + ifndef LOCAL_SDK_VERSION + # Platform code. Select the appropriate STL. + my_cxx_stl := libc++ + ifdef LOCAL_IS_HOST_MODULE + ifneq (,$(BUILD_HOST_static)) + my_cxx_stl := libc++_static + endif + endif + else + my_cxx_stl := ndk + endif +else + my_cxx_stl := $(strip $(LOCAL_CXX_STL)) + ifdef LOCAL_SDK_VERSION + # The NDK has historically used LOCAL_NDK_STL_VARIANT to specify the + # STL. An Android.mk that specifies both LOCAL_CXX_STL and + # LOCAL_SDK_VERSION will incorrectly try (and most likely fail) to use + # the platform STL in an NDK binary. Emit an error to direct the user + # toward the correct option. + # + # Note that we could also accept LOCAL_CXX_STL as an alias for + # LOCAL_NDK_STL_VARIANT (and in fact soong does use the same name), but + # the two options use different names for the STLs. + $(error $(LOCAL_PATH): $(LOCAL_MODULE): Must use LOCAL_NDK_STL_VARIANT rather than LOCAL_CXX_STL for NDK binaries) + endif +endif + +my_link_type := dynamic +ifdef LOCAL_IS_HOST_MODULE + ifneq (,$(BUILD_HOST_static)) + my_link_type := static + endif + ifeq (-static,$(filter -static,$(my_ldflags))) + my_link_type := static + endif +else + ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE)) + my_link_type := static + endif +endif + +my_cxx_ldlibs := +ifneq ($(filter $(my_cxx_stl),libc++ libc++_static),) + ifeq ($($(my_prefix)OS),darwin) + # libc++'s headers are annotated with availability macros that indicate + # which version of Mac OS was the first to ship with a libc++ feature + # available in its *system's* libc++.dylib. We do not use the system's + # library, but rather ship our own. As such, these availability + # attributes are meaningless for us but cause build breaks when we try + # to use code that would not be available in the system's dylib. + my_cppflags += -D_LIBCPP_DISABLE_AVAILABILITY + endif + + # Note that the structure of this means that LOCAL_CXX_STL := libc++ will + # use the static libc++ for static executables. + ifeq ($(my_link_type),dynamic) + ifeq ($(my_cxx_stl),libc++) + my_shared_libraries += libc++ + else + my_static_libraries += libc++_static + endif + else + my_static_libraries += libc++_static + endif + + ifdef LOCAL_IS_HOST_MODULE + my_cppflags += -nostdinc++ + my_ldflags += -nostdlib++ + else + my_static_libraries += libc++demangle + + ifeq ($(my_link_type),static) + my_static_libraries += libm libc libunwind + endif + endif +else ifeq ($(my_cxx_stl),ndk) + # Using an NDK STL. Handled in binary.mk. +else ifeq ($(my_cxx_stl),libstdc++) + $(error $(LOCAL_PATH): $(LOCAL_MODULE): libstdc++ is not supported) +else ifeq ($(my_cxx_stl),none) + ifdef LOCAL_IS_HOST_MODULE + my_cppflags += -nostdinc++ + my_ldflags += -nostdlib++ + endif +else + $(error $(LOCAL_PATH): $(LOCAL_MODULE): $(my_cxx_stl) is not a supported STL.) +endif diff --git a/make/core/definitions.mk b/make/core/definitions.mk new file mode 100644 index 0000000..0c46de9 --- /dev/null +++ b/make/core/definitions.mk @@ -0,0 +1,3847 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +## +## Common build system definitions. Mostly standard +## commands for building various types of targets, which +## are used by others to construct the final targets. +## + +# These are variables we use to collect overall lists +# of things being processed. + +# Full paths to all of the documentation +ALL_DOCS:= + +# The short names of all of the targets in the system. +# For each element of ALL_MODULES, two other variables +# are defined: +# $(ALL_MODULES.$(target)).BUILT +# $(ALL_MODULES.$(target)).INSTALLED +# The BUILT variable contains LOCAL_BUILT_MODULE for that +# target, and the INSTALLED variable contains the LOCAL_INSTALLED_MODULE. +# Some targets may have multiple files listed in the BUILT and INSTALLED +# sub-variables. +ALL_MODULES:= + +# The relative paths of the non-module targets in the system. +ALL_NON_MODULES:= +NON_MODULES_WITHOUT_LICENSE_METADATA:= + +# Full paths to targets that should be added to the "make droid" +# set of installed targets. +ALL_DEFAULT_INSTALLED_MODULES:= + +# The list of tags that have been defined by +# LOCAL_MODULE_TAGS. Each word in this variable maps +# to a corresponding ALL_MODULE_TAGS. variable +# that contains all of the INSTALLED_MODULEs with that tag. +ALL_MODULE_TAGS:= + +# Similar to ALL_MODULE_TAGS, but contains the short names +# of all targets for a particular tag. The top-level variable +# won't have the list of tags; ust ALL_MODULE_TAGS to get +# the list of all known tags. (This means that this variable +# will always be empty; it's just here as a placeholder for +# its sub-variables.) +ALL_MODULE_NAME_TAGS:= + +# Full path to all asm, C, C++, lex and yacc generated C files. +# These all have an order-only dependency on the copied headers +ALL_C_CPP_ETC_OBJECTS:= + +# These files go into the SDK +ALL_SDK_FILES:= + +# Files for dalvik. This is often build without building the rest of the OS. +INTERNAL_DALVIK_MODULES:= + +# All findbugs xml files +ALL_FINDBUGS_FILES:= + +# GPL module license files +ALL_GPL_MODULE_LICENSE_FILES:= + +# Packages with certificate violation +CERTIFICATE_VIOLATION_MODULES := + +# Target and host installed module's dependencies on shared libraries. +# They are list of "::lib1,lib2...". +TARGET_DEPENDENCIES_ON_SHARED_LIBRARIES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_DEPENDENCIES_ON_SHARED_LIBRARIES := +HOST_DEPENDENCIES_ON_SHARED_LIBRARIES := +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_DEPENDENCIES_ON_SHARED_LIBRARIES := +HOST_CROSS_DEPENDENCIES_ON_SHARED_LIBRARIES := +$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_DEPENDENCIES_ON_SHARED_LIBRARIES := + +# Generated class file names for Android resource. +# They are escaped and quoted so can be passed safely to a bash command. +ANDROID_RESOURCE_GENERATED_CLASSES := 'R.class' 'R$$*.class' 'Manifest.class' 'Manifest$$*.class' + +# Display names for various build targets +TARGET_DISPLAY := target +HOST_DISPLAY := host +HOST_CROSS_DISPLAY := host cross + +# All installed initrc files +ALL_INIT_RC_INSTALLED_PAIRS := + +# All installed vintf manifest fragments for a partition at +ALL_VINTF_MANIFEST_FRAGMENTS_LIST:= + +# All tests that should be skipped in presubmit check. +ALL_DISABLED_PRESUBMIT_TESTS := + +# All compatibility suites mentioned in LOCAL_COMPATIBILITY_SUITE +ALL_COMPATIBILITY_SUITES := + +# All compatibility suite files to dist. +ALL_COMPATIBILITY_DIST_FILES := + +# All LINK_TYPE entries +ALL_LINK_TYPES := + +# All exported/imported include entries +EXPORTS_LIST := + +# All modules already converted to Soong +SOONG_ALREADY_CONV := + +# ALL_DEPS.*.ALL_DEPS keys +ALL_DEPS.MODULES := + +########################################################### +## Debugging; prints a variable list to stdout +########################################################### + +# $(1): variable name list, not variable values +define print-vars +$(foreach var,$(1), \ + $(info $(var):) \ + $(foreach word,$($(var)), \ + $(info $(space)$(space)$(word)) \ + ) \ + ) +endef + +########################################################### +## Evaluates to true if the string contains the word true, +## and empty otherwise +## $(1): a var to test +########################################################### + +define true-or-empty +$(filter true, $(1)) +endef + +########################################################### +## Rule for touching GCNO files. +########################################################### +define gcno-touch-rule +$(2): $(1) + touch -c $$@ +endef + +########################################################### + +########################################################### +## Retrieve the directory of the current makefile +## Must be called before including any other makefile!! +########################################################### + +# Figure out where we are. +define my-dir +$(strip \ + $(eval LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST))) \ + $(if $(filter $(BUILD_SYSTEM)/% $(OUT_DIR)/%,$(LOCAL_MODULE_MAKEFILE)), \ + $(error my-dir must be called before including any other makefile.) \ + , \ + $(patsubst %/,%,$(dir $(LOCAL_MODULE_MAKEFILE))) \ + ) \ + ) +endef + + +########################################################### +## Retrieve a list of all makefiles immediately below some directory +########################################################### + +define all-makefiles-under +$(wildcard $(1)/*/Android.mk) +endef + +########################################################### +## Look under a directory for makefiles that don't have parent +## makefiles. +########################################################### + +# $(1): directory to search under +# Ignores $(1)/Android.mk +define first-makefiles-under +$(shell build/make/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) \ + --mindepth=2 $(addprefix --dir=,$(1)) Android.mk) +endef + +########################################################### +## Retrieve a list of all makefiles immediately below your directory +## Must be called before including any other makefile!! +########################################################### + +define all-subdir-makefiles +$(call all-makefiles-under,$(call my-dir)) +endef + +########################################################### +## Look in the named list of directories for makefiles, +## relative to the current directory. +## Must be called before including any other makefile!! +########################################################### + +# $(1): List of directories to look for under this directory +define all-named-subdir-makefiles +$(wildcard $(addsuffix /Android.mk, $(addprefix $(call my-dir)/,$(1)))) +endef + +########################################################### +## Find all of the directories under the named directories with +## the specified name. +## Meant to be used like: +## INC_DIRS := $(call all-named-dirs-under,inc,.) +########################################################### + +define all-named-dirs-under +$(call find-subdir-files,$(2) -type d -name "$(1)") +endef + +########################################################### +## Find all the directories under the current directory that +## haves name that match $(1) +########################################################### + +define all-subdir-named-dirs +$(call all-named-dirs-under,$(1),.) +endef + +########################################################### +## Find all of the files under the named directories with +## the specified name. +## Meant to be used like: +## SRC_FILES := $(call all-named-files-under,*.h,src tests) +########################################################### + +define all-named-files-under +$(call find-files-in-subdirs,$(LOCAL_PATH),"$(1)",$(2)) +endef + +########################################################### +## Find all of the files under the current directory with +## the specified name. +########################################################### + +define all-subdir-named-files +$(call all-named-files-under,$(1),.) +endef + +########################################################### +## Find all of the java files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-java-files-under,src tests) +########################################################### + +define all-java-files-under +$(call all-named-files-under,*.java,$(1)) +endef + +########################################################### +## Find all of the java files from here. Meant to be used like: +## SRC_FILES := $(call all-subdir-java-files) +########################################################### + +define all-subdir-java-files +$(call all-java-files-under,.) +endef + +########################################################### +## Find all of the c files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-c-files-under,src tests) +########################################################### + +define all-c-files-under +$(call all-named-files-under,*.c,$(1)) +endef + +########################################################### +## Find all of the c files from here. Meant to be used like: +## SRC_FILES := $(call all-subdir-c-files) +########################################################### + +define all-subdir-c-files +$(call all-c-files-under,.) +endef + +########################################################### +## Find all of the cpp files under the named directories. +## LOCAL_CPP_EXTENSION is respected if set. +## Meant to be used like: +## SRC_FILES := $(call all-cpp-files-under,src tests) +########################################################### + +define all-cpp-files-under +$(sort $(patsubst ./%,%, \ + $(shell cd $(LOCAL_PATH) ; \ + find -L $(1) -name "*$(or $(LOCAL_CPP_EXTENSION),.cpp)" -and -not -name ".*") \ + )) +endef + +########################################################### +## Find all of the cpp files from here. Meant to be used like: +## SRC_FILES := $(call all-subdir-cpp-files) +########################################################### + +define all-subdir-cpp-files +$(call all-cpp-files-under,.) +endef + +########################################################### +## Find all files named "I*.aidl" under the named directories, +## which must be relative to $(LOCAL_PATH). The returned list +## is relative to $(LOCAL_PATH). +########################################################### + +define all-Iaidl-files-under +$(call all-named-files-under,I*.aidl,$(1)) +endef + +########################################################### +## Find all of the "I*.aidl" files under $(LOCAL_PATH). +########################################################### + +define all-subdir-Iaidl-files +$(call all-Iaidl-files-under,.) +endef + +########################################################### +## Find all files named "*.vts" under the named directories, +## which must be relative to $(LOCAL_PATH). The returned list +## is relative to $(LOCAL_PATH). +########################################################### + +define all-vts-files-under +$(call all-named-files-under,*.vts,$(1)) +endef + +########################################################### +## Find all of the "*.vts" files under $(LOCAL_PATH). +########################################################### + +define all-subdir-vts-files +$(call all-vts-files-under,.) +endef + +########################################################### +## Find all of the logtags files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-logtags-files-under,src) +########################################################### + +define all-logtags-files-under +$(call all-named-files-under,*.logtags,$(1)) +endef + +########################################################### +## Find all of the .proto files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-proto-files-under,src) +########################################################### + +define all-proto-files-under +$(call all-named-files-under,*.proto,$(1)) +endef + +########################################################### +## Find all of the RenderScript files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-renderscript-files-under,src) +########################################################### + +define all-renderscript-files-under +$(call find-subdir-files,$(1) \( -name "*.rscript" -or -name "*.fs" \) -and -not -name ".*") +endef + +########################################################### +## Find all of the S files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-c-files-under,src tests) +########################################################### + +define all-S-files-under +$(call all-named-files-under,*.S,$(1)) +endef + +########################################################### +## Find all of the html files under the named directories. +## Meant to be used like: +## SRC_FILES := $(call all-html-files-under,src tests) +########################################################### + +define all-html-files-under +$(call all-named-files-under,*.html,$(1)) +endef + +########################################################### +## Find all of the html files from here. Meant to be used like: +## SRC_FILES := $(call all-subdir-html-files) +########################################################### + +define all-subdir-html-files +$(call all-html-files-under,.) +endef + +########################################################### +## Find all of the files matching pattern +## SRC_FILES := $(call find-subdir-files, ) +########################################################### + +define find-subdir-files +$(sort $(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find -L $(1)))) +endef + +########################################################### +# find the files in the subdirectory $1 of LOCAL_DIR +# matching pattern $2, filtering out files $3 +# e.g. +# SRC_FILES += $(call find-subdir-subdir-files, \ +# css, *.cpp, DontWantThis.cpp) +########################################################### + +define find-subdir-subdir-files +$(sort $(filter-out $(patsubst %,$(1)/%,$(3)),$(patsubst ./%,%,$(shell cd \ + $(LOCAL_PATH) ; find -L $(1) -maxdepth 1 -name $(2))))) +endef + +########################################################### +## Find all of the files matching pattern +## SRC_FILES := $(call all-subdir-java-files) +########################################################### + +define find-subdir-assets +$(sort $(if $(1),$(patsubst ./%,%, \ + $(shell if [ -d $(1) ] ; then cd $(1) ; find -L ./ -not -name '.*' -and -type f ; fi)), \ + $(warning Empty argument supplied to find-subdir-assets in $(LOCAL_PATH)) \ +)) +endef + +########################################################### +## Find various file types in a list of directories relative to $(LOCAL_PATH) +########################################################### + +define find-other-java-files +$(call all-java-files-under,$(1)) +endef + +define find-other-html-files +$(call all-html-files-under,$(1)) +endef + +########################################################### +# Use utility find to find given files in the given subdirs. +# This function uses $(1), instead of LOCAL_PATH as the base. +# $(1): the base dir, relative to the root of the source tree. +# $(2): the file name pattern to be passed to find as "-name". +# $(3): a list of subdirs of the base dir. +# Returns: a list of paths relative to the base dir. +########################################################### + +define find-files-in-subdirs +$(sort $(patsubst ./%,%, \ + $(shell cd $(1) ; \ + find -L $(3) -name $(2) -and -not -name ".*") \ + )) +endef + +########################################################### +## Scan through each directory of $(1) looking for files +## that match $(2) using $(wildcard). Useful for seeing if +## a given directory or one of its parents contains +## a particular file. Returns the first match found, +## starting furthest from the root. +########################################################### + +define find-parent-file +$(strip \ + $(eval _fpf := $(sort $(wildcard $(foreach f, $(2), $(strip $(1))/$(f))))) \ + $(if $(_fpf),$(_fpf), \ + $(if $(filter-out ./ .,$(1)), \ + $(call find-parent-file,$(patsubst %/,%,$(dir $(1))),$(2)) \ + ) \ + ) \ +) +endef + +########################################################### +## Find test data in a form required by LOCAL_TEST_DATA +## $(1): the base dir, relative to the root of the source tree. +## $(2): the file name pattern to be passed to find as "-name" +## $(3): a list of subdirs of the base dir +########################################################### + +define find-test-data-in-subdirs +$(foreach f,$(sort $(patsubst ./%,%, \ + $(shell cd $(1) ; \ + find -L $(3) -type f -and -name $(2) -and -not -name ".*") \ +)),$(1):$(f)) +endef + +########################################################### +## Function we can evaluate to introduce a dynamic dependency +########################################################### + +define add-dependency +$(1): $(2) +endef + +########################################################### +## Reverse order of a list +########################################################### + +define reverse-list +$(if $(1),$(call reverse-list,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) +endef + +########################################################### +## Sometimes a notice dependency will reference an unadorned +## module name that only appears in ALL_MODULES adorned with +## an ARCH suffix or a `host_cross_` prefix. +## +## After all of the modules are processed in base_rules.mk, +## replace all such dependencies with every matching adorned +## module name. +########################################################### + +define fix-notice-deps +$(strip \ + $(eval _all_module_refs := \ + $(sort \ + $(foreach m,$(sort $(ALL_MODULES)), \ + $(call word-colon,1,$(ALL_MODULES.$(m).NOTICE_DEPS)) \ + ) \ + ) \ + ) \ + $(foreach m, $(_all_module_refs), \ + $(eval _lookup.$(m) := \ + $(sort \ + $(if $(strip $(ALL_MODULES.$(m).PATH)), \ + $(m), \ + $(filter $(m)_32 $(m)_64 host_cross_$(m) host_cross_$(m)_32 host_cross_$(m)_64, $(ALL_MODULES)) \ + ) \ + ) \ + ) \ + ) \ + $(foreach m, $(ALL_MODULES), \ + $(eval ALL_MODULES.$(m).NOTICE_DEPS := \ + $(sort \ + $(foreach d,$(sort $(ALL_MODULES.$(m).NOTICE_DEPS)), \ + $(foreach n,$(_lookup.$(call word-colon,1,$(d))),$(n):$(call wordlist-colon,2,9999,$(d))) \ + ) \ + ) \ + ) \ + ) \ +) +endef + +########################################################### +## Target directory for license metadata files. +########################################################### +define license-metadata-dir +$(call generated-sources-dir-for,META,lic,) +endef + +########################################################### +# License metadata targets corresponding to targets in $(1) +########################################################### +define corresponding-license-metadata +$(strip $(foreach target, $(sort $(1)), \ + $(if $(strip $(ALL_MODULES.$(target).META_LIC)), \ + $(ALL_MODULES.$(target).META_LIC), \ + $(if $(strip $(ALL_TARGETS.$(target).META_LIC)), \ + $(ALL_TARGETS.$(target).META_LIC), \ + $(call append-path,$(call license-metadata-dir),$(patsubst $(OUT_DIR)%,out%,$(target).meta_lic)))))) +endef + +########################################################### +## License metadata build rule for my_register_name $(1) +########################################################### +define license-metadata-rule +$(foreach meta_lic, $(ALL_MODULES.$(1).DELAYED_META_LIC),$(call _license-metadata-rule,$(1),$(meta_lic))) +endef + +$(KATI_obsolete_var notice-rule, This function has been removed) + +define _license-metadata-rule +$(strip $(eval _srcs := $(strip $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED)), $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT)), $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT), $(call word-colon,1,$d))))))) +$(strip $(eval _deps := $(sort $(filter-out $(2)%,\ + $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),\ + $(addsuffix :$(call wordlist-colon,2,9999,$(d)), \ + $(foreach dt,$(ALL_MODULES.$(d).BUILT) $(ALL_MODULES.$(d).INSTALLED),\ + $(ALL_TARGETS.$(dt).META_LIC)))))))) +$(strip $(eval _notices := $(sort $(ALL_MODULES.$(1).NOTICES)))) +$(strip $(eval _tgts := $(sort $(ALL_MODULES.$(1).BUILT)))) +$(strip $(eval _inst := $(sort $(ALL_MODULES.$(1).INSTALLED)))) +$(strip $(eval _path := $(sort $(ALL_MODULES.$(1).PATH)))) +$(strip $(eval _map := $(strip $(foreach _m,$(sort $(ALL_MODULES.$(1).LICENSE_INSTALL_MAP)), \ + $(eval _s := $(call word-colon,1,$(_m))) \ + $(eval _d := $(call word-colon,2,$(_m))) \ + $(eval _ns := $(if $(strip $(ALL_MODULES.$(_s).INSTALLED)),$(ALL_MODULES.$(_s).INSTALLED),$(if $(strip $(ALL_MODULES.$(_s).BUILT)),$(ALL_MODULES.$(_s).BUILT),$(_s)))) \ + $(foreach ns,$(_ns),$(ns):$(_d) ) \ +)))) + +$(2): PRIVATE_KINDS := $(sort $(ALL_MODULES.$(1).LICENSE_KINDS)) +$(2): PRIVATE_CONDITIONS := $(sort $(ALL_MODULES.$(1).LICENSE_CONDITIONS)) +$(2): PRIVATE_NOTICES := $(_notices) +$(2): PRIVATE_NOTICE_DEPS := $(_deps) +$(2): PRIVATE_SOURCES := $(_srcs) +$(2): PRIVATE_TARGETS := $(_tgts) +$(2): PRIVATE_INSTALLED := $(_inst) +$(2): PRIVATE_PATH := $(_path) +$(2): PRIVATE_IS_CONTAINER := $(ALL_MODULES.$(1).IS_CONTAINER) +$(2): PRIVATE_PACKAGE_NAME := $(strip $(ALL_MODULES.$(1).LICENSE_PACKAGE_NAME)) +$(2): PRIVATE_INSTALL_MAP := $(_map) +$(2): PRIVATE_MODULE_TYPE := $(ALL_MODULES.$(1).MODULE_TYPE) +$(2): PRIVATE_MODULE_CLASS := $(ALL_MODULES.$(1).MODULE_CLASS) +$(2): PRIVATE_INSTALL_MAP := $(_map) +$(2): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,notice)/$(2)/arguments +$(2): $(BUILD_LICENSE_METADATA) +$(2) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) ) + rm -f $$@ + mkdir -p $$(dir $$@) + mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) + $$(call dump-words-to-file,\ + $$(addprefix -mt ,$$(PRIVATE_MODULE_TYPE))\ + $$(addprefix -mc ,$$(PRIVATE_MODULE_CLASS))\ + $$(addprefix -k ,$$(PRIVATE_KINDS))\ + $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\ + $$(addprefix -n ,$$(PRIVATE_NOTICES))\ + $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS))\ + $$(addprefix -s ,$$(PRIVATE_SOURCES))\ + $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP))\ + $$(addprefix -t ,$$(PRIVATE_TARGETS))\ + $$(addprefix -i ,$$(PRIVATE_INSTALLED))\ + $$(addprefix -r ,$$(PRIVATE_PATH)),\ + $$(PRIVATE_ARGUMENT_FILE)) + OUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \ + $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \ + -p '$$(PRIVATE_PACKAGE_NAME)' \ + @$$(PRIVATE_ARGUMENT_FILE) \ + -o $$@ +endef + + +########################################################### +## License metadata build rule for non-module target $(1) +########################################################### +define non-module-license-metadata-rule +$(strip $(eval _dir := $(call license-metadata-dir))) +$(strip $(eval _tgt := $(strip $(1)))) +$(strip $(eval _meta := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(_tgt).meta_lic)))) +$(strip $(eval _deps := $(sort $(filter-out 0p: :,$(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)),$(ALL_TARGETS.$(call word-colon,1,$(d)).META_LIC):$(call wordlist-colon,2,9999,$(d))))))) +$(strip $(eval _notices := $(sort $(ALL_NON_MODULES.$(_tgt).NOTICES)))) +$(strip $(eval _path := $(sort $(ALL_NON_MODULES.$(_tgt).PATH)))) +$(strip $(eval _install_map := $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS))) +$(strip $(eval \ + $$(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)), \ + $$(if $$(strip $$(ALL_TARGETS.$$(d).META_LIC)), \ + , \ + $$(eval NON_MODULES_WITHOUT_LICENSE_METADATA += $$(d))) \ + )) \ +) + +$(_meta): PRIVATE_KINDS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_KINDS)) +$(_meta): PRIVATE_CONDITIONS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS)) +$(_meta): PRIVATE_NOTICES := $(_notices) +$(_meta): PRIVATE_NOTICE_DEPS := $(_deps) +$(_meta): PRIVATE_SOURCES := $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) +$(_meta): PRIVATE_TARGETS := $(_tgt) +$(_meta): PRIVATE_PATH := $(_path) +$(_meta): PRIVATE_IS_CONTAINER := $(ALL_NON_MODULES.$(_tgt).IS_CONTAINER) +$(_meta): PRIVATE_PACKAGE_NAME := $(strip $(ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME)) +$(_meta): PRIVATE_INSTALL_MAP := $(strip $(_install_map)) +$(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,notice)/$(_meta)/arguments +$(_meta): $(BUILD_LICENSE_METADATA) +$(_meta) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) ) + rm -f $$@ + mkdir -p $$(dir $$@) + mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) + $$(call dump-words-to-file,\ + $$(addprefix -k ,$$(PRIVATE_KINDS))\ + $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\ + $$(addprefix -n ,$$(PRIVATE_NOTICES))\ + $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS))\ + $$(addprefix -s ,$$(PRIVATE_SOURCES))\ + $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP))\ + $$(addprefix -t ,$$(PRIVATE_TARGETS))\ + $$(addprefix -r ,$$(PRIVATE_PATH)),\ + $$(PRIVATE_ARGUMENT_FILE)) + OUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \ + -mt raw -mc unknown \ + $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \ + $$(addprefix -r ,$$(PRIVATE_PATH)) \ + @$$(PRIVATE_ARGUMENT_FILE) \ + -o $$@ + +endef + +########################################################### +## Declare the license metadata for non-module target $(1). +## +## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0 +## $(3) -- license conditions e.g. notice by_exception_only +## $(4) -- license text filenames (notices) +## $(5) -- package name +## $(6) -- project path +########################################################### +define declare-license-metadata +$(strip \ + $(eval _tgt := $(subst //,/,$(strip $(1)))) \ + $(eval ALL_NON_MODULES += $(_tgt)) \ + $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \ + $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \ + $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \ + $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \ + $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \ +) +endef + +########################################################### +## Declare that non-module targets copied from project $(1) and +## optionally ending in $(2) have the following license +## metadata: +## +## $(3) -- license kinds e.g. SPDX-license-identifier-Apache-2.0 +## $(4) -- license conditions e.g. notice by_exception_only +## $(5) -- license text filenames (notices) +## $(6) -- package name +########################################################### +define declare-copy-files-license-metadata +$(strip \ + $(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(eval $(call declare-license-metadata,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair)),$(3),$(4),$(5),$(6),$(1)))) \ +) +endef + +########################################################### +## Declare the license metadata for non-module container-type target $(1). +## +## Container-type targets are targets like .zip files that +## merely aggregate other files. +## +## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0 +## $(3) -- license conditions e.g. notice by_exception_only +## $(4) -- license text filenames (notices) +## $(5) -- package name +## $(6) -- project path +########################################################### +define declare-container-license-metadata +$(strip \ + $(eval _tgt := $(subst //,/,$(strip $(1)))) \ + $(eval ALL_NON_MODULES += $(_tgt)) \ + $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \ + $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \ + $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \ + $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \ + $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \ + $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \ +) +endef + +########################################################### +## Declare that non-module target $(1) is a non-copyrightable file. +## +## e.g. an information-only file merely listing other files. +########################################################### +define declare-0p-target +$(strip \ + $(eval _tgt := $(subst //,/,$(strip $(1)))) \ + $(eval ALL_0P_TARGETS += $(_tgt)) \ +) +endef + +########################################################### +## Declare that non-module targets copied from project $(1) and +## optionally ending in $(2) are non-copyrightable files. +## +## e.g. an information-only file merely listing other files. +########################################################### +define declare-0p-copy-files +$(strip \ + $(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(eval $(call declare-0p-target,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair))))) \ +) +endef + +########################################################### +## Declare non-module target $(1) to have a first-party license +## (Android Apache 2.0) +## +## $(2) -- project path +########################################################### +define declare-1p-target +$(call declare-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2)) +endef + +########################################################### +## Declare that non-module targets copied from project $(1) and +## optionally ending in $(2) are first-party licensed +## (Android Apache 2.0) +########################################################### +define declare-1p-copy-files +$(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(call declare-1p-target,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair)),$(1))) +endef + +########################################################### +## Declare non-module container-type target $(1) to have a +## first-party license (Android Apache 2.0). +## +## Container-type targets are targets like .zip files that +## merely aggregate other files. +## +## $92) -- project path +########################################################### +define declare-1p-container +$(call declare-container-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2)) +endef + +########################################################### +## Declare license dependencies $(2) for non-module target $(1) +########################################################### +define declare-license-deps +$(strip \ + $(eval _tgt := $(strip $(1))) \ + $(eval ALL_NON_MODULES += $(_tgt)) \ + $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \ +) +endef + +########################################################### +## Declare license dependencies $(2) for non-module container-type target $(1) +## +## Container-type targets are targets like .zip files that +## merely aggregate other files. +## +## $(3) -- root mappings space-separated source:target +########################################################### +define declare-container-license-deps +$(strip \ + $(eval _tgt := $(strip $(1))) \ + $(eval ALL_NON_MODULES += $(_tgt)) \ + $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \ + $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \ + $(eval ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS := $(strip $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS) $(3))) \ +) +endef + +########################################################### +## Declares the rule to report targets with no license metadata. +########################################################### +define report-missing-licenses-rule +.PHONY: reportmissinglicenses +reportmissinglicenses: PRIVATE_NON_MODULES:=$(sort $(NON_MODULES_WITHOUT_LICENSE_METADATA)) +reportmissinglicenses: PRIVATE_COPIED_FILES:=$(sort $(filter $(NON_MODULES_WITHOUT_LICENSE_METADATA),$(foreach _pair,$(PRODUCT_COPY_FILES), $(PRODUCT_OUT)/$(call word-colon,2,$(_pair))))) +reportmissinglicenses: + @echo Reporting $$(words $$(PRIVATE_NON_MODULES)) targets without license metadata + $$(foreach t,$$(PRIVATE_NON_MODULES),if ! [ -h $$(t) ]; then echo No license metadata for $$(t) >&2; fi;) + $$(foreach t,$$(PRIVATE_COPIED_FILES),if ! [ -h $$(t) ]; then echo No license metadata for copied file $$(t) >&2; fi;) + +endef + + +########################################################### +# Returns the unique list of built license metadata files. +########################################################### +define all-license-metadata +$(sort \ + $(foreach t,$(ALL_NON_MODULES),$(if $(filter 0p,$(ALL_TARGETS.$(t).META_LIC)),, $(ALL_TARGETS.$(t).META_LIC))) \ + $(foreach m,$(ALL_MODULES), $(ALL_MODULES.$(m).META_LIC)) \ +) +endef + +########################################################### +# Declares the rule to report all library names used in any notice files. +########################################################### +define report-all-notice-library-names-rule +$(strip $(eval _all := $(call all-license-metadata))) + +.PHONY: reportallnoticelibrarynames +reportallnoticelibrarynames: PRIVATE_LIST_FILE := $(call license-metadata-dir)/filelist +reportallnoticelibrarynames: | $(COMPLIANCENOTICE_SHIPPEDLIBS) +reportallnoticelibrarynames: $(_all) + @echo Reporting notice library names for at least $$(words $(_all)) license metadata files + $(hide) rm -f $$(PRIVATE_LIST_FILE) + $(hide) mkdir -p $$(dir $$(PRIVATE_LIST_FILE)) + $(hide) find out -name '*meta_lic' -type f -printf '"%p"\n' >$$(PRIVATE_LIST_FILE) + OUT_DIR=$(OUT_DIR) $(COMPLIANCENOTICE_SHIPPEDLIBS) @$$(PRIVATE_LIST_FILE) +endef + +########################################################### +# Declares the rule to build all license metadata. +########################################################### +define build-all-license-metadata-rule +$(strip $(eval _all := $(call all-license-metadata))) + +.PHONY: alllicensemetadata +alllicensemetadata: $(_all) + @echo Building all $(words $(_all)) license metadata files +endef + + +########################################################### +## Declares a license metadata build rule for ALL_MODULES +########################################################### +define build-license-metadata +$(strip \ + $(strip $(eval _dir := $(call license-metadata-dir))) \ + $(foreach t,$(sort $(ALL_0P_TARGETS)), \ + $(eval ALL_TARGETS.$(t).META_LIC := 0p) \ + ) \ + $(foreach t,$(sort $(ALL_NON_MODULES)), \ + $(eval ALL_TARGETS.$(t).META_LIC := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(t).meta_lic))) \ + ) \ + $(foreach t,$(sort $(ALL_NON_MODULES)),$(eval $(call non-module-license-metadata-rule,$(t)))) \ + $(foreach m,$(sort $(ALL_MODULES)),$(eval $(call license-metadata-rule,$(m)))) \ + $(eval $(call report-missing-licenses-rule)) \ + $(eval $(call report-all-notice-library-names-rule)) \ + $(eval $(call build-all-license-metadata-rule))) +endef + +########################################################### +## Returns correct _idfPrefix from the list: +## { HOST, HOST_CROSS, TARGET } +########################################################### +# the following rules checked in order: +# ($1 is in {HOST_CROSS} => $1; +# ($1 is empty) => TARGET; +# ($2 is not empty) => HOST_CROSS; +# => HOST; +define find-idf-prefix +$(strip \ + $(eval _idf_pfx_:=$(strip $(filter HOST_CROSS,$(1)))) \ + $(eval _idf_pfx_:=$(if $(strip $(1)),$(if $(_idf_pfx_),$(_idf_pfx_),$(if $(strip $(2)),HOST_CROSS,HOST)),TARGET)) \ + $(_idf_pfx_) +) +endef + +########################################################### +## The intermediates directory. Where object files go for +## a given target. We could technically get away without +## the "_intermediates" suffix on the directory, but it's +## nice to be able to grep for that string to find out if +## anyone's abusing the system. +########################################################### + +# $(1): target class, like "APPS" +# $(2): target name, like "NotePad" +# $(3): { HOST, HOST_CROSS, , } +# $(4): if non-empty, force the intermediates to be COMMON +# $(5): if non-empty, force the intermediates to be for the 2nd arch +# $(6): if non-empty, force the intermediates to be for the host cross os +define intermediates-dir-for +$(strip \ + $(eval _idfClass := $(strip $(1))) \ + $(if $(_idfClass),, \ + $(error $(LOCAL_PATH): Class not defined in call to intermediates-dir-for)) \ + $(eval _idfName := $(strip $(2))) \ + $(if $(_idfName),, \ + $(error $(LOCAL_PATH): Name not defined in call to intermediates-dir-for)) \ + $(eval _idfPrefix := $(call find-idf-prefix,$(3),$(6))) \ + $(eval _idf2ndArchPrefix := $(if $(strip $(5)),$(TARGET_2ND_ARCH_VAR_PREFIX))) \ + $(if $(filter $(_idfPrefix)_$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \ + $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_INTERMEDIATES)) \ + ,$(if $(filter $(_idfClass),$(PER_ARCH_MODULE_CLASSES)),\ + $(eval _idfIntBase := $($(_idf2ndArchPrefix)$(_idfPrefix)_OUT_INTERMEDIATES)) \ + ,$(eval _idfIntBase := $($(_idfPrefix)_OUT_INTERMEDIATES)) \ + ) \ + ) \ + $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \ +) +endef + +# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE +# to determine the intermediates directory. +# +# $(1): if non-empty, force the intermediates to be COMMON +# $(2): if non-empty, force the intermediates to be for the 2nd arch +# $(3): if non-empty, force the intermediates to be for the host cross os +define local-intermediates-dir +$(strip \ + $(if $(strip $(LOCAL_MODULE_CLASS)),, \ + $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-intermediates-dir)) \ + $(if $(strip $(LOCAL_MODULE)),, \ + $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-intermediates-dir)) \ + $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1),$(2),$(3)) \ +) +endef + +########################################################### +## The generated sources directory. Placing generated +## source files directly in the intermediates directory +## causes problems for multiarch builds, where there are +## two intermediates directories for a single target. Put +## them in a separate directory, and they will be copied to +## each intermediates directory automatically. +########################################################### + +# $(1): target class, like "APPS" +# $(2): target name, like "NotePad" +# $(3): { HOST, HOST_CROSS, , } +# $(4): if non-empty, force the generated sources to be COMMON +define generated-sources-dir-for +$(strip \ + $(eval _idfClass := $(strip $(1))) \ + $(if $(_idfClass),, \ + $(error $(LOCAL_PATH): Class not defined in call to generated-sources-dir-for)) \ + $(eval _idfName := $(strip $(2))) \ + $(if $(_idfName),, \ + $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \ + $(eval _idfPrefix := $(call find-idf-prefix,$(3),)) \ + $(if $(filter $(_idfPrefix)_$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \ + $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_GEN)) \ + , \ + $(eval _idfIntBase := $($(_idfPrefix)_OUT_GEN)) \ + ) \ + $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \ +) +endef + +# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE +# to determine the generated sources directory. +# +# $(1): if non-empty, force the intermediates to be COMMON +define local-generated-sources-dir +$(strip \ + $(if $(strip $(LOCAL_MODULE_CLASS)),, \ + $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \ + $(if $(strip $(LOCAL_MODULE)),, \ + $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \ + $(call generated-sources-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1)) \ +) +endef + +########################################################### +## The packaging directory for a module. Similar to intermedates, but +## in a location that will be wiped by an m installclean. +########################################################### + +# $(1): subdir in PACKAGING +# $(2): target class, like "APPS" +# $(3): target name, like "NotePad" +# $(4): { HOST, HOST_CROSS, , } +define packaging-dir-for +$(strip \ + $(eval _pdfClass := $(strip $(2))) \ + $(if $(_pdfClass),, \ + $(error $(LOCAL_PATH): Class not defined in call to generated-sources-dir-for)) \ + $(eval _pdfName := $(strip $(3))) \ + $(if $(_pdfName),, \ + $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \ + $(call intermediates-dir-for,PACKAGING,$(1),$(4))/$(_pdfClass)/$(_pdfName)_intermediates \ +) +endef + +# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE +# to determine the packaging directory. +# +# $(1): subdir in PACKAGING +define local-packaging-dir +$(strip \ + $(if $(strip $(LOCAL_MODULE_CLASS)),, \ + $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \ + $(if $(strip $(LOCAL_MODULE)),, \ + $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \ + $(call packaging-dir-for,$(1),$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST)) \ +) +endef + + +########################################################### +## Convert a list of short module names (e.g., "framework", "Browser") +## into the list of files that are built for those modules. +## NOTE: this won't return reliable results until after all +## sub-makefiles have been included. +## $(1): target list +########################################################### + +define module-built-files +$(foreach module,$(1),$(ALL_MODULES.$(module).BUILT)) +endef + +########################################################### +## Convert a list of short modules names (e.g., "framework", "Browser") +## into the list of files that are installed for those modules. +## NOTE: this won't return reliable results until after all +## sub-makefiles have been included. +## $(1): target list +########################################################### + +define module-installed-files +$(foreach module,$(1),$(ALL_MODULES.$(module).INSTALLED)) +endef + +########################################################### +## Convert a list of short modules names (e.g., "framework", "Browser") +## into the list of files that are built *for the target* for those modules. +## NOTE: this won't return reliable results until after all +## sub-makefiles have been included. +## $(1): target list +########################################################### + +define module-target-built-files +$(foreach module,$(1),$(ALL_MODULES.$(module).TARGET_BUILT)) +endef + +########################################################### +## Convert a list of short modules names (e.g., "framework", "Browser") +## into the list of files that should be used when linking +## against that module as a public API. +## TODO: Allow this for more than JAVA_LIBRARIES modules +## NOTE: this won't return reliable results until after all +## sub-makefiles have been included. +## $(1): target list +########################################################### + +define module-stubs-files +$(foreach module,$(1),$(if $(filter $(module),$(JAVA_SDK_LIBRARIES)),\ +$(call java-lib-files,$(module).stubs),$(ALL_MODULES.$(module).STUBS))) +endef + +########################################################### +## Evaluates to the timestamp file for a doc module, which +## is the dependency that should be used. +## $(1): doc module +########################################################### + +define doc-timestamp-for +$(OUT_DOCS)/$(strip $(1))-timestamp +endef + + +########################################################### +## Convert "core ext framework" to "out/.../javalib.jar ..." +## $(1): library list +## $(2): Non-empty if IS_HOST_MODULE +########################################################### + +# Get the jar files (you can pass to "javac -classpath") of static or shared +# Java libraries that you want to link against. +# $(1): library name list +# $(2): Non-empty if IS_HOST_MODULE +define java-lib-files +$(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),$(2),COMMON)/classes.jar) +endef + +# Get the header jar files (you can pass to "javac -classpath") of static or shared +# Java libraries that you want to link against. +# $(1): library name list +# $(2): Non-empty if IS_HOST_MODULE +ifneq ($(TURBINE_ENABLED),false) +define java-lib-header-files +$(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),$(2),COMMON)/classes-header.jar) +endef +else +define java-lib-header-files +$(call java-lib-files,$(1),$(2)) +endef +endif + +# Get the dependency files (you can put on the right side of "|" of a build rule) +# of the Java libraries. +# $(1): library name list +# $(2): Non-empty if IS_HOST_MODULE +# Historically for target Java libraries we used a different file (javalib.jar) +# as the dependency. +# Now we can use classes.jar as dependency, so java-lib-deps is the same +# as java-lib-files. +define java-lib-deps +$(call java-lib-files,$(1),$(2)) +endef + +# Get the jar files (you can pass to "javac -classpath") of static or shared +# APK libraries that you want to link against. +# $(1): library name list +define app-lib-files +$(foreach lib,$(1),$(call intermediates-dir-for,APPS,$(lib),,COMMON)/classes.jar) +endef + +# Get the header jar files (you can pass to "javac -classpath") of static or shared +# APK libraries that you want to link against. +# $(1): library name list +ifneq ($(TURBINE_ENABLED),false) +define app-lib-header-files +$(foreach lib,$(1),$(call intermediates-dir-for,APPS,$(lib),,COMMON)/classes-header.jar) +endef +else +define app-lib-header-files +$(call app-lib-files,$(1)) +endef +endif + +# Get the exported-sdk-libs files which collectively give you the list of exported java sdk +# lib names that are (transitively) exported from the given set of java libs +# $(1): library name list +define exported-sdk-libs-files +$(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/exported-sdk-libs) +endef + +########################################################### +## MODULE_TAG set operations +########################################################### + +# Given a list of tags, return the targets that specify +# any of those tags. +# $(1): tag list +define modules-for-tag-list +$(sort $(foreach tag,$(1),$(foreach m,$(ALL_MODULE_NAME_TAGS.$(tag)),$(ALL_MODULES.$(m).INSTALLED)))) +endef + +# Same as modules-for-tag-list, but operates on +# ALL_MODULE_NAME_TAGS. +# $(1): tag list +define module-names-for-tag-list +$(sort $(foreach tag,$(1),$(ALL_MODULE_NAME_TAGS.$(tag)))) +endef + +# Given an accept and reject list, find the matching +# set of targets. If a target has multiple tags and +# any of them are rejected, the target is rejected. +# Reject overrides accept. +# $(1): list of tags to accept +# $(2): list of tags to reject +#TODO(dbort): do $(if $(strip $(1)),$(1),$(ALL_MODULE_TAGS)) +#TODO(jbq): as of 20100106 nobody uses the second parameter +define get-tagged-modules +$(filter-out \ + $(call modules-for-tag-list,$(2)), \ + $(call modules-for-tag-list,$(1))) +endef + +########################################################### +## Append a leaf to a base path. Properly deals with +## base paths ending in /. +## +## $(1): base path +## $(2): leaf path +########################################################### + +define append-path +$(subst //,/,$(1)/$(2)) +endef + + +########################################################### +## Color-coded warnings and errors +## Use echo-(warning|error) in a build rule +## Use pretty-(warning|error) instead of $(warning)/$(error) +########################################################### +ESC_BOLD := \033[1m +ESC_WARNING := \033[35m +ESC_ERROR := \033[31m +ESC_RESET := \033[0m + +# $(1): path (and optionally line) information +# $(2): message to print +define echo-warning +echo -e "$(ESC_BOLD)$(1): $(ESC_WARNING)warning:$(ESC_RESET)$(ESC_BOLD)" '$(subst ','\'',$(2))' "$(ESC_RESET)" >&2 +endef + +# $(1): path (and optionally line) information +# $(2): message to print +define echo-error +echo -e "$(ESC_BOLD)$(1): $(ESC_ERROR)error:$(ESC_RESET)$(ESC_BOLD)" '$(subst ','\'',$(2))' "$(ESC_RESET)" >&2 +endef + +########################################################### +## Legacy showcommands compatibility +########################################################### + +define pretty +@echo $1 +endef + +########################################################### +## Commands for including the dependency files the compiler generates +########################################################### +# $(1): the .P file +# $(2): the main build target +define include-depfile +$(eval $(2) : .KATI_DEPFILE := $1) +endef + +# $(1): object files +define include-depfiles-for-objs +$(foreach obj, $(1), $(call include-depfile, $(obj:%.o=%.d), $(obj))) +endef + +########################################################### +## Track source files compiled to objects +########################################################### +# $(1): list of sources +# $(2): list of matching objects +define track-src-file-obj +$(eval $(call _track-src-file-obj,$(1))) +endef +define _track-src-file-obj +i := w +$(foreach s,$(1), +my_tracked_src_files += $(s) +my_src_file_obj_$(s) := $$(word $$(words $$(i)),$$(2)) +i += w) +endef + +# $(1): list of sources +# $(2): list of matching generated sources +define track-src-file-gen +$(eval $(call _track-src-file-gen,$(2))) +endef +define _track-src-file-gen +i := w +$(foreach s,$(1), +my_tracked_gen_files += $(s) +my_src_file_gen_$(s) := $$(word $$(words $$(i)),$$(1)) +i += w) +endef + +# $(1): list of generated sources +# $(2): list of matching objects +define track-gen-file-obj +$(call track-src-file-obj,$(foreach f,$(1),\ + $(or $(my_src_file_gen_$(f)),$(f))),$(2)) +endef + +########################################################### +## Commands for running lex +########################################################### + +define transform-l-to-c-or-cpp +@echo "Lex: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +M4=$(M4) $(LEX) -o$@ $< +endef + +########################################################### +## Commands for running yacc +## +########################################################### + +define transform-y-to-c-or-cpp +@echo "Yacc: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +M4=$(M4) $(YACC) $(PRIVATE_YACCFLAGS) \ + --defines=$(basename $@).h \ + -o $@ $< +endef + +########################################################### +## Commands to compile RenderScript to Java +########################################################### + +## Merge multiple .d files generated by llvm-rs-cc. This is necessary +## because ninja can handle only a single depfile per build target. +## .d files generated by llvm-rs-cc define .stamp, .bc, and optionally +## .java as build targets. However, there's no way to let ninja know +## dependencies to .bc files and .java files, so we give up build +## targets for them. As we write the .stamp file as the target by +## ourselves, the awk script removes the first lines before the colon +## and append a backslash to the last line to concatenate contents of +## multiple files. +# $(1): .d files to be merged +# $(2): merged .d file +define _merge-renderscript-d +$(hide) echo '$@: $(backslash)' > $2 +$(foreach d,$1, \ + $(hide) awk 'start { sub(/( \\)?$$/, " \\"); print } /:/ { start=1 }' < $d >> $2$(newline)) +$(hide) echo >> $2 +endef + +# b/37755219 +RS_CC_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0:detect_container_overflow=0 + +define transform-renderscripts-to-java-and-bc +@echo "RenderScript: $(PRIVATE_MODULE) <= $(PRIVATE_RS_SOURCE_FILES)" +$(hide) rm -rf $(PRIVATE_RS_OUTPUT_DIR) +$(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/res/raw +$(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/src +$(hide) $(RS_CC_ASAN_OPTIONS) $(PRIVATE_RS_CC) \ + -o $(PRIVATE_RS_OUTPUT_DIR)/res/raw \ + -p $(PRIVATE_RS_OUTPUT_DIR)/src \ + -d $(PRIVATE_RS_OUTPUT_DIR) \ + -a $@ -MD \ + $(addprefix -target-api , $(PRIVATE_RS_TARGET_API)) \ + $(PRIVATE_RS_FLAGS) \ + $(foreach inc,$(PRIVATE_RS_INCLUDES),$(addprefix -I , $(inc))) \ + $(PRIVATE_RS_SOURCE_FILES) +$(SOONG_ZIP) -o $@ -C $(PRIVATE_RS_OUTPUT_DIR)/src -D $(PRIVATE_RS_OUTPUT_DIR)/src +$(SOONG_ZIP) -o $(PRIVATE_RS_OUTPUT_RES_ZIP) -C $(PRIVATE_RS_OUTPUT_DIR)/res -D $(PRIVATE_RS_OUTPUT_DIR)/res +$(call _merge-renderscript-d,$(PRIVATE_DEP_FILES),$@.d) +endef + +define transform-bc-to-so +@echo "Renderscript compatibility: $(notdir $@) <= $(notdir $<)" +$(hide) mkdir -p $(dir $@) +$(hide) $(BCC_COMPAT) -O3 -o $(dir $@)/$(notdir $(<:.bc=.o)) -fPIC -shared \ + -rt-path $(RS_PREBUILT_CLCORE) -mtriple $(RS_COMPAT_TRIPLE) $< +$(hide) $(PRIVATE_CXX_LINK) -fuse-ld=lld -target $(CLANG_TARGET_TRIPLE) -shared -Wl,-soname,$(notdir $@) -nostdlib \ + -Wl,-rpath,\$$ORIGIN/../lib \ + $(dir $@)/$(notdir $(<:.bc=.o)) \ + $(RS_PREBUILT_COMPILER_RT) \ + -o $@ $(CLANG_TARGET_GLOBAL_LLDFLAGS) -Wl,--hash-style=sysv \ + -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib64 \ + -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib \ + $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so \ + -lm -lc +endef + +########################################################### +## Commands to compile RenderScript to C++ +########################################################### + +define transform-renderscripts-to-cpp-and-bc +@echo "RenderScript: $(PRIVATE_MODULE) <= $(PRIVATE_RS_SOURCE_FILES)" +$(hide) rm -rf $(PRIVATE_RS_OUTPUT_DIR) +$(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/ +$(hide) $(RS_CC_ASAN_OPTIONS) $(PRIVATE_RS_CC) \ + -o $(PRIVATE_RS_OUTPUT_DIR)/ \ + -d $(PRIVATE_RS_OUTPUT_DIR) \ + -a $@ -MD \ + -reflect-c++ \ + $(addprefix -target-api , $(PRIVATE_RS_TARGET_API)) \ + $(PRIVATE_RS_FLAGS) \ + $(addprefix -I , $(PRIVATE_RS_INCLUDES)) \ + $(PRIVATE_RS_SOURCE_FILES) +$(call _merge-renderscript-d,$(PRIVATE_DEP_FILES),$@.d) +$(hide) mkdir -p $(dir $@) +$(hide) touch $@ +endef + + +########################################################### +## Commands for running aidl +########################################################### + +define transform-aidl-to-java +@mkdir -p $(dir $@) +@echo "Aidl: $(PRIVATE_MODULE) <= $<" +$(hide) $(AIDL) -d$(patsubst %.java,%.P,$@) $(PRIVATE_AIDL_FLAGS) $< $@ +endef +#$(AIDL) $(PRIVATE_AIDL_FLAGS) $< - | indent -nut -br -npcs -l1000 > $@ + +define transform-aidl-to-cpp +@mkdir -p $(dir $@) +@mkdir -p $(PRIVATE_HEADER_OUTPUT_DIR) +@echo "Generating C++ from AIDL: $(PRIVATE_MODULE) <= $<" +$(hide) $(AIDL_CPP) -d$(basename $@).aidl.d --ninja $(PRIVATE_AIDL_FLAGS) \ + $< $(PRIVATE_HEADER_OUTPUT_DIR) $@ +endef + +## Given a .aidl file path, generate the rule to compile it a .java file +# $(1): a .aidl source file +# $(2): a directory to place the generated .java files in +# $(3): name of a variable to add the path to the generated source file to +# +# You must call this with $(eval). +define define-aidl-java-rule +define-aidl-java-rule-src := $(patsubst %.aidl,%.java,$(subst ../,dotdot/,$(addprefix $(2)/,$(1)))) +$$(define-aidl-java-rule-src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL) + $$(transform-aidl-to-java) +$(3) += $$(define-aidl-java-rule-src) +endef + +## Given a .aidl file path generate the rule to compile it a .cpp file. +# $(1): a .aidl source file +# $(2): a directory to place the generated .cpp files in +# $(3): name of a variable to add the path to the generated source file to +# +# You must call this with $(eval). +define define-aidl-cpp-rule +define-aidl-cpp-rule-src := $(patsubst %.aidl,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1)))) +$$(define-aidl-cpp-rule-src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL_CPP) + $$(transform-aidl-to-cpp) +$(3) += $$(define-aidl-cpp-rule-src) +endef + +########################################################### +## Commands for running vts +########################################################### + +define transform-vts-to-cpp +@mkdir -p $(dir $@) +@mkdir -p $(PRIVATE_HEADER_OUTPUT_DIR) +@echo "Generating C++ from VTS: $(PRIVATE_MODULE) <= $<" +$(hide) $(VTSC) -TODO_b/120496070 $(PRIVATE_VTS_FLAGS) \ + $< $(PRIVATE_HEADER_OUTPUT_DIR) $@ +endef + +## Given a .vts file path generate the rule to compile it a .cpp file. +# $(1): a .vts source file +# $(2): a directory to place the generated .cpp files in +# $(3): name of a variable to add the path to the generated source file to +# +# You must call this with $(eval). +define define-vts-cpp-rule +define-vts-cpp-rule-src := $(patsubst %.vts,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1)))) +$$(define-vts-cpp-rule-src) : $(LOCAL_PATH)/$(1) $(VTSC) + $$(transform-vts-to-cpp) +$(3) += $$(define-vts-cpp-rule-src) +endef + +########################################################### +## Commands for running java-event-log-tags.py +########################################################### + +define transform-logtags-to-java +@mkdir -p $(dir $@) +@echo "logtags: $@ <= $<" +$(hide) $(JAVATAGS) -o $@ $< $(PRIVATE_MERGED_TAG) +endef + + +########################################################### +## Commands for running protoc to compile .proto into .java +########################################################### + +define transform-proto-to-java +@mkdir -p $(dir $@) +@echo "Protoc: $@ <= $(PRIVATE_PROTO_SRC_FILES)" +@rm -rf $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) +@mkdir -p $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) +$(hide) for f in $(PRIVATE_PROTO_SRC_FILES); do \ + $(PROTOC) \ + $(addprefix --proto_path=, $(PRIVATE_PROTO_INCLUDES)) \ + $(PRIVATE_PROTO_JAVA_OUTPUT_OPTION)="$(PRIVATE_PROTO_JAVA_OUTPUT_PARAMS):$(PRIVATE_PROTO_JAVA_OUTPUT_DIR)" \ + $(PRIVATE_PROTOC_FLAGS) \ + $$f || exit 33; \ + done +$(SOONG_ZIP) -o $@ -C $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) -D $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) +endef + +###################################################################### +## Commands for running protoc to compile .proto into .pb.cc (or.pb.c) and .pb.h +###################################################################### + +define transform-proto-to-cc +@echo "Protoc: $@ <= $<" +@mkdir -p $(dir $@) +$(hide) \ + $(PROTOC) \ + $(addprefix --proto_path=, $(PRIVATE_PROTO_INCLUDES)) \ + $(PRIVATE_PROTOC_FLAGS) \ + $< +@# aprotoc outputs only .cc. Rename it to .cpp if necessary. +$(if $(PRIVATE_RENAME_CPP_EXT),\ + $(hide) mv $(basename $@).cc $@) +endef + +########################################################### +## Helper to set include paths form transform-*-to-o +########################################################### +define c-includes +$(addprefix -I , $(PRIVATE_C_INCLUDES)) \ +$(foreach i,$(PRIVATE_IMPORTED_INCLUDES),$(EXPORTS.$(i)))\ +$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),,\ + $(addprefix -I ,\ + $(filter-out $(PRIVATE_C_INCLUDES), \ + $(PRIVATE_GLOBAL_C_INCLUDES))) \ + $(addprefix -isystem ,\ + $(filter-out $(PRIVATE_C_INCLUDES), \ + $(PRIVATE_GLOBAL_C_SYSTEM_INCLUDES)))) +endef + +########################################################### +## Commands for running gcc to compile a C++ file +########################################################### + +define transform-cpp-to-o-compiler-args +$(c-includes) \ +-c \ +$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_TARGET_GLOBAL_CFLAGS) \ + $(PRIVATE_TARGET_GLOBAL_CPPFLAGS) \ + $(PRIVATE_ARM_CFLAGS) \ + ) \ +$(PRIVATE_RTTI_FLAG) \ +$(PRIVATE_CFLAGS) \ +$(PRIVATE_CPPFLAGS) \ +$(PRIVATE_DEBUG_CFLAGS) \ +$(PRIVATE_CFLAGS_NO_OVERRIDE) \ +$(PRIVATE_CPPFLAGS_NO_OVERRIDE) +endef + +# PATH_TO_CLANG_TIDY is defined in build/soong +define call-clang-tidy +$(PATH_TO_CLANG_TIDY) \ + $(PRIVATE_TIDY_FLAGS) \ + -checks=$(PRIVATE_TIDY_CHECKS) +endef + +define clang-tidy-cpp +$(hide) $(call-clang-tidy) $< -- $(transform-cpp-to-o-compiler-args) +endef + +ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) +define transform-cpp-to-o +$(if $(PRIVATE_TIDY_CHECKS), + @echo "$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C++: $<" + $(clang-tidy-cpp)) +endef +else +define transform-cpp-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-cpp)) +$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \ + $(transform-cpp-to-o-compiler-args) \ + -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< +endef +endif + + +########################################################### +## Commands for running gcc to compile a C file +########################################################### + +# $(1): extra flags +define transform-c-or-s-to-o-compiler-args +$(c-includes) \ +-c \ +$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_TARGET_GLOBAL_CFLAGS) \ + $(PRIVATE_TARGET_GLOBAL_CONLYFLAGS) \ + $(PRIVATE_ARM_CFLAGS) \ + ) \ + $(1) +endef + +define transform-c-to-o-compiler-args +$(call transform-c-or-s-to-o-compiler-args, \ + $(PRIVATE_CFLAGS) \ + $(PRIVATE_CONLYFLAGS) \ + $(PRIVATE_DEBUG_CFLAGS) \ + $(PRIVATE_CFLAGS_NO_OVERRIDE)) +endef + +define clang-tidy-c +$(hide) $(call-clang-tidy) $< -- $(transform-c-to-o-compiler-args) +endef + +ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) +define transform-c-to-o +$(if $(PRIVATE_TIDY_CHECKS), + @echo "$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C: $<" + $(clang-tidy-c)) +endef +else +define transform-c-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-c)) +$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \ + $(transform-c-to-o-compiler-args) \ + -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< +endef +endif + +define transform-s-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +$(RELATIVE_PWD) $(PRIVATE_CC) \ + $(call transform-c-or-s-to-o-compiler-args, $(PRIVATE_ASFLAGS)) \ + -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< +endef + +# YASM compilation +define transform-asm-to-o +@mkdir -p $(dir $@) +$(hide) $(YASM) \ + $(addprefix -I , $(PRIVATE_C_INCLUDES)) \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_YASM_FLAGS) \ + $(PRIVATE_ASFLAGS) \ + -o $@ $< +endef + +########################################################### +## Commands for running gcc to compile an Objective-C file +## This should never happen for target builds but this +## will error at build time. +########################################################### + +define transform-m-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<" +$(call transform-c-or-s-to-o, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS)) +endef + +########################################################### +## Commands for running gcc to compile a host C++ file +########################################################### + +define transform-host-cpp-to-o-compiler-args +$(c-includes) \ +-c \ +$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_HOST_GLOBAL_CFLAGS) \ + $(PRIVATE_HOST_GLOBAL_CPPFLAGS) \ + ) \ +$(PRIVATE_CFLAGS) \ +$(PRIVATE_CPPFLAGS) \ +$(PRIVATE_DEBUG_CFLAGS) \ +$(PRIVATE_CFLAGS_NO_OVERRIDE) \ +$(PRIVATE_CPPFLAGS_NO_OVERRIDE) +endef + +define clang-tidy-host-cpp +$(hide) $(call-clang-tidy) $< -- $(transform-host-cpp-to-o-compiler-args) +endef + +ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) +define transform-host-cpp-to-o +$(if $(PRIVATE_TIDY_CHECKS), + @echo "tidy $($(PRIVATE_PREFIX)DISPLAY) C++: $<" + $(clang-tidy-host-cpp)) +endef +else +define transform-host-cpp-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) C++: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-host-cpp)) +$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \ + $(transform-host-cpp-to-o-compiler-args) \ + -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< +endef +endif + + +########################################################### +## Commands for running gcc to compile a host C file +########################################################### + +define transform-host-c-or-s-to-o-common-args +$(c-includes) \ +-c \ +$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_HOST_GLOBAL_CFLAGS) \ + $(PRIVATE_HOST_GLOBAL_CONLYFLAGS) \ + ) +endef + +# $(1): extra flags +define transform-host-c-or-s-to-o +@mkdir -p $(dir $@) +$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \ + $(transform-host-c-or-s-to-o-common-args) \ + $(1) \ + -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< +endef + +define transform-host-c-to-o-compiler-args + $(transform-host-c-or-s-to-o-common-args) \ + $(PRIVATE_CFLAGS) $(PRIVATE_CONLYFLAGS) \ + $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE) +endef + +define clang-tidy-host-c +$(hide) $(call-clang-tidy) $< -- $(transform-host-c-to-o-compiler-args) +endef + +ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) +define transform-host-c-to-o +$(if $(PRIVATE_TIDY_CHECKS), + @echo "tidy $($(PRIVATE_PREFIX)DISPLAY) C: $<" + $(clang-tidy-host-c)) +endef +else +define transform-host-c-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) C: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +$(if $(PRIVATE_TIDY_CHECKS), $(clang-tidy-host-c)) +$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \ + $(transform-host-c-to-o-compiler-args) \ + -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< +endef +endif + +define transform-host-s-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<" +$(call transform-host-c-or-s-to-o, $(PRIVATE_ASFLAGS)) +endef + +########################################################### +## Commands for running gcc to compile a host Objective-C file +########################################################### + +define transform-host-m-to-o +@echo "$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<" +$(call transform-host-c-or-s-to-o, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE)) +endef + +########################################################### +## Commands for running gcc to compile a host Objective-C++ file +########################################################### + +define transform-host-mm-to-o +$(transform-host-cpp-to-o) +endef + + +########################################################### +## Rules to compile a single C/C++ source with ../ in the path +########################################################### +# Replace "../" in object paths with $(DOTDOT_REPLACEMENT). +DOTDOT_REPLACEMENT := dotdot/ + +## Rule to compile a C++ source file with ../ in the path. +## Must be called with $(eval). +# $(1): the C++ source file in LOCAL_SRC_FILES. +# $(2): the additional dependencies. +# $(3): the variable name to collect the output object file. +# $(4): the ninja pool to use for the rule +define compile-dotdot-cpp-file +o := $(intermediates)/$(patsubst %$(LOCAL_CPP_EXTENSION),%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) +$$(o) : .KATI_NINJA_POOL := $(4) +$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG_CXX) + $$(transform-$$(PRIVATE_HOST)cpp-to-o) +$$(call include-depfiles-for-objs, $$(o)) +$(3) += $$(o) +endef + +## Rule to compile a C source file with ../ in the path. +## Must be called with $(eval). +# $(1): the C source file in LOCAL_SRC_FILES. +# $(2): the additional dependencies. +# $(3): the variable name to collect the output object file. +# $(4): the ninja pool to use for the rule +define compile-dotdot-c-file +o := $(intermediates)/$(patsubst %.c,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) +$$(o) : .KATI_NINJA_POOL := $(4) +$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG) + $$(transform-$$(PRIVATE_HOST)c-to-o) +$$(call include-depfiles-for-objs, $$(o)) +$(3) += $$(o) +endef + +## Rule to compile a .S source file with ../ in the path. +## Must be called with $(eval). +# $(1): the .S source file in LOCAL_SRC_FILES. +# $(2): the additional dependencies. +# $(3): the variable name to collect the output object file. +# $(4): the ninja pool to use for the rule +define compile-dotdot-s-file +o := $(intermediates)/$(patsubst %.S,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) +$$(o) : .KATI_NINJA_POOL := $(4) +$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG) + $$(transform-$$(PRIVATE_HOST)s-to-o) +$$(call include-depfiles-for-objs, $$(o)) +$(3) += $$(o) +endef + +## Rule to compile a .s source file with ../ in the path. +## Must be called with $(eval). +# $(1): the .s source file in LOCAL_SRC_FILES. +# $(2): the additional dependencies. +# $(3): the variable name to collect the output object file. +# $(4): the ninja pool to use for the rule +define compile-dotdot-s-file-no-deps +o := $(intermediates)/$(patsubst %.s,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) +$$(o) : .KATI_NINJA_POOL := $(4) +$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG) + $$(transform-$$(PRIVATE_HOST)s-to-o) +$(3) += $$(o) +endef + +########################################################### +## Commands for running ar +########################################################### + +define _concat-if-arg2-not-empty +$(if $(2),$(hide) $(1) $(2)) +endef + +# Split long argument list into smaller groups and call the command repeatedly +# Call the command at least once even if there are no arguments, as otherwise +# the output file won't be created. +# +# $(1): the command without arguments +# $(2): the arguments +define split-long-arguments +$(hide) $(1) $(wordlist 1,500,$(2)) +$(call _concat-if-arg2-not-empty,$(1),$(wordlist 501,1000,$(2))) +$(call _concat-if-arg2-not-empty,$(1),$(wordlist 1001,1500,$(2))) +$(call _concat-if-arg2-not-empty,$(1),$(wordlist 1501,2000,$(2))) +$(call _concat-if-arg2-not-empty,$(1),$(wordlist 2001,2500,$(2))) +$(call _concat-if-arg2-not-empty,$(1),$(wordlist 2501,3000,$(2))) +$(call _concat-if-arg2-not-empty,$(1),$(wordlist 3001,99999,$(2))) +endef + +# $(1): the full path of the source static library. +# $(2): the full path of the destination static library. +define _extract-and-include-single-target-whole-static-lib +$(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\ + rm -rf $$ldir; \ + mkdir -p $$ldir; \ + cp $(1) $$ldir; \ + lib_to_include=$$ldir/$(notdir $(1)); \ + filelist=; \ + subdir=0; \ + for f in `$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) t $(1)`; do \ + if [ -e $$ldir/$$f ]; then \ + mkdir $$ldir/$$subdir; \ + ext=$$subdir/; \ + subdir=$$((subdir+1)); \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) m $$lib_to_include $$f; \ + else \ + ext=; \ + fi; \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \ + filelist="$$filelist $$ldir/$$ext$$f"; \ + done ; \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_ARFLAGS) \ + $(PRIVATE_ARFLAGS) $(2) $$filelist + +endef + +# $(1): the full path of the source static library. +# $(2): the full path of the destination static library. +define extract-and-include-whole-static-libs-first +$(if $(strip $(1)), +$(hide) cp $(1) $(2)) +endef + +# $(1): the full path of the destination static library. +define extract-and-include-target-whole-static-libs +$(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)),$(1)) +$(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \ + $(call _extract-and-include-single-target-whole-static-lib, $(lib), $(1))) +endef + +# Explicitly delete the archive first so that ar doesn't +# try to add to an existing archive. +define transform-o-to-static-lib +@echo "$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +@rm -f $@ $@.tmp +$(call extract-and-include-target-whole-static-libs,$@.tmp) +$(call split-long-arguments,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_ARFLAGS) \ + $(PRIVATE_ARFLAGS) \ + $@.tmp,$(PRIVATE_ALL_OBJECTS)) +$(hide) mv -f $@.tmp $@ +endef + +########################################################### +## Commands for running host ar +########################################################### + +# $(1): the full path of the source static library. +# $(2): the full path of the destination static library. +define _extract-and-include-single-host-whole-static-lib +$(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\ + rm -rf $$ldir; \ + mkdir -p $$ldir; \ + cp $(1) $$ldir; \ + lib_to_include=$$ldir/$(notdir $(1)); \ + filelist=; \ + subdir=0; \ + for f in `$($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) t $(1) | \grep '\.o$$'`; do \ + if [ -e $$ldir/$$f ]; then \ + mkdir $$ldir/$$subdir; \ + ext=$$subdir/; \ + subdir=$$((subdir+1)); \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) m $$lib_to_include $$f; \ + else \ + ext=; \ + fi; \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \ + filelist="$$filelist $$ldir/$$ext$$f"; \ + done ; \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)GLOBAL_ARFLAGS) \ + $(2) $$filelist + +endef + +define extract-and-include-host-whole-static-libs +$(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)),$(1)) +$(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \ + $(call _extract-and-include-single-host-whole-static-lib, $(lib),$(1))) +endef + +ifeq ($(HOST_OS),darwin) +# On Darwin the host ar fails if there is nothing to add to .a at all. +# We work around by adding a dummy.o and then deleting it. +define create-dummy.o-if-no-objs +$(if $(PRIVATE_ALL_OBJECTS),,$(hide) touch $(dir $(1))dummy.o) +endef + +define get-dummy.o-if-no-objs +$(if $(PRIVATE_ALL_OBJECTS),,$(dir $(1))dummy.o) +endef + +define delete-dummy.o-if-no-objs +$(if $(PRIVATE_ALL_OBJECTS),,$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) d $(1) $(dir $(1))dummy.o \ + && rm -f $(dir $(1))dummy.o) +endef +else +create-dummy.o-if-no-objs = +get-dummy.o-if-no-objs = +delete-dummy.o-if-no-objs = +endif # HOST_OS is darwin + +# Explicitly delete the archive first so that ar doesn't +# try to add to an existing archive. +define transform-host-o-to-static-lib +@echo "$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +@rm -f $@ $@.tmp +$(call extract-and-include-host-whole-static-libs,$@.tmp) +$(call create-dummy.o-if-no-objs,$@.tmp) +$(call split-long-arguments,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) \ + $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)GLOBAL_ARFLAGS) $@.tmp,\ + $(PRIVATE_ALL_OBJECTS) $(call get-dummy.o-if-no-objs,$@.tmp)) +$(call delete-dummy.o-if-no-objs,$@.tmp) +$(hide) mv -f $@.tmp $@ +endef + + +########################################################### +## Commands for running gcc to link a shared library or package +########################################################### + +# ld just seems to be so finicky with command order that we allow +# it to be overriden en-masse see combo/linux-arm.make for an example. +ifneq ($(HOST_CUSTOM_LD_COMMAND),true) +define transform-host-o-to-shared-lib-inner +$(hide) $(PRIVATE_CXX_LINK) \ + -Wl,-rpath,\$$ORIGIN/../$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OUT_SHARED_LIBRARIES)) \ + -Wl,-rpath,\$$ORIGIN/$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OUT_SHARED_LIBRARIES)) \ + -shared -Wl,-soname,$(notdir $@) \ + $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ + ) \ + $(PRIVATE_LDFLAGS) \ + $(PRIVATE_ALL_OBJECTS) \ + -Wl,--whole-archive \ + $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ + -Wl,--no-whole-archive \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ + $(PRIVATE_ALL_STATIC_LIBRARIES) \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ + $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_HOST_LIBPROFILE_RT)) \ + $(PRIVATE_ALL_SHARED_LIBRARIES) \ + -o $@ \ + $(PRIVATE_LDLIBS) +endef +endif + +define transform-host-o-to-shared-lib +@echo "$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +$(transform-host-o-to-shared-lib-inner) +endef + +define transform-host-o-to-package +@echo "$($(PRIVATE_PREFIX)DISPLAY) Package: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +$(transform-host-o-to-shared-lib-inner) +endef + + +########################################################### +## Commands for running gcc to link a shared library or package +########################################################### + +define transform-o-to-shared-lib-inner +$(hide) $(PRIVATE_CXX_LINK) \ + -nostdlib -Wl,-soname,$(notdir $@) \ + -Wl,--gc-sections \ + -shared \ + $(PRIVATE_TARGET_CRTBEGIN_SO_O) \ + $(PRIVATE_ALL_OBJECTS) \ + -Wl,--whole-archive \ + $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ + -Wl,--no-whole-archive \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ + $(PRIVATE_ALL_STATIC_LIBRARIES) \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ + $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \ + $(PRIVATE_TARGET_LIBCRT_BUILTINS) \ + $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \ + $(PRIVATE_LDFLAGS) \ + $(PRIVATE_ALL_SHARED_LIBRARIES) \ + -o $@ \ + $(PRIVATE_TARGET_CRTEND_SO_O) \ + $(PRIVATE_LDLIBS) +endef + +define transform-o-to-shared-lib +@echo "$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +$(transform-o-to-shared-lib-inner) +endef + +########################################################### +## Commands for running gcc to link an executable +########################################################### + +define transform-o-to-executable-inner +$(hide) $(PRIVATE_CXX_LINK) -pie \ + -nostdlib -Bdynamic \ + -Wl,-dynamic-linker,$(PRIVATE_LINKER) \ + -Wl,--gc-sections \ + -Wl,-z,nocopyreloc \ + $(PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O) \ + $(PRIVATE_ALL_OBJECTS) \ + -Wl,--whole-archive \ + $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ + -Wl,--no-whole-archive \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ + $(PRIVATE_ALL_STATIC_LIBRARIES) \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ + $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \ + $(PRIVATE_TARGET_LIBCRT_BUILTINS) \ + $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \ + $(PRIVATE_LDFLAGS) \ + $(PRIVATE_ALL_SHARED_LIBRARIES) \ + -o $@ \ + $(PRIVATE_TARGET_CRTEND_O) \ + $(PRIVATE_LDLIBS) +endef + +define transform-o-to-executable +@echo "$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +$(transform-o-to-executable-inner) +endef + + +########################################################### +## Commands for linking a static executable. In practice, +## we only use this on arm, so the other platforms don't +## have transform-o-to-static-executable defined. +## Clang driver needs -static to create static executable. +## However, bionic/linker uses -shared to overwrite. +## Linker for x86 targets does not allow coexistance of -static and -shared, +## so we add -static only if -shared is not used. +########################################################### + +define transform-o-to-static-executable-inner +$(hide) $(PRIVATE_CXX_LINK) \ + -nostdlib -Bstatic \ + $(if $(filter $(PRIVATE_LDFLAGS),-shared),,-static) \ + -Wl,--gc-sections \ + -o $@ \ + $(PRIVATE_TARGET_CRTBEGIN_STATIC_O) \ + $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \ + $(PRIVATE_LDFLAGS) \ + $(PRIVATE_ALL_OBJECTS) \ + -Wl,--whole-archive \ + $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ + -Wl,--no-whole-archive \ + $(filter-out %libcompiler_rt.hwasan.a %libc_nomalloc.hwasan.a %libc.hwasan.a %libcompiler_rt.a %libc_nomalloc.a %libc.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ + -Wl,--start-group \ + $(filter %libc.a %libc.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ + $(filter %libc_nomalloc.a %libc_nomalloc.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ + $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \ + $(filter %libcompiler_rt.a %libcompiler_rt.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ + $(PRIVATE_TARGET_LIBCRT_BUILTINS) \ + -Wl,--end-group \ + $(PRIVATE_TARGET_CRTEND_O) +endef + +define transform-o-to-static-executable +@echo "$($(PRIVATE_PREFIX)DISPLAY) StaticExecutable: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +$(transform-o-to-static-executable-inner) +endef + + +########################################################### +## Commands for running gcc to link a host executable +########################################################### + +ifneq ($(HOST_CUSTOM_LD_COMMAND),true) +define transform-host-o-to-executable-inner +$(hide) $(PRIVATE_CXX_LINK) \ + $(PRIVATE_ALL_OBJECTS) \ + -Wl,--whole-archive \ + $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ + -Wl,--no-whole-archive \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ + $(PRIVATE_ALL_STATIC_LIBRARIES) \ + $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ + $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_HOST_LIBPROFILE_RT)) \ + $(PRIVATE_ALL_SHARED_LIBRARIES) \ + $(foreach path,$(PRIVATE_RPATHS), \ + -Wl,-rpath,\$$ORIGIN/$(path)) \ + $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ + $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ + ) \ + $(PRIVATE_LDFLAGS) \ + -o $@ \ + $(PRIVATE_LDLIBS) +endef +endif + +define transform-host-o-to-executable +@echo "$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)" +@mkdir -p $(dir $@) +$(transform-host-o-to-executable-inner) +endef + +########################################################### +## Commands for packaging native coverage files +########################################################### +define package-coverage-files + @rm -f $@ $@.lst $@.premerged + @touch $@.lst + $(foreach obj,$(strip $(PRIVATE_ALL_OBJECTS)), $(hide) echo $(obj) >> $@.lst$(newline)) + $(hide) $(SOONG_ZIP) -o $@.premerged -C $(OUT_DIR) -l $@.lst + $(hide) $(MERGE_ZIPS) -ignore-duplicates $@ $@.premerged $(strip $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) +endef + +########################################################### +## Commands for running javac to make .class files +########################################################### + +# b/37750224 +AAPT_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0 + +# Search for generated R.java in $1, copy the found R.java as $2. +define find-generated-R.java +$(hide) for GENERATED_R_FILE in `find $(1) \ + -name R.java 2> /dev/null`; do \ + cp $$GENERATED_R_FILE $(2) || exit 32; \ + done; +@# Ensure that the target file is always created, i.e. also in case we did not +@# enter the GENERATED_R_FILE-loop above. This avoids unnecessary rebuilding. +$(hide) touch $(2) +endef + +########################################################### +# AAPT2 compilation and link +########################################################### +define aapt2-compile-one-resource-file +@mkdir -p $(dir $@) +$(hide) $(AAPT2) compile -o $(dir $@) $(PRIVATE_AAPT2_CFLAGS) $< +endef + +define aapt2-compile-resource-dirs +@mkdir -p $(dir $@) +$(hide) $(AAPT2) compile -o $@ $(addprefix --dir ,$(PRIVATE_SOURCE_RES_DIRS)) \ + $(PRIVATE_AAPT2_CFLAGS) +endef + +# TODO(b/74574557): use aapt2 compile --zip if it gets implemented +define aapt2-compile-resource-zips +@mkdir -p $(dir $@) +$(ZIPSYNC) -d $@.contents -l $@.list $(PRIVATE_SOURCE_RES_ZIPS) +$(hide) $(AAPT2) compile -o $@ --dir $@.contents $(PRIVATE_AAPT2_CFLAGS) +endef + +# Set up rule to compile one resource file with aapt2. +# Must be called with $(eval). +# $(1): the source file +# $(2): the output file +define aapt2-compile-one-resource-file-rule +$(2) : $(1) $(AAPT2) + @echo "AAPT2 compile $$@ <- $$<" + $$(call aapt2-compile-one-resource-file) +endef + +# Convert input resource file path to output file path. +# values-[config]/.xml -> values-[config]_.arsc.flat; +# For other resource file, just replace the last "/" with "_" and +# add .flat extension. +# +# $(1): the input resource file path +# $(2): the base dir of the output file path +# Returns: the compiled output file path +define aapt2-compiled-resource-out-file +$(strip \ + $(eval _p_w := $(strip $(subst /,$(space),$(dir $(call clean-path,$(1)))))) + $(2)/$(subst $(space),/,$(_p_w))_$(if $(filter values%,$(lastword $(_p_w))),$(patsubst %.xml,%.arsc,$(notdir $(1))),$(notdir $(1))).flat) +endef + +define aapt2-link +@mkdir -p $(dir $@) +rm -rf $(PRIVATE_JAVA_GEN_DIR) +mkdir -p $(PRIVATE_JAVA_GEN_DIR) +$(call dump-words-to-file,$(PRIVATE_RES_FLAT),$(dir $@)aapt2-flat-list) +$(call dump-words-to-file,$(PRIVATE_OVERLAY_FLAT),$(dir $@)aapt2-flat-overlay-list) +$(hide) $(AAPT2) link -o $@ \ + $(PRIVATE_AAPT_FLAGS) \ + $(if $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES),$$(cat $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES))) \ + $(addprefix --manifest ,$(PRIVATE_ANDROID_MANIFEST)) \ + $(addprefix -I ,$(PRIVATE_AAPT_INCLUDES)) \ + $(addprefix -I ,$(PRIVATE_SHARED_ANDROID_LIBRARIES)) \ + $(addprefix -A ,$(foreach d,$(PRIVATE_ASSET_DIR),$(call clean-path,$(d)))) \ + $(addprefix --java ,$(PRIVATE_JAVA_GEN_DIR)) \ + $(addprefix --proguard ,$(PRIVATE_PROGUARD_OPTIONS_FILE)) \ + $(addprefix --min-sdk-version ,$(PRIVATE_DEFAULT_APP_TARGET_SDK)) \ + $(addprefix --target-sdk-version ,$(PRIVATE_DEFAULT_APP_TARGET_SDK)) \ + $(if $(filter --product,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --product ,$(PRIVATE_TARGET_AAPT_CHARACTERISTICS))) \ + $(addprefix -c ,$(PRIVATE_PRODUCT_AAPT_CONFIG)) \ + $(addprefix --preferred-density ,$(PRIVATE_PRODUCT_AAPT_PREF_CONFIG)) \ + $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \ + $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \ + $(addprefix --rename-manifest-package ,$(PRIVATE_MANIFEST_PACKAGE_NAME)) \ + $(addprefix --rename-instrumentation-target-package ,$(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \ + -R \@$(dir $@)aapt2-flat-overlay-list \ + \@$(dir $@)aapt2-flat-list +$(SOONG_ZIP) -o $(PRIVATE_SRCJAR) -C $(PRIVATE_JAVA_GEN_DIR) -D $(PRIVATE_JAVA_GEN_DIR) +$(EXTRACT_JAR_PACKAGES) -i $(PRIVATE_SRCJAR) -o $(PRIVATE_AAPT_EXTRA_PACKAGES) --prefix '--extra-packages ' +endef + +define _create-default-manifest-file +$(1): + rm -f $1 + (echo '' && \ + echo ' ' && \ + echo '' ) > $1 +endef + +define create-default-manifest-file + $(eval $(call _create-default-manifest-file,$(1),$(2))) +endef + + +########################################################### +xlint_unchecked := -Xlint:unchecked + +# emit-line, , +define emit-line + $(if $(1),echo -n '$(strip $(1)) ' >> $(2)) +endef + +# dump-words-to-file, , +define dump-words-to-file + @rm -f $(2) + @touch $(2) + @$(call emit-line,$(wordlist 1,500,$(1)),$(2)) + @$(call emit-line,$(wordlist 501,1000,$(1)),$(2)) + @$(call emit-line,$(wordlist 1001,1500,$(1)),$(2)) + @$(call emit-line,$(wordlist 1501,2000,$(1)),$(2)) + @$(call emit-line,$(wordlist 2001,2500,$(1)),$(2)) + @$(call emit-line,$(wordlist 2501,3000,$(1)),$(2)) + @$(call emit-line,$(wordlist 3001,3500,$(1)),$(2)) + @$(call emit-line,$(wordlist 3501,4000,$(1)),$(2)) + @$(call emit-line,$(wordlist 4001,4500,$(1)),$(2)) + @$(call emit-line,$(wordlist 4501,5000,$(1)),$(2)) + @$(call emit-line,$(wordlist 5001,5500,$(1)),$(2)) + @$(call emit-line,$(wordlist 5501,6000,$(1)),$(2)) + @$(call emit-line,$(wordlist 6001,6500,$(1)),$(2)) + @$(call emit-line,$(wordlist 6501,7000,$(1)),$(2)) + @$(call emit-line,$(wordlist 7001,7500,$(1)),$(2)) + @$(call emit-line,$(wordlist 7501,8000,$(1)),$(2)) + @$(call emit-line,$(wordlist 8001,8500,$(1)),$(2)) + @$(call emit-line,$(wordlist 8501,9000,$(1)),$(2)) + @$(call emit-line,$(wordlist 9001,9500,$(1)),$(2)) + @$(call emit-line,$(wordlist 9501,10000,$(1)),$(2)) + @$(call emit-line,$(wordlist 10001,10500,$(1)),$(2)) + @$(call emit-line,$(wordlist 10501,11000,$(1)),$(2)) + @$(call emit-line,$(wordlist 11001,11500,$(1)),$(2)) + @$(call emit-line,$(wordlist 11501,12000,$(1)),$(2)) + @$(call emit-line,$(wordlist 12001,12500,$(1)),$(2)) + @$(call emit-line,$(wordlist 12501,13000,$(1)),$(2)) + @$(call emit-line,$(wordlist 13001,13500,$(1)),$(2)) + @$(call emit-line,$(wordlist 13501,14000,$(1)),$(2)) + @$(call emit-line,$(wordlist 14001,14500,$(1)),$(2)) + @$(call emit-line,$(wordlist 14501,15000,$(1)),$(2)) + @$(call emit-line,$(wordlist 15001,15500,$(1)),$(2)) + @$(call emit-line,$(wordlist 15501,16000,$(1)),$(2)) + @$(call emit-line,$(wordlist 16001,16500,$(1)),$(2)) + @$(call emit-line,$(wordlist 16501,17000,$(1)),$(2)) + @$(call emit-line,$(wordlist 17001,17500,$(1)),$(2)) + @$(call emit-line,$(wordlist 17501,18000,$(1)),$(2)) + @$(call emit-line,$(wordlist 18001,18500,$(1)),$(2)) + @$(call emit-line,$(wordlist 18501,19000,$(1)),$(2)) + @$(call emit-line,$(wordlist 19001,19500,$(1)),$(2)) + @$(call emit-line,$(wordlist 19501,20000,$(1)),$(2)) + @$(call emit-line,$(wordlist 20001,20500,$(1)),$(2)) + @$(call emit-line,$(wordlist 20501,21000,$(1)),$(2)) + @$(call emit-line,$(wordlist 21001,21500,$(1)),$(2)) + @$(call emit-line,$(wordlist 21501,22000,$(1)),$(2)) + @$(call emit-line,$(wordlist 22001,22500,$(1)),$(2)) + @$(call emit-line,$(wordlist 22501,23000,$(1)),$(2)) + @$(call emit-line,$(wordlist 23001,23500,$(1)),$(2)) + @$(call emit-line,$(wordlist 23501,24000,$(1)),$(2)) + @$(call emit-line,$(wordlist 24001,24500,$(1)),$(2)) + @$(call emit-line,$(wordlist 24501,25000,$(1)),$(2)) + @$(call emit-line,$(wordlist 25001,25500,$(1)),$(2)) + @$(call emit-line,$(wordlist 25501,26000,$(1)),$(2)) + @$(call emit-line,$(wordlist 26001,26500,$(1)),$(2)) + @$(call emit-line,$(wordlist 26501,27000,$(1)),$(2)) + @$(call emit-line,$(wordlist 27001,27500,$(1)),$(2)) + @$(call emit-line,$(wordlist 27501,28000,$(1)),$(2)) + @$(call emit-line,$(wordlist 28001,28500,$(1)),$(2)) + @$(call emit-line,$(wordlist 28501,29000,$(1)),$(2)) + @$(call emit-line,$(wordlist 29001,29500,$(1)),$(2)) + @$(call emit-line,$(wordlist 29501,30000,$(1)),$(2)) + @$(call emit-line,$(wordlist 30001,30500,$(1)),$(2)) + @$(call emit-line,$(wordlist 30501,31000,$(1)),$(2)) + @$(call emit-line,$(wordlist 31001,31500,$(1)),$(2)) + @$(call emit-line,$(wordlist 31501,32000,$(1)),$(2)) + @$(call emit-line,$(wordlist 32001,32500,$(1)),$(2)) + @$(call emit-line,$(wordlist 32501,33000,$(1)),$(2)) + @$(call emit-line,$(wordlist 33001,33500,$(1)),$(2)) + @$(call emit-line,$(wordlist 33501,34000,$(1)),$(2)) + @$(call emit-line,$(wordlist 34001,34500,$(1)),$(2)) + @$(call emit-line,$(wordlist 34501,35000,$(1)),$(2)) + @$(call emit-line,$(wordlist 35001,35500,$(1)),$(2)) + @$(call emit-line,$(wordlist 35501,36000,$(1)),$(2)) + @$(call emit-line,$(wordlist 36001,36500,$(1)),$(2)) + @$(call emit-line,$(wordlist 36501,37000,$(1)),$(2)) + @$(call emit-line,$(wordlist 37001,37500,$(1)),$(2)) + @$(call emit-line,$(wordlist 37501,38000,$(1)),$(2)) + @$(call emit-line,$(wordlist 38001,38500,$(1)),$(2)) + @$(call emit-line,$(wordlist 38501,39000,$(1)),$(2)) + @$(call emit-line,$(wordlist 39001,39500,$(1)),$(2)) + @$(if $(wordlist 39501,39502,$(1)),$(error Too many words ($(words $(1))))) +endef +# Return jar arguments to compress files in a given directory +# $(1): directory +# +# Returns an @-file argument that contains the output of a subshell +# that looks like -C $(1) path/to/file1 -C $(1) path/to/file2 +# Also adds "-C out/empty ." which avoids errors in jar when +# there are no files in the directory. +define jar-args-sorted-files-in-directory + @<(find $(1) -type f | sort | $(JAR_ARGS) $(1); echo "-C $(EMPTY_DIRECTORY) .") +endef + +# append additional Java sources(resources/Proto sources, and etc) to $(1). +define fetch-additional-java-source +$(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \ + find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' -and -not -name '.*' >> $(1); \ +fi +endef + +# Some historical notes: +# - below we write the list of java files to java-source-list to avoid argument +# list length problems with Cygwin +# - we filter out duplicate java file names because eclipse's compiler +# doesn't like them. +define write-java-source-list +@echo "$($(PRIVATE_PREFIX)DISPLAY) Java source list: $(PRIVATE_MODULE)" +$(hide) rm -f $@ +$(call dump-words-to-file,$(sort $(PRIVATE_JAVA_SOURCES)),$@.tmp) +$(call fetch-additional-java-source,$@.tmp) +$(hide) tr ' ' '\n' < $@.tmp | $(NORMALIZE_PATH) | sort -u > $@ +endef + +# Common definition to invoke javac on the host and target. +# +# $(1): javac +# $(2): classpath_libs +define compile-java +$(hide) rm -f $@ +$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR) +$(hide) mkdir -p $(dir $@) +$(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR) +$(if $(PRIVATE_SRCJARS),\ + $(ZIPSYNC) -d $(PRIVATE_SRCJAR_INTERMEDIATES_DIR) -l $(PRIVATE_SRCJAR_LIST_FILE) -f "*.java" $(PRIVATE_SRCJARS)) +$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) $(if $(PRIVATE_SRCJARS),-o -s $(PRIVATE_SRCJAR_LIST_FILE) )] ; then \ + $(SOONG_JAVAC_WRAPPER) $(JAVAC_WRAPPER) $(1) -encoding UTF-8 \ + $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \ + $(if $(PRIVATE_USE_SYSTEM_MODULES), \ + $(addprefix --system=,$(PRIVATE_SYSTEM_MODULES_DIR)), \ + $(addprefix -bootclasspath ,$(strip \ + $(call normalize-path-list,$(PRIVATE_BOOTCLASSPATH)) \ + $(PRIVATE_EMPTY_BOOTCLASSPATH)))) \ + $(if $(PRIVATE_USE_SYSTEM_MODULES), \ + $(if $(PRIVATE_PATCH_MODULE), \ + --patch-module=$(PRIVATE_PATCH_MODULE)=$(call normalize-path-list,. $(2)))) \ + $(addprefix -classpath ,$(call normalize-path-list,$(strip \ + $(if $(PRIVATE_USE_SYSTEM_MODULES), \ + $(filter-out $(PRIVATE_SYSTEM_MODULES_LIBS),$(PRIVATE_BOOTCLASSPATH))) \ + $(2)))) \ + $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \ + -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) -s $(PRIVATE_ANNO_INTERMEDIATES_DIR) \ + $(PRIVATE_JAVACFLAGS) \ + \@$(PRIVATE_JAVA_SOURCE_LIST) \ + $(if $(PRIVATE_SRCJARS),\@$(PRIVATE_SRCJAR_LIST_FILE)) \ + || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 ) \ +fi +$(if $(PRIVATE_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))) \ + | xargs rm -rf) +$(if $(PRIVATE_JAR_PACKAGES), \ + $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -mindepth 1 -type f \ + $(foreach pkg, $(PRIVATE_JAR_PACKAGES), \ + -not -path $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))/\*) -delete ; \ + find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -empty -delete) +$(if $(PRIVATE_JAR_EXCLUDE_PACKAGES), $(hide) rm -rf \ + $(foreach pkg, $(PRIVATE_JAR_EXCLUDE_PACKAGES), \ + $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg)))) +$(hide) $(SOONG_ZIP) -jar -o $@ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) -D $(PRIVATE_CLASS_INTERMEDIATES_DIR) +$(if $(PRIVATE_EXTRA_JAR_ARGS),$(call add-java-resources-to,$@)) +endef + +define transform-java-to-header.jar +@echo "$($(PRIVATE_PREFIX)DISPLAY) Turbine: $(PRIVATE_MODULE)" +@mkdir -p $(dir $@) +@rm -rf $(dir $@)/classes-turbine +@mkdir $(dir $@)/classes-turbine +$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) -o -n "$(PRIVATE_SRCJARS)" ] ; then \ + $(JAVA) -jar $(TURBINE) \ + --output $@.premerged --temp_dir $(dir $@)/classes-turbine \ + --sources \@$(PRIVATE_JAVA_SOURCE_LIST) --source_jars $(PRIVATE_SRCJARS) \ + --javacopts $(PRIVATE_JAVACFLAGS) $(COMMON_JDK_FLAGS) -- \ + $(if $(PRIVATE_USE_SYSTEM_MODULES), \ + --system $(PRIVATE_SYSTEM_MODULES_DIR), \ + --bootclasspath $(strip $(PRIVATE_BOOTCLASSPATH))) \ + --classpath $(strip $(if $(PRIVATE_USE_SYSTEM_MODULES), \ + $(filter-out $(PRIVATE_SYSTEM_MODULES_LIBS),$(PRIVATE_BOOTCLASSPATH))) \ + $(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) \ + || ( rm -rf $(dir $@)/classes-turbine ; exit 41 ) && \ + $(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $@.premerged $(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES) ; \ +else \ + $(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES) ; \ +fi +$(hide) $(ZIPTIME) $@.tmp +$(hide) $(call commit-change-for-toc,$@) +endef + +# Runs jarjar on an input file. Jarjar doesn't exit with a nonzero return code +# when there is a syntax error in a rules file and doesn't write the output +# file, so removes the output file before running jarjar and check if it exists +# after running jarjar. +define transform-jarjar +echo $($(PRIVATE_PREFIX)DISPLAY) JarJar: $@ +rm -f $@ +$(JAVA) -jar $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@ +[ -e $@ ] || (echo "Missing output file"; exit 1) +endef + +# Moves $1.tmp to $1 if necessary. This is designed to be used with +# .KATI_RESTAT. For kati, this function doesn't update the timestamp +# of $1 when $1.tmp is identical to $1 so that ninja won't rebuild +# targets which depend on $1. +define commit-change-for-toc +$(hide) if cmp -s $1.tmp $1 ; then \ + rm $1.tmp ; \ +else \ + mv $1.tmp $1 ; \ +fi +endef + +ifeq (,$(TARGET_BUILD_APPS)) + +## Rule to create a table of contents from a .dex file. +## Must be called with $(eval). +# $(1): The directory which contains classes*.dex files +define _transform-dex-to-toc +$1/classes.dex.toc: PRIVATE_INPUT_DEX_FILES := $1/classes*.dex +$1/classes.dex.toc: $1/classes.dex $(DEXDUMP) + @echo Generating TOC: $$@ + $(hide) ANDROID_LOG_TAGS="*:e" $(DEXDUMP) -l xml $$(PRIVATE_INPUT_DEX_FILES) > $$@.tmp + $$(call commit-change-for-toc,$$@) +endef + +## Define a rule which generates .dex.toc and mark it as .KATI_RESTAT. +# $(1): The directory which contains classes*.dex files +define define-dex-to-toc-rule +$(eval $(call _transform-dex-to-toc,$1))\ +$(eval .KATI_RESTAT: $1/classes.dex.toc) +endef + +else + +# Turn off .toc optimization for apps build as we cannot build dexdump. +define define-dex-to-toc-rule +endef + +endif # TARGET_BUILD_APPS + + +# Takes an sdk version that might be PLATFORM_VERSION_CODENAME (for example P), +# returns a number greater than the highest existing sdk version if it is, or +# the input if it is not. +define codename-or-sdk-to-sdk +$(if $(filter $(1),$(PLATFORM_VERSION_CODENAME)),10000,$(1)) +endef + +# Uses LOCAL_SDK_VERSION and PLATFORM_SDK_VERSION to determine a compileSdkVersion +# in the form of a number or a codename (28 or P) +define module-sdk-version +$(strip \ + $(if $(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)), \ + $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)), \ + $(PLATFORM_SDK_VERSION))) +endef + +# Uses LOCAL_SDK_VERSION and DEFAULT_APP_TARGET_SDK to determine +# a targetSdkVersion in the form of a number or a codename (28 or P). +define module-target-sdk-version +$(strip \ + $(if $(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)), \ + $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)), \ + $(DEFAULT_APP_TARGET_SDK))) +endef + +# Uses LOCAL_MIN_SDK_VERSION, LOCAL_SDK_VERSION and DEFAULT_APP_TARGET_SDK to determine +# a minSdkVersion in the form of a number or a codename (28 or P). +define module-min-sdk-version +$(if $(LOCAL_MIN_SDK_VERSION),$(LOCAL_MIN_SDK_VERSION),$(call module-target-sdk-version)) +endef + + +define transform-classes.jar-to-dex +@echo "target Dex: $(PRIVATE_MODULE)" +@mkdir -p $(dir $@)tmp +$(hide) rm -f $(dir $@)classes*.dex $(dir $@)d8_input.jar +$(hide) $(ZIP2ZIP) -j -i $< -o $(dir $@)d8_input.jar "**/*.class" +$(hide) $(D8_WRAPPER) $(DX_COMMAND) $(DEX_FLAGS) \ + --output $(dir $@)tmp \ + $(addprefix --lib ,$(PRIVATE_D8_LIBS)) \ + --min-api $(PRIVATE_MIN_SDK_VERSION) \ + $(subst --main-dex-list=, --main-dex-list , \ + $(filter-out --core-library --multi-dex --minimal-main-dex,$(PRIVATE_DX_FLAGS))) \ + $(dir $@)d8_input.jar +$(hide) mv $(dir $@)tmp/* $(dir $@) +$(hide) rm -f $(dir $@)d8_input.jar +$(hide) rm -rf $(dir $@)tmp +endef + +# We need the extra blank line, so that the command will be on a separate line. +# $(1): the package +# $(2): the ABI name +# $(3): the list of shared libraies +define _add-jni-shared-libs-to-package-per-abi +$(hide) cp $(3) $(dir $(1))lib/$(2) + +endef + +# $(1): the package file +# $(2): if true, uncompress jni libs +define create-jni-shared-libs-package +rm -rf $(dir $(1))lib +mkdir -p $(addprefix $(dir $(1))lib/,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI)) +$(foreach abi,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI),\ + $(call _add-jni-shared-libs-to-package-per-abi,$(1),$(abi),\ + $(patsubst $(abi):%,%,$(filter $(abi):%,$(PRIVATE_JNI_SHARED_LIBRARIES))))) +$(SOONG_ZIP) $(if $(2),-L 0) -o $(1) -C $(dir $(1)) -D $(dir $(1))lib +rm -rf $(dir $(1))lib +endef + +# $(1): the jar file. +# $(2): the classes.dex file. +define create-dex-jar +find $(dir $(2)) -maxdepth 1 -name "classes*.dex" | sort > $(1).lst +$(SOONG_ZIP) -o $(1) -C $(dir $(2)) -l $(1).lst +endef + +# Add java resources added by the current module to an existing package. +# $(1) destination package. +define add-java-resources-to + $(call _java-resources,$(1),u) +endef + +# Add java resources added by the current module to a new jar. +# $(1) destination jar. +define create-java-resources-jar + $(call _java-resources,$(1),c) +endef + +define _java-resources +$(call dump-words-to-file, $(PRIVATE_EXTRA_JAR_ARGS), $(1).jar-arg-list) +$(hide) $(JAR) $(2)f $(1) @$(1).jar-arg-list +@rm -f $(1).jar-arg-list +endef + +# Add resources (non .class files) from a jar to a package +# $(1): the package file +# $(2): the jar file +# $(3): temporary directory +define add-jar-resources-to-package + rm -rf $(3) + mkdir -p $(3) + zipinfo -1 $(2) > /dev/null + unzip -qo $(2) -d $(3) $$(zipinfo -1 $(2) | grep -v -E "\.class$$") + $(JAR) uf $(1) $(call jar-args-sorted-files-in-directory,$(3)) +endef + +# $(1): the output resources jar. +# $(2): the input jar +define extract-resources-jar + $(ZIP2ZIP) -i $(2) -o $(1) -x '**/*.class' -x '**/*/' +endef + +# Sign a package using the specified key/cert. +# +define sign-package +$(call sign-package-arg,$@) +endef + +# $(1): the package file we are signing. +define sign-package-arg +$(hide) mv $(1) $(1).unsigned +$(hide) $(JAVA) -Djava.library.path=$$(dirname $(SIGNAPK_JNI_LIBRARY_PATH)) -jar $(SIGNAPK_JAR) \ + $(if $(strip $(PRIVATE_CERTIFICATE_LINEAGE)), --lineage $(PRIVATE_CERTIFICATE_LINEAGE)) \ + $(if $(strip $(PRIVATE_ROTATION_MIN_SDK_VERSION)), --rotation-min-sdk-version $(PRIVATE_ROTATION_MIN_SDK_VERSION)) \ + $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) \ + $(PRIVATE_ADDITIONAL_CERTIFICATES) $(1).unsigned $(1).signed +$(hide) mv $(1).signed $(1) +endef + +# Align STORED entries of a package on 4-byte boundaries to make them easier to mmap. +# +define align-package +$(hide) if ! $(ZIPALIGN) -c -p 4 $@ >/dev/null ; then \ + mv $@ $@.unaligned; \ + $(ZIPALIGN) \ + -f \ + -p \ + 4 \ + $@.unaligned $@.aligned; \ + mv $@.aligned $@; \ + fi +endef + +# Verifies ZIP alignment of a package. +# +define check-package-alignment +$(hide) if ! $(ZIPALIGN) -c -p 4 $@ >/dev/null ; then \ + $(call echo-error,$@,Improper package alignment); \ + exit 1; \ + fi +endef + +# Compress a package using the standard gzip algorithm. +define compress-package +$(hide) \ + mv $@ $@.uncompressed; \ + $(MINIGZIP) -c $@.uncompressed > $@.compressed; \ + rm -f $@.uncompressed; \ + mv $@.compressed $@; +endef + +ifeq ($(HOST_OS),linux) +# Runs appcompat and store logs in $(PRODUCT_OUT)/appcompat +define extract-package +$(AAPT2) dump resources $@ | awk -F ' |=' '/^Package/{print $$3}' >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && +endef +define appcompat-header +$(hide) \ + mkdir -p $(PRODUCT_OUT)/appcompat && \ + rm -f $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ + echo -n "Package name: " >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ + $(extract-package) \ + echo "Module name in Android tree: $(PRIVATE_MODULE)" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ + echo "Local path in Android tree: $(PRIVATE_PATH)" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ + echo "Install path on $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT): $(PRIVATE_INSTALLED_MODULE)" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ + echo >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log +endef +ART_VERIDEX_APPCOMPAT_SCRIPT:=$(HOST_OUT)/bin/appcompat.sh +define run-appcompat +$(hide) \ + echo "appcompat.sh output:" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ + PACKAGING=$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING ANDROID_LOG_TAGS="*:e" $(ART_VERIDEX_APPCOMPAT_SCRIPT) --dex-file=$@ --api-flags=$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) 2>&1 >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log +endef +appcompat-files = \ + $(AAPT2) \ + $(ART_VERIDEX_APPCOMPAT_SCRIPT) \ + $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) \ + $(HOST_OUT_EXECUTABLES)/veridex \ + $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex \ + $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex +else +appcompat-header = +run-appcompat = +appcompat-files = +endif # HOST_OS == linux +.KATI_READONLY: appcompat-header run-appcompat appcompat-files + +# Remove dynamic timestamps from packages +# +define remove-timestamps-from-package +$(hide) $(ZIPTIME) $@ +endef + +# Uncompress dex files embedded in an apk. +# +define uncompress-dexs + if (zipinfo $@ '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ + $(ZIP2ZIP) -i $@ -o $@.tmp -0 "classes*.dex" && \ + mv -f $@.tmp $@ ; \ + fi +endef + +# Uncompress shared JNI libraries embedded in an apk. +# +define uncompress-prebuilt-embedded-jni-libs + if (zipinfo $@ 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ + $(ZIP2ZIP) -i $@ -o $@.tmp -0 'lib/**/*.so' && mv -f $@.tmp $@ ; \ + fi +endef + +# Verifies shared JNI libraries and dex files in an apk are uncompressed. +# +define check-jni-dex-compression + if (zipinfo $@ 'lib/*.so' '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ + $(call echo-error,$@,Contains compressed JNI libraries and/or dex files); \ + exit 1; \ + fi +endef + +# Remove unwanted shared JNI libraries embedded in an apk. +# +define remove-unwanted-prebuilt-embedded-jni-libs + $(if $(PRIVATE_EMBEDDED_JNI_LIBS), \ + $(ZIP2ZIP) -i $@ -o $@.tmp \ + -x 'lib/**/*.so' $(addprefix -X ,$(PRIVATE_EMBEDDED_JNI_LIBS)) && \ + mv -f $@.tmp $@) +endef + +# TODO(joeo): If we can ever upgrade to post 3.81 make and get the +# new prebuilt rules to work, we should change this to copy the +# resources to the out directory and then copy the resources. + +# Note: we intentionally don't clean PRIVATE_CLASS_INTERMEDIATES_DIR +# in transform-java-to-classes for the sake of vm-tests. +define transform-host-java-to-package +@echo "Host Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))" +$(call compile-java,$(HOST_JAVAC),$(PRIVATE_ALL_JAVA_LIBRARIES)) +endef + +# Note: we intentionally don't clean PRIVATE_CLASS_INTERMEDIATES_DIR +# in transform-java-to-classes for the sake of vm-tests. +define transform-host-java-to-dalvik-package +@echo "Dalvik Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))" +$(call compile-java,$(HOST_JAVAC),$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) +endef + +########################################################### +## Commands for copying files +########################################################### + +# Define a rule to copy a header. Used via $(eval) by copy_headers.make. +# $(1): source header +# $(2): destination header +define copy-one-header +$(2): $(1) + @echo "Header: $$@" + $$(copy-file-to-new-target-with-cp) +endef + +# Define a rule to copy a file. For use via $(eval). +# $(1): source file +# $(2): destination file +define copy-one-file +$(2): $(1) + @echo "Copy: $$@" + $$(copy-file-to-target) +endef + +define copy-and-uncompress-dexs +$(2): $(1) $(ZIPALIGN) $(ZIP2ZIP) + @echo "Uncompress dexs in: $$@" + $$(copy-file-to-target) + $$(uncompress-dexs) + $$(align-package) +endef + +# Create copy pair for compatibility suite +# Filter out $(LOCAL_INSTALLED_MODULE) to prevent overriding target +# $(1): source path +# $(2): destination path +# The format of copy pair is src:dst +define compat-copy-pair +$(if $(filter-out $(2), $(LOCAL_INSTALLED_MODULE)), $(1):$(2)) +endef + +# Create copy pair for $(1) $(2) +# If $(2) is substring of $(3) do nothing. +# $(1): source path +# $(2): destination path +# $(3): filter-out target +# The format of copy pair is src:dst +define filter-copy-pair +$(if $(findstring $(2), $(3)),,$(1):$(2)) +endef + +# Copies many files. +# $(1): The files to copy. Each entry is a ':' separated src:dst pair +# $(2): An optional directory to prepend to the destination +# Evaluates to the list of the dst files (ie suitable for a dependency list) +define copy-many-files +$(foreach f, $(1), $(strip \ + $(eval _cmf_tuple := $(subst :, ,$(f))) \ + $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ + $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ + $(if $(strip $(2)), \ + $(eval _cmf_dest := $(patsubst %/,%,$(strip $(2)))/$(patsubst /%,%,$(_cmf_dest)))) \ + $(if $(filter-out $(_cmf_src), $(_cmf_dest)), \ + $(eval $(call copy-one-file,$(_cmf_src),$(_cmf_dest)))) \ + $(_cmf_dest))) +endef + +# Copy the file only if it's a well-formed init script file. For use via $(eval). +# $(1): source file +# $(2): destination file +define copy-init-script-file-checked +ifdef TARGET_BUILD_UNBUNDLED +# TODO (b/185624993): Remove the chck on TARGET_BUILD_UNBUNDLED when host_init_verifier can run +# without requiring the HIDL interface map. +$(2): $(1) +else ifneq ($(HOST_OS),darwin) +# Host init verifier doesn't exist on darwin. +$(2): \ + $(1) \ + $(HOST_INIT_VERIFIER) \ + $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ + $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ + $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ + $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ + $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ + $(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ + $(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ + $(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ + $(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ + $(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts + $(hide) $(HOST_INIT_VERIFIER) \ + -p $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ + -p $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ + -p $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ + -p $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ + -p $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ + --property-contexts=$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts \ + $$< +else +$(2): $(1) +endif + @echo "Copy init script: $$@" + $$(copy-file-to-target) +endef + +# Copies many init script files and check they are well-formed. +# $(1): The init script files to copy. Each entry is a ':' separated src:dst pair. +# Evaluates to the list of the dst files. (ie suitable for a dependency list.) +define copy-many-init-script-files-checked +$(foreach f, $(1), $(strip \ + $(eval _cmf_tuple := $(subst :, ,$(f))) \ + $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ + $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ + $(eval $(call copy-init-script-file-checked,$(_cmf_src),$(_cmf_dest))) \ + $(_cmf_dest))) +endef + +# Copy the file only if it's a well-formed xml file. For use via $(eval). +# $(1): source file +# $(2): destination file, must end with .xml. +define copy-xml-file-checked +$(2): $(1) $(XMLLINT) + @echo "Copy xml: $$@" + $(hide) $(XMLLINT) $$< >/dev/null # Don't print the xml file to stdout. + $$(copy-file-to-target) +endef + +# Copies many xml files and check they are well-formed. +# $(1): The xml files to copy. Each entry is a ':' separated src:dst pair. +# Evaluates to the list of the dst files. (ie suitable for a dependency list.) +define copy-many-xml-files-checked +$(foreach f, $(1), $(strip \ + $(eval _cmf_tuple := $(subst :, ,$(f))) \ + $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ + $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ + $(eval $(call copy-xml-file-checked,$(_cmf_src),$(_cmf_dest))) \ + $(_cmf_dest))) +endef + +# Copy the file only if it is a well-formed manifest file. For use viea $(eval) +# $(1): source file +# $(2): destination file +define copy-vintf-manifest-checked +$(2): $(1) $(HOST_OUT_EXECUTABLES)/assemble_vintf + @echo "Copy xml: $$@" + $(hide) $(HOST_OUT_EXECUTABLES)/assemble_vintf -i $$< >/dev/null # Don't print the xml file to stdout. + $$(copy-file-to-target) +endef + +# Copies many vintf manifest files checked. +# $(1): The files to copy. Each entry is a ':' separated src:dst pair +# Evaluates to the list of the dst files (ie suitable for a dependency list) +define copy-many-vintf-manifest-files-checked +$(foreach f, $(1), $(strip \ + $(eval _cmf_tuple := $(subst :, ,$(f))) \ + $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ + $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ + $(eval $(call copy-vintf-manifest-checked,$(_cmf_src),$(_cmf_dest))) \ + $(_cmf_dest))) +endef + +# Copy the file only if it's not an ELF file. For use via $(eval). +# $(1): source file +# $(2): destination file +# $(3): message to print on error +define copy-non-elf-file-checked +$(eval check_non_elf_file_timestamp := \ + $(call intermediates-dir-for,FAKE,check-non-elf-file-timestamps)/$(2).timestamp) +$(check_non_elf_file_timestamp): $(1) $(LLVM_READOBJ) + @echo "Check non-ELF: $$<" + $(hide) mkdir -p "$$(dir $$@)" + $(hide) rm -f "$$@" + $(hide) \ + if $(LLVM_READOBJ) -h "$$<" >/dev/null 2>&1; then \ + $(call echo-error,$(2),$(3)); \ + $(call echo-error,$(2),found ELF file: $$<); \ + false; \ + fi + $(hide) touch "$$@" + +$(2): $(1) $(check_non_elf_file_timestamp) + @echo "Copy non-ELF: $$@" + $$(copy-file-to-target) + +check-elf-prebuilt-product-copy-files: $(check_non_elf_file_timestamp) +endef + +# The -t option to acp and the -p option to cp is +# required for OSX. OSX has a ridiculous restriction +# where it's an error for a .a file's modification time +# to disagree with an internal timestamp, and this +# macro is used to install .a files (among other things). + +# Copy a single file from one place to another, +# preserving permissions and overwriting any existing +# file. +# When we used acp, it could not handle high resolution timestamps +# on file systems like ext4. Because of that, '-t' option was disabled +# and copy-file-to-target was identical to copy-file-to-new-target. +# Keep the behavior until we audit and ensure that switching this back +# won't break anything. +define copy-file-to-target +@mkdir -p $(dir $@) +$(hide) rm -f $@ +$(hide) cp "$<" "$@" +endef + +# The same as copy-file-to-target, but use the local +# cp command instead of acp. +define copy-file-to-target-with-cp +@mkdir -p $(dir $@) +$(hide) rm -f $@ +$(hide) cp -p "$<" "$@" +endef + +# The same as copy-file-to-target, but strip out "# comment"-style +# comments (for config files and such). +define copy-file-to-target-strip-comments +@mkdir -p $(dir $@) +$(hide) rm -f $@ +$(hide) sed -e 's/#.*$$//' -e 's/[ \t]*$$//' -e '/^$$/d' < $< > $@ +endef + +# The same as copy-file-to-target, but don't preserve +# the old modification time. +define copy-file-to-new-target +@mkdir -p $(dir $@) +$(hide) rm -f $@ +$(hide) cp $< $@ +endef + +# The same as copy-file-to-new-target, but use the local +# cp command instead of acp. +define copy-file-to-new-target-with-cp +@mkdir -p $(dir $@) +$(hide) rm -f $@ +$(hide) cp $< $@ +endef + +# The same as copy-file-to-new-target, but preserve symlinks. Symlinks are +# converted to absolute to not break. +define copy-file-or-link-to-new-target +@mkdir -p $(dir $@) +$(hide) rm -f $@ +$(hide) if [ -h $< ]; then \ + ln -s $$(realpath $<) $@; \ +else \ + cp $< $@; \ +fi +endef + +# Copy a prebuilt file to a target location. +define transform-prebuilt-to-target +@echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)" +$(copy-file-to-target) +endef + +# Copy a prebuilt file to a target location, stripping "# comment" comments. +define transform-prebuilt-to-target-strip-comments +@echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)" +$(copy-file-to-target-strip-comments) +endef + +# Copy a prebuilt file to a target location, but preserve symlinks rather than +# dereference them. +define copy-or-link-prebuilt-to-target +@echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)" +$(copy-file-or-link-to-new-target) +endef + +# Copy a list of files/directories to target location, with sub dir structure preserved. +# For example $(HOST_OUT_EXECUTABLES)/aapt -> $(staging)/bin/aapt . +# $(1): the source list of files/directories. +# $(2): the path prefix to strip. In the above example it would be $(HOST_OUT). +# $(3): the target location. +define copy-files-with-structure +$(foreach t,$(1),\ + $(eval s := $(patsubst $(2)%,%,$(t)))\ + $(hide) mkdir -p $(dir $(3)/$(s)); cp -Rf $(t) $(3)/$(s)$(newline)) +endef + +# Define a rule to create a symlink to a file. +# $(1): any dependencies +# $(2): source (may be relative) +# $(3): full path to destination +define symlink-file +$(eval $(_symlink-file)) +$(eval $(call declare-license-metadata,$(3),,,,,,)) +$(eval $(call declare-license-deps,$(3),$(1))) +endef + +define _symlink-file +$(3): $(1) + @echo "Symlink: $$@ -> $(2)" + @mkdir -p $$(dir $$@) + @rm -rf $$@ + $(hide) ln -sf $(2) $$@ +$(3): .KATI_SYMLINK_OUTPUTS := $(3) +endef + +# Copy an apk to a target location while removing classes*.dex +# $(1): source file +# $(2): destination file +# $(3): LOCAL_STRIP_DEX, if non-empty then strip classes*.dex +define dexpreopt-copy-jar +$(2): $(1) + @echo "Copy: $$@" + $$(copy-file-to-target) + $(if $(3),$$(call dexpreopt-remove-classes.dex,$$@)) +endef + +# $(1): the .jar or .apk to remove classes.dex. Note that if all dex files +# are uncompressed in the archive, then dexopt will not do a copy of the dex +# files and we should not strip. +define dexpreopt-remove-classes.dex +$(hide) if (zipinfo $1 '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ +zip --quiet --delete $(1) classes.dex; \ +dex_index=2; \ +while zip --quiet --delete $(1) classes$${dex_index}.dex > /dev/null; do \ + let dex_index=dex_index+1; \ +done \ +fi +endef + +# Copy an unstripped binary to the symbols directory while also extracting +# a hash mapping to the mapping directory. +# $(1): unstripped intermediates file +# $(2): path in symbols directory +define copy-unstripped-elf-file-with-mapping +$(call _copy-symbols-file-with-mapping,$(1),$(2),\ + elf,$(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(2).textproto)) +endef + +# Copy an R8 dictionary to the packaging directory while also extracting +# a hash mapping to the mapping directory. +# $(1): unstripped intermediates file +# $(2): path in packaging directory +# $(3): path in mappings packaging directory +define copy-r8-dictionary-file-with-mapping +$(call _copy-symbols-file-with-mapping,$(1),$(2),r8,$(3)) +endef + +# Copy an unstripped binary or R8 dictionary to the symbols directory +# while also extracting a hash mapping to the mapping directory. +# $(1): unstripped intermediates file +# $(2): path in symbols directory +# $(3): file type (elf or r8) +# $(4): path in the mappings directory +define _copy-symbols-file-with-mapping +$(2): .KATI_IMPLICIT_OUTPUTS := $(4) +$(2): $(SYMBOLS_MAP) +$(2): $(1) + @echo "Copy symbols with mapping: $$@" + $$(copy-file-to-target) + $(SYMBOLS_MAP) -$(strip $(3)) $(2) -write_if_changed $(4) +.KATI_RESTAT: $(2) +endef + +# Returns the directory to copy proguard dictionaries into +define local-proguard-dictionary-directory +$(call intermediates-dir-for,PACKAGING,proguard_dictionary)/out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates +endef + +# Returns the directory to copy proguard dictionary mappings into +define local-proguard-dictionary-mapping-directory +$(call intermediates-dir-for,PACKAGING,proguard_dictionary_mapping)/out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates +endef + + +########################################################### +## Commands to call R8 +########################################################### + +# Use --debug flag for eng builds by default +ifeq (eng,$(TARGET_BUILD_VARIANT)) +R8_DEBUG_MODE := --debug +else +R8_DEBUG_MODE := +endif + +define transform-jar-to-dex-r8 +@echo R8: $@ +$(hide) rm -f $(PRIVATE_PROGUARD_DICTIONARY) +$(hide) $(R8_WRAPPER) $(R8_COMPAT_PROGUARD) $(DEX_FLAGS) \ + -injars '$<' \ + --min-api $(PRIVATE_MIN_SDK_VERSION) \ + --no-data-resources \ + --force-proguard-compatibility --output $(subst classes.dex,,$@) \ + $(R8_DEBUG_MODE) \ + $(PRIVATE_PROGUARD_FLAGS) \ + $(addprefix -injars , $(PRIVATE_EXTRA_INPUT_JAR)) \ + $(PRIVATE_DX_FLAGS) \ + -ignorewarnings +$(hide) touch $(PRIVATE_PROGUARD_DICTIONARY) +endef + +########################################################### +## Stuff source generated from one-off tools +########################################################### + +define transform-generated-source +@echo "$($(PRIVATE_PREFIX)DISPLAY) Generated: $(PRIVATE_MODULE) <= $<" +@mkdir -p $(dir $@) +$(hide) $(PRIVATE_CUSTOM_TOOL) +endef + + +########################################################### +## Assertions about attributes of the target +########################################################### + +# $(1): The file to check +define get-file-size +stat -c "%s" "$(1)" | tr -d '\n' +endef + +# $(1): The file(s) to check (often $@) +# $(2): The partition size. +define assert-max-image-size +$(if $(2), \ + size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \ + total=$$(( $$( echo "$$size" ) )); \ + printname=$$(echo -n "$(1)" | tr " " +); \ + maxsize=$$(($(2))); \ + if [ "$$total" -gt "$$maxsize" ]; then \ + echo "error: $$printname too large ($$total > $$maxsize)"; \ + false; \ + elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \ + echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \ + fi \ + , \ + true \ + ) +endef + + +########################################################### +## Define device-specific radio files +########################################################### +INSTALLED_RADIOIMAGE_TARGET := + +# Copy a radio image file to the output location, and add it to +# INSTALLED_RADIOIMAGE_TARGET. +# $(1): filename +define add-radio-file + $(eval $(call add-radio-file-internal,$(1),$(notdir $(1)))) +endef +define add-radio-file-internal +INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(2) +$$(PRODUCT_OUT)/$(2) : $$(LOCAL_PATH)/$(1) + $$(transform-prebuilt-to-target) +endef + +# Version of add-radio-file that also arranges for the version of the +# file to be checked against the contents of +# $(TARGET_BOARD_INFO_FILE). +# $(1): filename +# $(2): name of version variable in board-info (eg, "version-baseband") +define add-radio-file-checked + $(eval $(call add-radio-file-checked-internal,$(1),$(notdir $(1)),$(2))) +endef +define add-radio-file-checked-internal +INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(2) +BOARD_INFO_CHECK += $(3):$(LOCAL_PATH)/$(1) +$$(PRODUCT_OUT)/$(2) : $$(LOCAL_PATH)/$(1) + $$(transform-prebuilt-to-target) +endef + +## Whether to build from source if prebuilt alternative exists +########################################################### +# $(1): module name +# $(2): LOCAL_PATH +# Expands to empty string if not from source. +ifeq (true,$(ANDROID_BUILD_FROM_SOURCE)) +define if-build-from-source +true +endef +else +define if-build-from-source +$(if $(filter $(ANDROID_NO_PREBUILT_MODULES),$(1))$(filter \ + $(addsuffix %,$(ANDROID_NO_PREBUILT_PATHS)),$(2)),true) +endef +endif + +# Include makefile $(1) if build from source for module $(2) +# $(1): the makefile to include +# $(2): module name +# $(3): LOCAL_PATH +define include-if-build-from-source +$(if $(call if-build-from-source,$(2),$(3)),$(eval include $(1))) +endef + +# Return the arch for the source file of a prebuilt +# Return "none" if no matching arch found and return empty +# if the input is empty, so the result can be passed to +# LOCAL_MODULE_TARGET_ARCH. +# $(1) the list of archs supported by the prebuilt +define get-prebuilt-src-arch +$(strip $(if $(filter $(TARGET_ARCH),$(1)),$(TARGET_ARCH),\ + $(if $(filter $(TARGET_2ND_ARCH),$(1)),$(TARGET_2ND_ARCH),$(if $(1),none)))) +endef + +# ############################################################### +# Set up statistics gathering +# ############################################################### +STATS.MODULE_TYPE := \ + HOST_STATIC_LIBRARY \ + HOST_SHARED_LIBRARY \ + STATIC_LIBRARY \ + SHARED_LIBRARY \ + EXECUTABLE \ + HOST_EXECUTABLE \ + PACKAGE \ + PHONY_PACKAGE \ + HOST_PREBUILT \ + PREBUILT \ + MULTI_PREBUILT \ + JAVA_LIBRARY \ + STATIC_JAVA_LIBRARY \ + HOST_JAVA_LIBRARY \ + DROIDDOC \ + COPY_HEADERS \ + NATIVE_TEST \ + NATIVE_BENCHMARK \ + HOST_NATIVE_TEST \ + FUZZ_TEST \ + HOST_FUZZ_TEST \ + STATIC_TEST_LIBRARY \ + HOST_STATIC_TEST_LIBRARY \ + NOTICE_FILE \ + HOST_DALVIK_JAVA_LIBRARY \ + HOST_DALVIK_STATIC_JAVA_LIBRARY \ + base_rules \ + HEADER_LIBRARY \ + HOST_TEST_CONFIG \ + TARGET_TEST_CONFIG + +$(foreach s,$(STATS.MODULE_TYPE),$(eval STATS.MODULE_TYPE.$(s) :=)) +define record-module-type +$(strip $(if $(LOCAL_RECORDED_MODULE_TYPE),, + $(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)), + $(if $(filter $(1),$(STATS.MODULE_TYPE)), + $(eval LOCAL_RECORDED_MODULE_TYPE := true) + $(eval STATS.MODULE_TYPE.$(1) += 1), + $(error Invalid module type: $(1)))))) +endef + +########################################################### +## Compatibility suite tools +########################################################### + +# Return a list of output directories for a given suite and the current LOCAL_MODULE. +# Can be passed a subdirectory to use for the common testcase directory. +define compatibility_suite_dirs + $(strip \ + $(if $(COMPATIBILITY_TESTCASES_OUT_$(1)), \ + $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1))$(LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY),\ + $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\ + $(COMPATIBILITY_TESTCASES_OUT_$(1)))) \ + $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(2)) +endef + +# For each suite: +# 1. Copy the files to the many suite output directories. +# And for test config files, we'll check the .xml is well-formed before copy. +# 2. Add all the files to each suite's dependent files list. +# 3. Do the dependency addition to my_all_targets. +# 4. Save the module name to COMPATIBILITY.$(suite).MODULES for each suite. +# 5. Collect files to dist to ALL_COMPATIBILITY_DIST_FILES. +# Requires for each suite: use my_compat_dist_config_$(suite) to define the test config. +# and use my_compat_dist_$(suite) to define the others. +define create-suite-dependencies +$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval $(if $(strip $(module_license_metadata)),\ + $$(foreach f,$$(my_compat_dist_$(suite)),$$(eval ALL_TARGETS.$$(call word-colon,2,$$(f)).META_LIC := $(module_license_metadata))),\ + $$(eval my_test_data += $$(foreach f,$$(my_compat_dist_$(suite)), $$(call word-colon,2,$$(f)))) \ + )) \ + $(eval $(if $(strip $(module_license_metadata)),\ + $$(foreach f,$$(my_compat_dist_config_$(suite)),$$(eval ALL_TARGETS.$$(call word-colon,2,$$(f)).META_LIC := $(module_license_metadata))),\ + $$(eval my_test_config += $$(foreach f,$$(my_compat_dist_config_$(suite)), $$(call word-colon,2,$$(f)))) \ + )) \ + $(if $(filter $(suite),$(ALL_COMPATIBILITY_SUITES)),,\ + $(eval ALL_COMPATIBILITY_SUITES += $(suite)) \ + $(eval COMPATIBILITY.$(suite).FILES :=) \ + $(eval COMPATIBILITY.$(suite).MODULES :=)) \ + $(eval COMPATIBILITY.$(suite).FILES += \ + $$(foreach f,$$(my_compat_dist_$(suite)),$$(call word-colon,2,$$(f))) \ + $$(foreach f,$$(my_compat_dist_config_$(suite)),$$(call word-colon,2,$$(f))) \ + $$(my_compat_dist_test_data_$(suite))) \ + $(eval ALL_COMPATIBILITY_DIST_FILES += $$(my_compat_dist_$(suite))) \ + $(eval COMPATIBILITY.$(suite).MODULES += $$(my_register_name))) \ +$(eval $(my_all_targets) : \ + $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE), \ + $(foreach f,$(my_compat_dist_$(suite)), $(call word-colon,2,$(f))))) \ + $(call copy-many-xml-files-checked, \ + $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE),$(my_compat_dist_config_$(suite)))))) +endef + +########################################################### +## Path Cleaning +########################################################### + +# Remove "dir .." combinations (but keep ".. ..") +# +# $(1): The expanded path, where / is converted to ' ' to work with $(word) +define _clean-path-strip-dotdot +$(strip \ + $(if $(word 2,$(1)), + $(if $(call streq,$(word 2,$(1)),..), + $(if $(call streq,$(word 1,$(1)),..), + $(word 1,$(1)) $(call _clean-path-strip-dotdot,$(wordlist 2,$(words $(1)),$(1))) + , + $(call _clean-path-strip-dotdot,$(wordlist 3,$(words $(1)),$(1))) + ) + , + $(word 1,$(1)) $(call _clean-path-strip-dotdot,$(wordlist 2,$(words $(1)),$(1))) + ) + , + $(1) + ) +) +endef + +# Remove any leading .. from the path (in case of /..) +# +# Should only be called if the original path started with / +# $(1): The expanded path, where / is converted to ' ' to work with $(word) +define _clean-path-strip-root-dotdots +$(strip $(if $(call streq,$(firstword $(1)),..), + $(call _clean-path-strip-root-dotdots,$(wordlist 2,$(words $(1)),$(1))), + $(1))) +endef + +# Call _clean-path-strip-dotdot until the path stops changing +# $(1): Non-empty if this path started with a / +# $(2): The expanded path, where / is converted to ' ' to work with $(word) +define _clean-path-expanded +$(strip \ + $(eval _ep := $(call _clean-path-strip-dotdot,$(2))) + $(if $(1),$(eval _ep := $(call _clean-path-strip-root-dotdots,$(_ep)))) + $(if $(call streq,$(2),$(_ep)), + $(_ep), + $(call _clean-path-expanded,$(1),$(_ep)))) +endef + +# Clean the file path -- remove //, dir/.., extra . +# +# This should be the same semantics as golang's filepath.Clean +# +# $(1): The file path to clean +define clean-path +$(strip \ + $(if $(call streq,$(words $(1)),1), + $(eval _rooted := $(filter /%,$(1))) + $(eval _expanded_path := $(filter-out .,$(subst /,$(space),$(1)))) + $(eval _path := $(if $(_rooted),/)$(subst $(space),/,$(call _clean-path-expanded,$(_rooted),$(_expanded_path)))) + $(if $(_path), + $(_path), + . + ) + , + $(if $(call streq,$(words $(1)),0), + ., + $(error Call clean-path with only one path (without spaces)) + ) + ) +) +endef + +ifeq ($(TEST_MAKE_clean_path),true) + define my_test + $(if $(call streq,$(call clean-path,$(1)),$(2)),, + $(eval my_failed := true) + $(warning clean-path test '$(1)': expected '$(2)', got '$(call clean-path,$(1))')) + endef + my_failed := + + # Already clean + $(call my_test,abc,abc) + $(call my_test,abc/def,abc/def) + $(call my_test,a/b/c,a/b/c) + $(call my_test,.,.) + $(call my_test,..,..) + $(call my_test,../..,../..) + $(call my_test,../../abc,../../abc) + $(call my_test,/abc,/abc) + $(call my_test,/,/) + + # Empty is current dir + $(call my_test,,.) + + # Remove trailing slash + $(call my_test,abc/,abc) + $(call my_test,abc/def/,abc/def) + $(call my_test,a/b/c/,a/b/c) + $(call my_test,./,.) + $(call my_test,../,..) + $(call my_test,../../,../..) + $(call my_test,/abc/,/abc) + + # Remove doubled slash + $(call my_test,abc//def//ghi,abc/def/ghi) + $(call my_test,//abc,/abc) + $(call my_test,///abc,/abc) + $(call my_test,//abc//,/abc) + $(call my_test,abc//,abc) + + # Remove . elements + $(call my_test,abc/./def,abc/def) + $(call my_test,/./abc/def,/abc/def) + $(call my_test,abc/.,abc) + + # Remove .. elements + $(call my_test,abc/def/ghi/../jkl,abc/def/jkl) + $(call my_test,abc/def/../ghi/../jkl,abc/jkl) + $(call my_test,abc/def/..,abc) + $(call my_test,abc/def/../..,.) + $(call my_test,/abc/def/../..,/) + $(call my_test,abc/def/../../..,..) + $(call my_test,/abc/def/../../..,/) + $(call my_test,abc/def/../../../ghi/jkl/../../../mno,../../mno) + $(call my_test,/../abc,/abc) + + # Combinations + $(call my_test,abc/./../def,def) + $(call my_test,abc//./../def,def) + $(call my_test,abc/../../././../def,../../def) + + ifdef my_failed + $(error failed clean-path test) + endif +endif + +########################################################### +## Given a filepath, returns nonempty if the path cannot be +## validated to be contained in the current directory +## This is, this function checks for '/' and '..' +## +## $(1): path to validate +define try-validate-path-is-subdir +$(strip \ + $(if $(filter /%,$(1)), + $(1) starts with a slash + ) + $(if $(filter ../%,$(call clean-path,$(1))), + $(1) escapes its parent using '..' + ) + $(if $(strip $(1)), + , + '$(1)' is empty + ) +) +endef + +define validate-path-is-subdir +$(if $(call try-validate-path-is-subdir,$(1)), + $(call pretty-error, Illegal path: $(call try-validate-path-is-subdir,$(1))) +) +endef + +########################################################### +## Given a space-delimited list of filepaths, returns +## nonempty if any cannot be validated to be contained in +## the current directory +## +## $(1): path list to validate +define try-validate-paths-are-subdirs +$(strip \ + $(foreach my_path,$(1),\ + $(call try-validate-path-is-subdir,$(my_path))\ + ) +) +endef + +define validate-paths-are-subdirs +$(if $(call try-validate-paths-are-subdirs,$(1)), + $(call pretty-error,Illegal paths:\'$(call try-validate-paths-are-subdirs,$(1))\') +) +endef + +########################################################### +## Tests of try-validate-path-is-subdir +## and try-validate-paths-are-subdirs +define test-validate-paths-are-subdirs +$(eval my_error := $(call try-validate-path-is-subdir,/tmp)) \ +$(if $(call streq,$(my_error),/tmp starts with a slash), +, + $(error incorrect error message for path /tmp. Got '$(my_error)') +) \ +$(eval my_error := $(call try-validate-path-is-subdir,../sibling)) \ +$(if $(call streq,$(my_error),../sibling escapes its parent using '..'), +, + $(error incorrect error message for path ../sibling. Got '$(my_error)') +) \ +$(eval my_error := $(call try-validate-path-is-subdir,child/../../sibling)) \ +$(if $(call streq,$(my_error),child/../../sibling escapes its parent using '..'), +, + $(error incorrect error message for path child/../../sibling. Got '$(my_error)') +) \ +$(eval my_error := $(call try-validate-path-is-subdir,)) \ +$(if $(call streq,$(my_error),'' is empty), +, + $(error incorrect error message for empty path ''. Got '$(my_error)') +) \ +$(eval my_error := $(call try-validate-path-is-subdir,subdir/subsubdir)) \ +$(if $(call streq,$(my_error),), +, + $(error rejected valid path 'subdir/subsubdir'. Got '$(my_error)') +) + +$(eval my_error := $(call try-validate-paths-are-subdirs,a/b /c/d e/f)) +$(if $(call streq,$(my_error),/c/d starts with a slash), +, + $(error incorrect error message for path list 'a/b /c/d e/f'. Got '$(my_error)') +) +$(eval my_error := $(call try-validate-paths-are-subdirs,a/b c/d)) +$(if $(call streq,$(my_error),), +, + $(error rejected valid path list 'a/b c/d'. Got '$(my_error)') +) +endef +# run test +$(strip $(call test-validate-paths-are-subdirs)) + +########################################################### +## Validate jacoco class filters and convert them to +## file arguments +## Jacoco class filters are comma-separated lists of class +## files (android.app.Application), and may have '*' as the +## last character to match all classes in a package +## including subpackages. +define jacoco-class-filter-to-file-args +$(strip $(call jacoco-validate-file-args,\ + $(subst $(comma),$(space),\ + $(subst .,/,\ + $(strip $(1)))))) +endef + +define jacoco-validate-file-args +$(strip $(1)\ + $(call validate-paths-are-subdirs,$(1)) + $(foreach arg,$(1),\ + $(if $(findstring ?,$(arg)),$(call pretty-error,\ + '?' filters are not supported in LOCAL_JACK_COVERAGE_INCLUDE_FILTER or LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\ + $(if $(findstring *,$(patsubst %*,%,$(arg))),$(call pretty-error,\ + '*' is only supported at the end of a filter in LOCAL_JACK_COVERAGE_INCLUDE_FILTER or LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\ + )) +endef + +########################################################### +## Other includes +########################################################### + +# Include any vendor specific definitions.mk file +-include $(TOPDIR)vendor/*/build/core/definitions.mk +-include $(TOPDIR)device/*/build/core/definitions.mk +-include $(TOPDIR)product/*/build/core/definitions.mk + +# broken: +# $(foreach file,$^,$(if $(findstring,.a,$(suffix $file)),-l$(file),$(file))) + +########################################################### +## Misc notes +########################################################### + +#DEPDIR = .deps +#df = $(DEPDIR)/$(*F) + +#SRCS = foo.c bar.c ... + +#%.o : %.c +# @$(MAKEDEPEND); \ +# cp $(df).d $(df).P; \ +# sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ +# -e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \ +# rm -f $(df).d +# $(COMPILE.c) -o $@ $< + +#-include $(SRCS:%.c=$(DEPDIR)/%.P) + + +#%.o : %.c +# $(COMPILE.c) -MD -o $@ $< +# @cp $*.d $*.P; \ +# sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ +# -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \ +# rm -f $*.d + + +########################################################### +# Append the information to generate a RRO package for the +# source module. +# +# $(1): Source module name. +# $(2): Whether $(3) is a manifest package name or not. +# $(3): Manifest package name if $(2) is true. +# Otherwise, android manifest file path of the +# source module. +# $(4): Whether LOCAL_EXPORT_PACKAGE_RESOURCES is set or +# not for the source module. +# $(5): Resource overlay list. +# $(6): Target partition +########################################################### +define append_enforce_rro_sources + $(eval ENFORCE_RRO_SOURCES += \ + $(strip $(1))||$(strip $(2))||$(strip $(3))||$(strip $(4))||$(call normalize-path-list, $(strip $(5)))||$(strip $(6)) \ + ) +endef + +########################################################### +# Generate all RRO packages for source modules stored in +# ENFORCE_RRO_SOURCES +########################################################### +define generate_all_enforce_rro_packages +$(foreach source,$(ENFORCE_RRO_SOURCES), \ + $(eval _o := $(subst ||,$(space),$(source))) \ + $(eval enforce_rro_source_module := $(word 1,$(_o))) \ + $(eval enforce_rro_source_is_manifest_package_name := $(word 2,$(_o))) \ + $(eval enforce_rro_source_manifest_package_info := $(word 3,$(_o))) \ + $(eval enforce_rro_use_res_lib := $(word 4,$(_o))) \ + $(eval enforce_rro_source_overlays := $(subst :, ,$(word 5,$(_o)))) \ + $(eval enforce_rro_partition := $(word 6,$(_o))) \ + $(eval include $(BUILD_SYSTEM)/generate_enforce_rro.mk) \ + $(eval ALL_MODULES.$$(enforce_rro_source_module).REQUIRED_FROM_TARGET += $$(LOCAL_PACKAGE_NAME)) \ +) +endef + +########################################################### +## Find system_$(VER) in LOCAL_SDK_VERSION +## note: system_server_* is excluded. It's a different API surface +## +## $(1): LOCAL_SDK_VERSION +########################################################### +define has-system-sdk-version +$(filter-out system_server_%,$(filter system_%,$(1))) +endef + +########################################################### +## Get numerical version in LOCAL_SDK_VERSION +## +## $(1): LOCAL_SDK_VERSION +########################################################### +define get-numeric-sdk-version +$(filter-out current,\ + $(if $(call has-system-sdk-version,$(1)),$(patsubst system_%,%,$(1)),$(1))) +endef + +########################################################### +## Verify module name meets character requirements: +## a-z A-Z 0-9 +## _.+-,@~ +## +## This is a subset of bazel's target name restrictions: +## https://docs.bazel.build/versions/master/build-ref.html#name +## +## Kati has problems with '=': https://github.com/google/kati/issues/138 +########################################################### +define verify-module-name +$(if $(filter-out $(LOCAL_MODULE),$(subst /,,$(LOCAL_MODULE))), \ + $(call pretty-warning,Module name contains a /$(comma) use LOCAL_MODULE_STEM and LOCAL_MODULE_RELATIVE_PATH instead)) \ +$(if $(call _invalid-name-chars,$(LOCAL_MODULE)), \ + $(call pretty-error,Invalid characters in module name: $(call _invalid-name-chars,$(LOCAL_MODULE)))) +endef +define _invalid-name-chars +$(subst _,,$(subst .,,$(subst +,,$(subst -,,$(subst $(comma),,$(subst @,,$(subst ~,,$(subst 0,,$(subst 1,,$(subst 2,,$(subst 3,,$(subst 4,,$(subst 5,,$(subst 6,,$(subst 7,,$(subst 8,,$(subst 9,,$(subst a,,$(subst b,,$(subst c,,$(subst d,,$(subst e,,$(subst f,,$(subst g,,$(subst h,,$(subst i,,$(subst j,,$(subst k,,$(subst l,,$(subst m,,$(subst n,,$(subst o,,$(subst p,,$(subst q,,$(subst r,,$(subst s,,$(subst t,,$(subst u,,$(subst v,,$(subst w,,$(subst x,,$(subst y,,$(subst z,,$(call to-lower,$(1))))))))))))))))))))))))))))))))))))))))))))) +endef +.KATI_READONLY := verify-module-name _invalid-name-chars + +########################################################### +## Verify module stem meets character requirements: +## a-z A-Z 0-9 +## _.+-,@~ +## +## This is a subset of bazel's target name restrictions: +## https://docs.bazel.build/versions/master/build-ref.html#name +## +## $(1): The module stem variable to check +########################################################### +define verify-module-stem +$(if $(filter-out $($(1)),$(subst /,,$($(1)))), \ + $(call pretty-warning,Module stem \($(1)\) contains a /$(comma) use LOCAL_MODULE_RELATIVE_PATH instead)) \ +$(if $(call _invalid-name-chars,$($(1))), \ + $(call pretty-error,Invalid characters in module stem \($(1)\): $(call _invalid-name-chars,$($(1))))) +endef +.KATI_READONLY := verify-module-stem + +$(KATI_obsolete_var \ + create-empty-package \ + initialize-package-file \ + add-jni-shared-libs-to-package \ + inherit-package,\ + These functions have been removed) + +########################################################### +## Verify the variants of a VNDK library are identical +## +## $(1): Path to the core variant shared library file. +## $(2): Path to the vendor variant shared library file. +## $(3): TOOLS_PREFIX +########################################################### +LIBRARY_IDENTITY_CHECK_SCRIPT := build/make/tools/check_identical_lib.sh +define verify-vndk-libs-identical +@echo "Checking VNDK vendor variant: $(2)" +$(hide) CLANG_BIN="$(LLVM_PREBUILTS_PATH)" \ + CROSS_COMPILE="$(strip $(3))" \ + XZ="$(XZ)" \ + $(LIBRARY_IDENTITY_CHECK_SCRIPT) $(SOONG_STRIP_PATH) $(1) $(2) +endef + +# Convert Soong libraries that have SDK variant +define use_soong_sdk_libraries + $(foreach l,$(1),$(if $(filter $(l),$(SOONG_SDK_VARIANT_MODULES)),\ + $(l).sdk,$(l))) +endef diff --git a/make/core/deprecation.mk b/make/core/deprecation.mk new file mode 100644 index 0000000..2b7a869 --- /dev/null +++ b/make/core/deprecation.mk @@ -0,0 +1,55 @@ +# These module types can still be used without warnings or errors. +AVAILABLE_BUILD_MODULE_TYPES :=$= \ + BUILD_EXECUTABLE \ + BUILD_FUZZ_TEST \ + BUILD_HEADER_LIBRARY \ + BUILD_HOST_DALVIK_JAVA_LIBRARY \ + BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY \ + BUILD_HOST_JAVA_LIBRARY \ + BUILD_HOST_PREBUILT \ + BUILD_JAVA_LIBRARY \ + BUILD_MULTI_PREBUILT \ + BUILD_NATIVE_TEST \ + BUILD_NOTICE_FILE \ + BUILD_PACKAGE \ + BUILD_PHONY_PACKAGE \ + BUILD_PREBUILT \ + BUILD_RRO_PACKAGE \ + BUILD_SHARED_LIBRARY \ + BUILD_STATIC_JAVA_LIBRARY \ + BUILD_STATIC_LIBRARY \ + +# These are BUILD_* variables that will throw a warning when used. This is +# generally a temporary state until all the devices are marked with the +# relevant BUILD_BROKEN_USES_BUILD_* variables, then these would move to +# DEFAULT_ERROR_BUILD_MODULE_TYPES. +DEFAULT_WARNING_BUILD_MODULE_TYPES :=$= \ + +# These are BUILD_* variables that are errors to reference, but you can set +# BUILD_BROKEN_USES_BUILD_* in your BoardConfig.mk in order to turn them back +# to warnings. +DEFAULT_ERROR_BUILD_MODULE_TYPES :=$= \ + BUILD_COPY_HEADERS \ + BUILD_HOST_EXECUTABLE \ + BUILD_HOST_SHARED_LIBRARY \ + BUILD_HOST_STATIC_LIBRARY \ + +# These are BUILD_* variables that are always errors to reference. +# Setting the BUILD_BROKEN_USES_BUILD_* variables is also an error. +OBSOLETE_BUILD_MODULE_TYPES :=$= \ + BUILD_AUX_EXECUTABLE \ + BUILD_AUX_STATIC_LIBRARY \ + BUILD_HOST_FUZZ_TEST \ + BUILD_HOST_NATIVE_TEST \ + BUILD_HOST_SHARED_TEST_LIBRARY \ + BUILD_HOST_STATIC_TEST_LIBRARY \ + BUILD_HOST_TEST_CONFIG \ + BUILD_NATIVE_BENCHMARK \ + BUILD_SHARED_TEST_LIBRARY \ + BUILD_STATIC_TEST_LIBRARY \ + BUILD_TARGET_TEST_CONFIG \ + +$(foreach m,$(OBSOLETE_BUILD_MODULE_TYPES),\ + $(KATI_obsolete_var $(m),Please convert to Soong) \ + $(KATI_obsolete_var BUILD_BROKEN_USES_$(m),Please convert to Soong)) + diff --git a/make/core/device.mk b/make/core/device.mk new file mode 100644 index 0000000..20ff447 --- /dev/null +++ b/make/core/device.mk @@ -0,0 +1,76 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +_device_var_list := \ + DEVICE_NAME \ + DEVICE_BOARD \ + DEVICE_REGION + +define dump-device +$(info ==== $(1) ====)\ +$(foreach v,$(_device_var_list),\ +$(info DEVICES.$(1).$(v) := $(DEVICES.$(1).$(v))))\ +$(info --------) +endef + +define dump-devices +$(foreach p,$(DEVICES),$(call dump-device,$(p))) +endef + +# +# $(1): device to inherit +# +define inherit-device + $(foreach v,$(_device_var_list), \ + $(eval $(v) := $($(v)) $(INHERIT_TAG)$(strip $(1)))) +endef + +# +# $(1): device makefile list +# +#TODO: check to make sure that devices have all the necessary vars defined +define import-devices +$(call import-nodes,DEVICES,$(1),$(_device_var_list)) +endef + + +# +# $(1): short device name like "sooner" +# +define _resolve-short-device-name + $(eval dn := $(strip $(1))) + $(eval d := \ + $(foreach d,$(DEVICES), \ + $(if $(filter $(dn),$(DEVICES.$(d).DEVICE_NAME)), \ + $(d) \ + )) \ + ) + $(eval d := $(sort $(d))) + $(if $(filter 1,$(words $(d))), \ + $(d), \ + $(if $(filter 0,$(words $(d))), \ + $(error No matches for device "$(dn)"), \ + $(error Device "$(dn)" ambiguous: matches $(d)) \ + ) \ + ) +endef + +# +# $(1): short device name like "sooner" +# +define resolve-short-device-name +$(strip $(call _resolve-short-device-name,$(1))) +endef diff --git a/make/core/dex_preopt.mk b/make/core/dex_preopt.mk new file mode 100644 index 0000000..593ad66 --- /dev/null +++ b/make/core/dex_preopt.mk @@ -0,0 +1,83 @@ +#################################### +# dexpreopt support - typically used on user builds to run dexopt (for Dalvik) or dex2oat (for ART) ahead of time +# +#################################### + +include $(BUILD_SYSTEM)/dex_preopt_config.mk + +# Method returning whether the install path $(1) should be for system_other. +# Under SANITIZE_LITE, we do not want system_other. Just put things under /data/asan. +ifeq ($(SANITIZE_LITE),true) +install-on-system-other = +else +install-on-system-other = $(filter-out $(PRODUCT_DEXPREOPT_SPEED_APPS) $(PRODUCT_SYSTEM_SERVER_APPS),$(basename $(notdir $(filter $(foreach f,$(SYSTEM_OTHER_ODEX_FILTER),$(TARGET_OUT)/$(f)),$(1))))) +endif + +# We want to install the profile even if we are not using preopt since it is required to generate +# the image on the device. +ALL_DEFAULT_INSTALLED_MODULES += $(call copy-many-files,$(DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED),$(PRODUCT_OUT)) + +# Install boot images. Note that there can be multiple. +my_boot_image_arch := TARGET_ARCH +my_boot_image_out := $(PRODUCT_OUT) +my_boot_image_syms := $(TARGET_OUT_UNSTRIPPED) +DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE := \ + $(foreach my_boot_image_name,$(DEXPREOPT_IMAGE_NAMES),$(strip \ + $(eval include $(BUILD_SYSTEM)/dex_preopt_libart.mk) \ + $(my_boot_image_module))) +ifdef TARGET_2ND_ARCH + my_boot_image_arch := TARGET_2ND_ARCH + 2ND_DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE := \ + $(foreach my_boot_image_name,$(DEXPREOPT_IMAGE_NAMES),$(strip \ + $(eval include $(BUILD_SYSTEM)/dex_preopt_libart.mk) \ + $(my_boot_image_module))) +endif +# Install boot images for testing on host. We exclude framework image as it is not part of art manifest. +my_boot_image_arch := HOST_ARCH +my_boot_image_out := $(HOST_OUT) +my_boot_image_syms := $(HOST_OUT)/symbols +HOST_BOOT_IMAGE_MODULE := \ + $(foreach my_boot_image_name,art_host,$(strip \ + $(eval include $(BUILD_SYSTEM)/dex_preopt_libart.mk) \ + $(my_boot_image_module))) +HOST_BOOT_IMAGE := $(call module-installed-files,$(HOST_BOOT_IMAGE_MODULE)) +ifdef HOST_2ND_ARCH + my_boot_image_arch := HOST_2ND_ARCH + 2ND_HOST_BOOT_IMAGE_MODULE := \ + $(foreach my_boot_image_name,art_host,$(strip \ + $(eval include $(BUILD_SYSTEM)/dex_preopt_libart.mk) \ + $(my_boot_image_module))) + 2ND_HOST_BOOT_IMAGE := $(call module-installed-files,$(2ND_HOST_BOOT_IMAGE_MODULE)) +endif +my_boot_image_arch := +my_boot_image_out := +my_boot_image_syms := +my_boot_image_module := + +# Build the boot.zip which contains the boot jars and their compilation output +# We can do this only if preopt is enabled and if the product uses libart config (which sets the +# default properties for preopting). +ifeq ($(WITH_DEXPREOPT), true) +ifeq ($(PRODUCT_USES_DEFAULT_ART_CONFIG), true) + +boot_zip := $(PRODUCT_OUT)/boot.zip +bootclasspath_jars := $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES) +system_server_jars := \ + $(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),\ + $(PRODUCT_OUT)/system/framework/$(call word-colon,2,$(m)).jar) + +$(boot_zip): PRIVATE_BOOTCLASSPATH_JARS := $(bootclasspath_jars) +$(boot_zip): PRIVATE_SYSTEM_SERVER_JARS := $(system_server_jars) +$(boot_zip): $(bootclasspath_jars) $(system_server_jars) $(SOONG_ZIP) $(MERGE_ZIPS) $(DEXPREOPT_IMAGE_ZIP_boot) $(DEXPREOPT_IMAGE_ZIP_art) + @echo "Create boot package: $@" + rm -f $@ + $(SOONG_ZIP) -o $@.tmp \ + -C $(dir $(firstword $(PRIVATE_BOOTCLASSPATH_JARS)))/.. $(addprefix -f ,$(PRIVATE_BOOTCLASSPATH_JARS)) \ + -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) + $(MERGE_ZIPS) $@ $@.tmp $(DEXPREOPT_IMAGE_ZIP_boot) $(DEXPREOPT_IMAGE_ZIP_art) + rm -f $@.tmp + +$(call dist-for-goals, droidcore, $(boot_zip)) + +endif #PRODUCT_USES_DEFAULT_ART_CONFIG +endif #WITH_DEXPREOPT diff --git a/make/core/dex_preopt_config.mk b/make/core/dex_preopt_config.mk new file mode 100644 index 0000000..d5293cf --- /dev/null +++ b/make/core/dex_preopt_config.mk @@ -0,0 +1,167 @@ +DEX_PREOPT_CONFIG := $(SOONG_OUT_DIR)/dexpreopt.config + +ENABLE_PREOPT := true +ENABLE_PREOPT_BOOT_IMAGES := true +ifneq (true,$(filter true,$(WITH_DEXPREOPT))) + # Disable dexpreopt for libraries/apps and for boot images. + ENABLE_PREOPT := + ENABLE_PREOPT_BOOT_IMAGES := +else ifneq (true,$(filter true,$(PRODUCT_USES_DEFAULT_ART_CONFIG))) + # Disable dexpreopt for libraries/apps and for boot images: not having default + # ART config means that some important system properties are not set, which + # would result in passing bad arguments to dex2oat and failing the build. + ENABLE_PREOPT := + ENABLE_PREOPT_BOOT_IMAGES := +else ifeq (true,$(DISABLE_PREOPT)) + # Disable dexpreopt for libraries/apps, but do compile boot images. + ENABLE_PREOPT := +endif + +# The default value for LOCAL_DEX_PREOPT +DEX_PREOPT_DEFAULT ?= $(ENABLE_PREOPT) + +# Whether to fail immediately if verify_uses_libraries check fails, or to keep +# going and restrict dexpreopt to not compile any code for the failed module. +# +# The intended use case for this flag is to have a smoother migration path for +# the Java modules that need to add information in their build +# files. The flag allows to quickly silence build errors. This flag should be +# used with caution and only as a temporary measure, as it masks real errors +# and affects performance. +ifndef RELAX_USES_LIBRARY_CHECK + RELAX_USES_LIBRARY_CHECK := $(if \ + $(filter true,$(PRODUCT_BROKEN_VERIFY_USES_LIBRARIES)),true,false) +else + # Let the environment variable override PRODUCT_BROKEN_VERIFY_USES_LIBRARIES. +endif +.KATI_READONLY := RELAX_USES_LIBRARY_CHECK + +# The default filter for which files go into the system_other image (if it is +# being used). Note that each pattern p here matches both '/

' and /system/

'. +# To bundle everything one should set this to '%'. +SYSTEM_OTHER_ODEX_FILTER ?= \ + app/% \ + priv-app/% \ + system_ext/app/% \ + system_ext/priv-app/% \ + product/app/% \ + product/priv-app/% \ + +# Global switch to control if updatable boot jars are included in dexpreopt. +DEX_PREOPT_WITH_UPDATABLE_BCP := true + +# Conditional to building on linux, as dex2oat currently does not work on darwin. +ifeq ($(HOST_OS),linux) + ifeq (eng,$(TARGET_BUILD_VARIANT)) + # For an eng build only pre-opt the boot image and system server. This gives reasonable performance + # and still allows a simple workflow: building in frameworks/base and syncing. + WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY ?= true + endif + # Add mini-debug-info to the boot classpath unless explicitly asked not to. + ifneq (false,$(WITH_DEXPREOPT_DEBUG_INFO)) + PRODUCT_DEX_PREOPT_BOOT_FLAGS += --generate-mini-debug-info + endif + + # Non eng linux builds must have preopt enabled so that system server doesn't run as interpreter + # only. b/74209329 + ifeq (,$(filter eng, $(TARGET_BUILD_VARIANT))) + ifneq (true,$(WITH_DEXPREOPT)) + ifneq (true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY)) + $(call pretty-error, DEXPREOPT must be enabled for user and userdebug builds) + endif + endif + endif +endif + +# Get value of a property. It is first searched from PRODUCT_VENDOR_PROPERTIES +# and then falls back to PRODUCT_SYSTEM_PROPERTIES +# $1: name of the property +define get-product-default-property +$(strip \ + $(eval _prop := $(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_VENDOR_PROPERTIES))))\ + $(if $(_prop),$(_prop),$(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_SYSTEM_PROPERTIES))))) +endef + +DEX2OAT_IMAGE_XMS := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xms) +DEX2OAT_IMAGE_XMX := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xmx) +DEX2OAT_XMS := $(call get-product-default-property,dalvik.vm.dex2oat-Xms) +DEX2OAT_XMX := $(call get-product-default-property,dalvik.vm.dex2oat-Xmx) + +ifeq ($(WRITE_SOONG_VARIABLES),true) + + $(call json_start) + + $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT))) + $(call add_json_bool, DisablePreoptBootImages, $(call invert_bool,$(ENABLE_PREOPT_BOOT_IMAGES))) + $(call add_json_list, DisablePreoptModules, $(DEXPREOPT_DISABLED_MODULES)) + $(call add_json_bool, OnlyPreoptBootImageAndSystemServer, $(filter true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY))) + $(call add_json_bool, PreoptWithUpdatableBcp, $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP))) + $(call add_json_bool, UseArtImage, $(filter true,$(DEXPREOPT_USE_ART_IMAGE))) + $(call add_json_bool, DontUncompressPrivAppsDex, $(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))) + $(call add_json_list, ModulesLoadedByPrivilegedModules, $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES)) + $(call add_json_bool, HasSystemOther, $(BOARD_USES_SYSTEM_OTHER_ODEX)) + $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER)) + $(call add_json_bool, DisableGenerateProfile, $(filter false,$(WITH_DEX_PREOPT_GENERATE_PROFILE))) + $(call add_json_str, ProfileDir, $(PRODUCT_DEX_PREOPT_PROFILE_DIR)) + $(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS)) + $(call add_json_list, ApexBootJars, $(PRODUCT_APEX_BOOT_JARS)) + $(call add_json_list, ArtApexJars, $(filter $(PRODUCT_BOOT_JARS),$(ART_APEX_JARS))) + $(call add_json_list, SystemServerJars, $(PRODUCT_SYSTEM_SERVER_JARS)) + $(call add_json_list, SystemServerApps, $(PRODUCT_SYSTEM_SERVER_APPS)) + $(call add_json_list, ApexSystemServerJars, $(PRODUCT_APEX_SYSTEM_SERVER_JARS)) + $(call add_json_list, StandaloneSystemServerJars, $(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS)) + $(call add_json_list, ApexStandaloneSystemServerJars, $(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS)) + $(call add_json_bool, BrokenSuboptimalOrderOfSystemServerJars, $(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS)) + $(call add_json_list, SpeedApps, $(PRODUCT_DEXPREOPT_SPEED_APPS)) + $(call add_json_list, PreoptFlags, $(PRODUCT_DEX_PREOPT_DEFAULT_FLAGS)) + $(call add_json_str, DefaultCompilerFilter, $(PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER)) + $(call add_json_str, SystemServerCompilerFilter, $(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)) + $(call add_json_bool, GenerateDmFiles, $(PRODUCT_DEX_PREOPT_GENERATE_DM_FILES)) + $(call add_json_bool, NeverAllowStripping, $(PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING)) + $(call add_json_bool, NoDebugInfo, $(filter false,$(WITH_DEXPREOPT_DEBUG_INFO))) + $(call add_json_bool, DontResolveStartupStrings, $(filter false,$(PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS))) + $(call add_json_bool, AlwaysSystemServerDebugInfo, $(filter true,$(PRODUCT_SYSTEM_SERVER_DEBUG_INFO))) + $(call add_json_bool, NeverSystemServerDebugInfo, $(filter false,$(PRODUCT_SYSTEM_SERVER_DEBUG_INFO))) + $(call add_json_bool, AlwaysOtherDebugInfo, $(filter true,$(PRODUCT_OTHER_JAVA_DEBUG_INFO))) + $(call add_json_bool, NeverOtherDebugInfo, $(filter false,$(PRODUCT_OTHER_JAVA_DEBUG_INFO))) + $(call add_json_bool, IsEng, $(filter eng,$(TARGET_BUILD_VARIANT))) + $(call add_json_bool, SanitizeLite, $(SANITIZE_LITE)) + $(call add_json_bool, DefaultAppImages, $(WITH_DEX_PREOPT_APP_IMAGE)) + $(call add_json_bool, RelaxUsesLibraryCheck, $(filter true,$(RELAX_USES_LIBRARY_CHECK))) + $(call add_json_str, Dex2oatXmx, $(DEX2OAT_XMX)) + $(call add_json_str, Dex2oatXms, $(DEX2OAT_XMS)) + $(call add_json_str, EmptyDirectory, $(OUT_DIR)/empty) + +ifdef TARGET_ARCH + $(call add_json_map, CpuVariant) + $(call add_json_str, $(TARGET_ARCH), $(DEX2OAT_TARGET_CPU_VARIANT)) + ifdef TARGET_2ND_ARCH + $(call add_json_str, $(TARGET_2ND_ARCH), $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT)) + endif + $(call end_json_map) + + $(call add_json_map, InstructionSetFeatures) + $(call add_json_str, $(TARGET_ARCH), $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)) + ifdef TARGET_2ND_ARCH + $(call add_json_str, $(TARGET_2ND_ARCH), $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)) + endif + $(call end_json_map) +endif + + $(call add_json_list, BootImageProfiles, $(PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION)) + $(call add_json_str, BootFlags, $(PRODUCT_DEX_PREOPT_BOOT_FLAGS)) + $(call add_json_str, Dex2oatImageXmx, $(DEX2OAT_IMAGE_XMX)) + $(call add_json_str, Dex2oatImageXms, $(DEX2OAT_IMAGE_XMS)) + + $(call json_end) + + $(shell mkdir -p $(dir $(DEX_PREOPT_CONFIG))) + $(file >$(DEX_PREOPT_CONFIG).tmp,$(json_contents)) + + $(shell \ + if ! cmp -s $(DEX_PREOPT_CONFIG).tmp $(DEX_PREOPT_CONFIG); then \ + mv $(DEX_PREOPT_CONFIG).tmp $(DEX_PREOPT_CONFIG); \ + else \ + rm $(DEX_PREOPT_CONFIG).tmp; \ + fi) +endif diff --git a/make/core/dex_preopt_config_merger.py b/make/core/dex_preopt_config_merger.py new file mode 100755 index 0000000..4efcc17 --- /dev/null +++ b/make/core/dex_preopt_config_merger.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +""" +A tool for merging dexpreopt.config files for dependencies into +the dexpreopt.config file of the library/app that uses them. This is needed to +generate class loader context (CLC) for dexpreopt. + +In Make there is no topological order when processing different modules, so a + dependency module may have not been processed yet by the time the +dependent module is processed. Therefore makefiles communicate the information +from dependencies via dexpreopt.config files and add file-level dependencies +from a module dexpreopt.config to its dependency configs. The actual patching +of configs is done by this script, which is called from the makefiles. +""" + +from __future__ import print_function + +import json +from collections import OrderedDict +import sys + + +def main(): + """Program entry point.""" + if len(sys.argv) < 2: + raise SystemExit('usage: %s [dep-config ...]' % sys.argv[0]) + + # Read all JSON configs. + cfgs = [] + for arg in sys.argv[1:]: + with open(arg, 'r') as f: + cfgs.append(json.load(f, object_pairs_hook=OrderedDict)) + + # The first config is the dexpreopted library/app, the rest are its + # dependencies. + cfg0 = cfgs[0] + + # Put dependency configs in a map keyed on module name (for easier lookup). + uses_libs = {} + for cfg in cfgs[1:]: + uses_libs[cfg['Name']] = cfg + + # Load the original CLC map. + clc_map = cfg0['ClassLoaderContexts'] + + # Create a new CLC map that will be a copy of the original one with patched + # fields from dependency dexpreopt.config files. + clc_map2 = OrderedDict() + + # Patch CLC for each SDK version. Although this should not be necessary for + # compatibility libraries (so-called "conditional CLC"), because they all have + # known names, known paths in system/framework, and no subcontext. But keep + # the loop in case this changes in the future. + for sdk_ver in clc_map: + clcs = clc_map[sdk_ver] + clcs2 = [] + for clc in clcs: + lib = clc['Name'] + if lib in uses_libs: + ulib = uses_libs[lib] + # The real name (may be different from the module name). + clc['Name'] = ulib['ProvidesUsesLibrary'] + # On-device (install) path to the dependency DEX jar file. + clc['Device'] = ulib['DexLocation'] + # CLC of the dependency becomes a subcontext. We only need sub-CLC for + # 'any' version because all other versions are for compatibility + # libraries, which exist only for apps and not for libraries. + clc['Subcontexts'] = ulib['ClassLoaderContexts'].get('any') + else: + # dexpreopt.config for this is not among the script + # arguments, which may be the case with compatibility libraries that + # don't need patching anyway. Just use the original CLC. + pass + clcs2.append(clc) + clc_map2[sdk_ver] = clcs2 + + # Overwrite the original class loader context with the patched one. + cfg0['ClassLoaderContexts'] = clc_map2 + + # Update dexpreopt.config file. + with open(sys.argv[1], 'w') as f: + f.write(json.dumps(cfgs[0], indent=4, separators=(',', ': '))) + +if __name__ == '__main__': + main() diff --git a/make/core/dex_preopt_libart.mk b/make/core/dex_preopt_libart.mk new file mode 100644 index 0000000..a2c9942 --- /dev/null +++ b/make/core/dex_preopt_libart.mk @@ -0,0 +1,109 @@ +#################################### +# ART boot image installation +# Input variables: +# my_boot_image_name: the boot image to install +# my_boot_image_arch: the architecture to install (e.g. TARGET_ARCH, not expanded) +# my_boot_image_out: the install directory (e.g. $(PRODUCT_OUT)) +# my_boot_image_syms: the symbols director (e.g. $(TARGET_OUT_UNSTRIPPED)) +# +# Output variables: +# my_boot_image_module: the created module name. Empty if no module is created. +# +# Install the boot images compiled by Soong. +# Create a module named dexpreopt_bootjar.$(my_boot_image_name)_$($(my_boot_image_arch)) +# that installs all of boot image files. +# If there is no file to install for $(my_boot_image_name), for example when +# building an unbundled build, then no module is created. +# +#################################### + +# Takes a list of src:dest install pairs and returns a new list with a path +# prefixed to each dest value. +# $(1): list of src:dest install pairs +# $(2): path to prefix to each dest value +define prefix-copy-many-files-dest +$(foreach v,$(1),$(call word-colon,1,$(v)):$(2)$(call word-colon,2,$(v))) +endef + +# Converts an architecture-specific vdex path into a location that can be shared +# between architectures. +define vdex-shared-install-path +$(dir $(patsubst %/,%,$(dir $(1))))$(notdir $(1)) +endef + +# Takes a list of src:dest install pairs of vdex files and returns a new list +# where each dest has been rewritten to the shared location for vdex files. +define vdex-copy-many-files-shared-dest +$(foreach v,$(1),$(call word-colon,1,$(v)):$(call vdex-shared-install-path,$(call word-colon,2,$(v)))) +endef + +# Creates a rule to symlink an architecture specific vdex file to the shared +# location for that vdex file. +define symlink-vdex-file +$(strip \ + $(call symlink-file,\ + $(call vdex-shared-install-path,$(1)),\ + ../$(notdir $(1)),\ + $(1))\ + $(1)) +endef + +# Takes a list of src:dest install pairs of vdex files and creates rules to +# symlink each dest to the shared location for that vdex file. +define symlink-vdex-files +$(foreach v,$(1),$(call symlink-vdex-file,$(call word-colon,2,$(v)))) +endef + +my_boot_image_module := + +my_suffix := $(my_boot_image_name)_$($(my_boot_image_arch)) +my_copy_pairs := $(call prefix-copy-many-files-dest,$(DEXPREOPT_IMAGE_BUILT_INSTALLED_$(my_suffix)),$(my_boot_image_out)) +my_vdex_copy_pairs := $(call prefix-copy-many-files-dest,$(DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_$(my_suffix)),$(my_boot_image_out)) +my_vdex_copy_shared_pairs := $(call vdex-copy-many-files-shared-dest,$(my_vdex_copy_pairs)) +ifeq (,$(filter %_2ND_ARCH,$(my_boot_image_arch))) + # Only install the vdex to the shared location for the primary architecture. + my_copy_pairs += $(my_vdex_copy_shared_pairs) +endif + +my_unstripped_copy_pairs := $(call prefix-copy-many-files-dest,$(DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_$(my_suffix)),$(my_boot_image_syms)) + +# Generate the boot image module only if there is any file to install. +ifneq (,$(strip $(my_copy_pairs))) + my_first_pair := $(firstword $(my_copy_pairs)) + my_rest_pairs := $(wordlist 2,$(words $(my_copy_pairs)),$(my_copy_pairs)) + + my_first_src := $(call word-colon,1,$(my_first_pair)) + my_first_dest := $(call word-colon,2,$(my_first_pair)) + + my_installed := $(call copy-many-files,$(my_copy_pairs)) + my_unstripped_installed := $(call copy-many-files,$(my_unstripped_copy_pairs)) + + my_symlinks := $(call symlink-vdex-files,$(my_vdex_copy_pairs)) + + # We don't have a LOCAL_PATH for the auto-generated modules, so let it be the $(BUILD_SYSTEM). + LOCAL_PATH := $(BUILD_SYSTEM) + # Hack to let these pseudo-modules wrapped around Soong modules use LOCAL_SOONG_INSTALLED_MODULE. + LOCAL_MODULE_MAKEFILE := $(SOONG_ANDROID_MK) + + include $(CLEAR_VARS) + LOCAL_MODULE := dexpreopt_bootjar.$(my_suffix) + LOCAL_PREBUILT_MODULE_FILE := $(my_first_src) + LOCAL_MODULE_PATH := $(dir $(my_first_dest)) + LOCAL_MODULE_STEM := $(notdir $(my_first_dest)) + LOCAL_SOONG_INSTALL_PAIRS := $(my_copy_pairs) + LOCAL_SOONG_INSTALL_SYMLINKS := $(my_symlinks) + LOCAL_SOONG_INSTALLED_MODULE := $(my_first_dest) + LOCAL_SOONG_LICENSE_METADATA := $(DEXPREOPT_IMAGE_LICENSE_METADATA_$(my_suffix)) + ifneq (,$(strip $(filter HOST_%,$(my_boot_image_arch)))) + LOCAL_IS_HOST_MODULE := true + endif + LOCAL_MODULE_CLASS := ETC + include $(BUILD_PREBUILT) + $(LOCAL_BUILT_MODULE): | $(my_unstripped_installed) + # Installing boot.art causes all boot image bits to be installed. + # Keep this old behavior in case anyone still needs it. + $(LOCAL_INSTALLED_MODULE): $(wordlist 2,$(words $(my_installed)),$(my_installed)) $(my_symlinks) + $(my_all_targets): $(my_installed) $(my_symlinks) + + my_boot_image_module := $(LOCAL_MODULE) +endif # my_copy_pairs != empty diff --git a/make/core/dex_preopt_odex_install.mk b/make/core/dex_preopt_odex_install.mk new file mode 100644 index 0000000..ea50313 --- /dev/null +++ b/make/core/dex_preopt_odex_install.mk @@ -0,0 +1,507 @@ +# dexpreopt_odex_install.mk is used to define odex creation rules for JARs and APKs +# This file depends on variables set in base_rules.mk +# Input variables: my_manifest_or_apk +# Output variables: LOCAL_DEX_PREOPT, LOCAL_UNCOMPRESS_DEX + +ifeq (true,$(LOCAL_USE_EMBEDDED_DEX)) + LOCAL_UNCOMPRESS_DEX := true +else + LOCAL_UNCOMPRESS_DEX := +endif + +# We explicitly uncompress APKs of privileged apps, and used by +# privileged apps +ifneq (true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS)) + ifeq (true,$(LOCAL_PRIVILEGED_MODULE)) + LOCAL_UNCOMPRESS_DEX := true + endif + + ifneq (,$(filter $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES), $(LOCAL_MODULE))) + LOCAL_UNCOMPRESS_DEX := true + endif +endif # DONT_UNCOMPRESS_PRIV_APPS_DEXS + +# Setting LOCAL_DEX_PREOPT based on WITH_DEXPREOPT, LOCAL_DEX_PREOPT, etc +LOCAL_DEX_PREOPT := $(strip $(LOCAL_DEX_PREOPT)) +ifndef LOCAL_DEX_PREOPT # LOCAL_DEX_PREOPT undefined + LOCAL_DEX_PREOPT := $(DEX_PREOPT_DEFAULT) +endif + +ifeq (false,$(LOCAL_DEX_PREOPT)) + LOCAL_DEX_PREOPT := +endif + +# Disable preopt for tests. +ifneq (,$(filter $(LOCAL_MODULE_TAGS),tests)) + LOCAL_DEX_PREOPT := +endif + +# If we have product-specific config for this module? +ifneq (,$(filter $(LOCAL_MODULE),$(DEXPREOPT_DISABLED_MODULES))) + LOCAL_DEX_PREOPT := +endif + +# Disable preopt for DISABLE_PREOPT +ifeq (true,$(DISABLE_PREOPT)) + LOCAL_DEX_PREOPT := +endif + +# Disable preopt if not WITH_DEXPREOPT +ifneq (true,$(WITH_DEXPREOPT)) + LOCAL_DEX_PREOPT := +endif + +ifdef LOCAL_UNINSTALLABLE_MODULE + LOCAL_DEX_PREOPT := +endif + +# Disable preopt if the app contains no java code. +ifeq (,$(strip $(built_dex)$(my_prebuilt_src_file)$(LOCAL_SOONG_DEX_JAR))) + LOCAL_DEX_PREOPT := +endif + +# if WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true and module is not in boot class path skip +# Also preopt system server jars since selinux prevents system server from loading anything from +# /data. If we don't do this they will need to be extracted which is not favorable for RAM usage +# or performance. If my_preopt_for_extracted_apk is true, we ignore the only preopt boot image +# options. +system_server_jars := $(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),$(call word-colon,2,$(m))) +ifneq (true,$(my_preopt_for_extracted_apk)) + ifeq (true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY)) + ifeq ($(filter $(system_server_jars) $(DEXPREOPT_BOOT_JARS_MODULES),$(LOCAL_MODULE)),) + LOCAL_DEX_PREOPT := + endif + endif +endif + +my_process_profile := +my_profile_is_text_listing := + +ifeq (false,$(WITH_DEX_PREOPT_GENERATE_PROFILE)) + LOCAL_DEX_PREOPT_GENERATE_PROFILE := false +endif + +ifndef LOCAL_DEX_PREOPT_GENERATE_PROFILE + # If LOCAL_DEX_PREOPT_GENERATE_PROFILE is not defined, default it based on the existence of the + # profile class listing. TODO: Use product specific directory here. + my_classes_directory := $(PRODUCT_DEX_PREOPT_PROFILE_DIR) + LOCAL_DEX_PREOPT_PROFILE := $(my_classes_directory)/$(LOCAL_MODULE).prof + + ifneq (,$(wildcard $(LOCAL_DEX_PREOPT_PROFILE))) + my_process_profile := true + my_profile_is_text_listing := + endif +else + my_process_profile := $(LOCAL_DEX_PREOPT_GENERATE_PROFILE) + my_profile_is_text_listing := true + LOCAL_DEX_PREOPT_PROFILE := $(LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING) +endif + +ifeq (true,$(my_process_profile)) + ifndef LOCAL_DEX_PREOPT_PROFILE + $(call pretty-error,Must have specified class listing (LOCAL_DEX_PREOPT_PROFILE)) + endif + ifeq (,$(dex_preopt_profile_src_file)) + $(call pretty-error, Internal error: dex_preopt_profile_src_file must be set) + endif +endif + +################################################################################ +# Local module variables and functions used in dexpreopt and manifest_check. +################################################################################ + +my_filtered_optional_uses_libraries := $(filter-out $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES), \ + $(LOCAL_OPTIONAL_USES_LIBRARIES)) + +# TODO(b/132357300): This may filter out too much, as PRODUCT_PACKAGES doesn't +# include all packages (the full list is unknown until reading all Android.mk +# makefiles). As a consequence, a library may be present but not included in +# dexpreopt, which will result in class loader context mismatch and a failure +# to load dexpreopt code on device. We should fix this, either by deferring +# dependency computation until the full list of product packages is known, or +# by adding product-specific lists of missing libraries. +my_filtered_optional_uses_libraries := $(filter $(PRODUCT_PACKAGES), \ + $(my_filtered_optional_uses_libraries)) + +ifeq ($(LOCAL_MODULE_CLASS),APPS) + # compatibility libraries are added to class loader context of an app only if + # targetSdkVersion in the app's manifest is lower than the given SDK version + + my_dexpreopt_libs_compat_28 := \ + org.apache.http.legacy + + my_dexpreopt_libs_compat_29 := \ + android.hidl.manager-V1.0-java \ + android.hidl.base-V1.0-java + + my_dexpreopt_libs_compat_30 := \ + android.test.base \ + android.test.mock + + my_dexpreopt_libs_compat := \ + $(my_dexpreopt_libs_compat_28) \ + $(my_dexpreopt_libs_compat_29) \ + $(my_dexpreopt_libs_compat_30) +else + my_dexpreopt_libs_compat := +endif + +my_dexpreopt_libs := \ + $(LOCAL_USES_LIBRARIES) \ + $(my_filtered_optional_uses_libraries) + +# Module dexpreopt.config depends on dexpreopt.config files of each +# dependency, because these libraries may be processed after +# the current module by Make (there's no topological order), so the dependency +# information (paths, class loader context) may not be ready yet by the time +# this dexpreopt.config is generated. So it's necessary to add file-level +# dependencies between dexpreopt.config files. +my_dexpreopt_dep_configs := $(foreach lib, \ + $(filter-out $(my_dexpreopt_libs_compat),$(LOCAL_USES_LIBRARIES) $(my_filtered_optional_uses_libraries)), \ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,)/dexpreopt.config) + +# 1: SDK version +# 2: list of libraries +# +# Make does not process modules in topological order wrt. +# dependencies, therefore we cannot rely on variables to get the information +# about dependencies (in particular, their on-device path and class loader +# context). This information is communicated via dexpreopt.config files: each +# config depends on configs for dependencies of this module, +# and the dex_preopt_config_merger.py script reads all configs and inserts the +# missing bits from dependency configs into the module config. +# +# By default on-device path is /system/framework/*.jar, and class loader +# subcontext is empty. These values are correct for compatibility libraries, +# which are special and not handled by dex_preopt_config_merger.py. +# +add_json_class_loader_context = \ + $(call add_json_array, $(1)) \ + $(foreach lib, $(2),\ + $(call add_json_map_anon) \ + $(call add_json_str, Name, $(lib)) \ + $(call add_json_str, Host, $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) \ + $(call add_json_str, Device, /system/framework/$(lib).jar) \ + $(call add_json_val, Subcontexts, null) \ + $(call end_json_map)) \ + $(call end_json_array) + +################################################################################ +# Verify coherence between the build system and the manifest. +################################################################################ + +# Some libraries do not have a manifest, so there is nothing to check against. +# Handle it as if the manifest had zero tags: it is ok unless the +# module has non-empty LOCAL_USES_LIBRARIES or LOCAL_OPTIONAL_USES_LIBRARIES. +ifndef my_manifest_or_apk + ifneq (,$(strip $(LOCAL_USES_LIBRARIES)$(LOCAL_OPTIONAL_USES_LIBRARIES))) + $(error $(LOCAL_MODULE) has non-empty list but no manifest) + else + LOCAL_ENFORCE_USES_LIBRARIES := false + endif +endif + +# Disable the check for tests. +ifneq (,$(filter $(LOCAL_MODULE_TAGS),tests)) + LOCAL_ENFORCE_USES_LIBRARIES := false +endif +ifneq (,$(LOCAL_COMPATIBILITY_SUITE)) + LOCAL_ENFORCE_USES_LIBRARIES := false +endif + +# Disable the check if the app contains no java code. +ifeq (,$(strip $(built_dex)$(my_prebuilt_src_file)$(LOCAL_SOONG_DEX_JAR))) + LOCAL_ENFORCE_USES_LIBRARIES := false +endif + +# Disable checks if dexpreopt is globally disabled. +# Without dexpreopt the check is not necessary, and although it is good to have, +# it is difficult to maintain on non-linux build platforms where dexpreopt is +# generally disabled (the check may fail due to various unrelated reasons, such +# as a failure to get manifest from an APK). +ifneq (true,$(WITH_DEXPREOPT)) + LOCAL_ENFORCE_USES_LIBRARIES := false +else ifeq (true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY)) + LOCAL_ENFORCE_USES_LIBRARIES := false +endif + +# Verify LOCAL_USES_LIBRARIES/LOCAL_OPTIONAL_USES_LIBRARIES against the manifest. +ifndef LOCAL_ENFORCE_USES_LIBRARIES + LOCAL_ENFORCE_USES_LIBRARIES := true +endif + +my_enforced_uses_libraries := +ifeq (true,$(LOCAL_ENFORCE_USES_LIBRARIES)) + my_verify_script := build/soong/scripts/manifest_check.py + my_uses_libs_args := $(patsubst %,--uses-library %,$(LOCAL_USES_LIBRARIES)) + my_optional_uses_libs_args := $(patsubst %,--optional-uses-library %, \ + $(LOCAL_OPTIONAL_USES_LIBRARIES)) + my_relax_check_arg := $(if $(filter true,$(RELAX_USES_LIBRARY_CHECK)), \ + --enforce-uses-libraries-relax,) + my_dexpreopt_config_args := $(patsubst %,--dexpreopt-config %,$(my_dexpreopt_dep_configs)) + + my_enforced_uses_libraries := $(intermediates.COMMON)/enforce_uses_libraries.status + $(my_enforced_uses_libraries): PRIVATE_USES_LIBRARIES := $(my_uses_libs_args) + $(my_enforced_uses_libraries): PRIVATE_OPTIONAL_USES_LIBRARIES := $(my_optional_uses_libs_args) + $(my_enforced_uses_libraries): PRIVATE_DEXPREOPT_CONFIGS := $(my_dexpreopt_config_args) + $(my_enforced_uses_libraries): PRIVATE_RELAX_CHECK := $(my_relax_check_arg) + $(my_enforced_uses_libraries): $(AAPT) + $(my_enforced_uses_libraries): $(my_verify_script) + $(my_enforced_uses_libraries): $(my_dexpreopt_dep_configs) + $(my_enforced_uses_libraries): $(my_manifest_or_apk) + @echo Verifying uses-libraries: $< + rm -f $@ + $(my_verify_script) \ + --enforce-uses-libraries \ + --enforce-uses-libraries-status $@ \ + --aapt $(AAPT) \ + $(PRIVATE_USES_LIBRARIES) \ + $(PRIVATE_OPTIONAL_USES_LIBRARIES) \ + $(PRIVATE_DEXPREOPT_CONFIGS) \ + $(PRIVATE_RELAX_CHECK) \ + $< + $(LOCAL_BUILT_MODULE) : $(my_enforced_uses_libraries) +endif + +################################################################################ +# Dexpreopt command. +################################################################################ + +my_dexpreopt_archs := +my_dexpreopt_images := +my_dexpreopt_images_deps := +my_dexpreopt_image_locations_on_host := +my_dexpreopt_image_locations_on_device := +my_dexpreopt_infix := boot +my_create_dexpreopt_config := +ifeq (true, $(DEXPREOPT_USE_ART_IMAGE)) + my_dexpreopt_infix := art +endif + +ifdef LOCAL_DEX_PREOPT + ifeq (,$(filter PRESIGNED,$(LOCAL_CERTIFICATE))) + # Store uncompressed dex files preopted in /system + ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true) + ifeq ($(call install-on-system-other, $(my_module_path)),) + LOCAL_UNCOMPRESS_DEX := true + endif # install-on-system-other + else # BOARD_USES_SYSTEM_OTHER_ODEX + LOCAL_UNCOMPRESS_DEX := true + endif + endif + my_create_dexpreopt_config := true +endif + +# dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true, +# but dexpreopt config files are required to dexpreopt in post-processing. +ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true) + my_create_dexpreopt_config := true +endif + +ifeq ($(my_create_dexpreopt_config), true) + ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES) + my_module_multilib := $(LOCAL_MULTILIB) + # If the module is not an SDK library and it's a system server jar, only preopt the primary arch. + ifeq (,$(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_MODULE))) + # For a Java library, by default we build odex for both 1st arch and 2nd arch. + # But it can be overridden with "LOCAL_MULTILIB := first". + ifneq (,$(filter $(PRODUCT_SYSTEM_SERVER_JARS),$(LOCAL_MODULE))) + # For system server jars, we build for only "first". + my_module_multilib := first + endif + endif + + # Only preopt primary arch for translated arch since there is only an image there. + ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true) + my_module_multilib := first + endif + + # ################################################# + # Odex for the 1st arch + my_dexpreopt_archs += $(TARGET_ARCH) + my_dexpreopt_images += $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_ARCH)) + my_dexpreopt_images_deps += $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_ARCH)) + # Odex for the 2nd arch + ifdef TARGET_2ND_ARCH + ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) + ifneq (first,$(my_module_multilib)) + my_dexpreopt_archs += $(TARGET_2ND_ARCH) + my_dexpreopt_images += $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_2ND_ARCH)) + my_dexpreopt_images_deps += $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_2ND_ARCH)) + endif # my_module_multilib is not first. + endif # TARGET_TRANSLATE_2ND_ARCH not true + endif # TARGET_2ND_ARCH + # ################################################# + else # must be APPS + # The preferred arch + # Save the module multilib since setup_one_odex modifies it. + my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) + my_dexpreopt_archs += $(TARGET_$(my_2nd_arch_prefix)ARCH) + my_dexpreopt_images += \ + $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) + my_dexpreopt_images_deps += \ + $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) + ifdef TARGET_2ND_ARCH + ifeq ($(my_module_multilib),both) + # The non-preferred arch + my_2nd_arch_prefix := $(if $(LOCAL_2ND_ARCH_VAR_PREFIX),,$(TARGET_2ND_ARCH_VAR_PREFIX)) + my_dexpreopt_archs += $(TARGET_$(my_2nd_arch_prefix)ARCH) + my_dexpreopt_images += \ + $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) + my_dexpreopt_images_deps += \ + $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) + endif # LOCAL_MULTILIB is both + endif # TARGET_2ND_ARCH + endif # LOCAL_MODULE_CLASS + + my_dexpreopt_image_locations_on_host += $(DEXPREOPT_IMAGE_LOCATIONS_ON_HOST$(my_dexpreopt_infix)) + my_dexpreopt_image_locations_on_device += $(DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE$(my_dexpreopt_infix)) + + # Record dex-preopt config. + DEXPREOPT.$(LOCAL_MODULE).DEX_PREOPT := $(LOCAL_DEX_PREOPT) + DEXPREOPT.$(LOCAL_MODULE).MULTILIB := $(LOCAL_MULTILIB) + DEXPREOPT.$(LOCAL_MODULE).DEX_PREOPT_FLAGS := $(LOCAL_DEX_PREOPT_FLAGS) + DEXPREOPT.$(LOCAL_MODULE).PRIVILEGED_MODULE := $(LOCAL_PRIVILEGED_MODULE) + DEXPREOPT.$(LOCAL_MODULE).VENDOR_MODULE := $(LOCAL_VENDOR_MODULE) + DEXPREOPT.$(LOCAL_MODULE).TARGET_ARCH := $(LOCAL_MODULE_TARGET_ARCH) + DEXPREOPT.$(LOCAL_MODULE).INSTALLED_STRIPPED := $(LOCAL_INSTALLED_MODULE) + DEXPREOPT.MODULES.$(LOCAL_MODULE_CLASS) := $(sort \ + $(DEXPREOPT.MODULES.$(LOCAL_MODULE_CLASS)) $(LOCAL_MODULE)) + + $(call json_start) + + # DexPath is not set: it will be filled in by dexpreopt_gen. + + $(call add_json_str, Name, $(LOCAL_MODULE)) + $(call add_json_str, DexLocation, $(patsubst $(PRODUCT_OUT)%,%,$(LOCAL_INSTALLED_MODULE))) + $(call add_json_str, BuildPath, $(LOCAL_BUILT_MODULE)) + $(call add_json_str, ManifestPath, $(full_android_manifest)) + $(call add_json_str, ExtrasOutputPath, $$2) + $(call add_json_bool, Privileged, $(filter true,$(LOCAL_PRIVILEGED_MODULE))) + $(call add_json_bool, UncompressedDex, $(filter true,$(LOCAL_UNCOMPRESS_DEX))) + $(call add_json_bool, HasApkLibraries, $(LOCAL_APK_LIBRARIES)) + $(call add_json_list, PreoptFlags, $(LOCAL_DEX_PREOPT_FLAGS)) + $(call add_json_str, ProfileClassListing, $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))) + $(call add_json_bool, ProfileIsTextListing, $(my_profile_is_text_listing)) + $(call add_json_str, EnforceUsesLibrariesStatusFile, $(my_enforced_uses_libraries)) + $(call add_json_bool, EnforceUsesLibraries, $(filter true,$(LOCAL_ENFORCE_USES_LIBRARIES))) + $(call add_json_str, ProvidesUsesLibrary, $(firstword $(LOCAL_PROVIDES_USES_LIBRARY) $(LOCAL_MODULE))) + $(call add_json_map, ClassLoaderContexts) + $(call add_json_class_loader_context, any, $(my_dexpreopt_libs)) + $(call add_json_class_loader_context, 28, $(my_dexpreopt_libs_compat_28)) + $(call add_json_class_loader_context, 29, $(my_dexpreopt_libs_compat_29)) + $(call add_json_class_loader_context, 30, $(my_dexpreopt_libs_compat_30)) + $(call end_json_map) + $(call add_json_list, Archs, $(my_dexpreopt_archs)) + $(call add_json_list, DexPreoptImages, $(my_dexpreopt_images)) + $(call add_json_list, DexPreoptImageLocationsOnHost, $(my_dexpreopt_image_locations_on_host)) + $(call add_json_list, DexPreoptImageLocationsOnDevice,$(my_dexpreopt_image_locations_on_device)) + $(call add_json_list, PreoptBootClassPathDexFiles, $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)) + $(call add_json_list, PreoptBootClassPathDexLocations,$(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)) + $(call add_json_bool, PreoptExtractedApk, $(my_preopt_for_extracted_apk)) + $(call add_json_bool, NoCreateAppImage, $(filter false,$(LOCAL_DEX_PREOPT_APP_IMAGE))) + $(call add_json_bool, ForceCreateAppImage, $(filter true,$(LOCAL_DEX_PREOPT_APP_IMAGE))) + $(call add_json_bool, PresignedPrebuilt, $(filter PRESIGNED,$(LOCAL_CERTIFICATE))) + + $(call json_end) + + my_dexpreopt_config := $(intermediates)/dexpreopt.config + my_dexpreopt_config_for_postprocessing := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config + my_dexpreopt_config_merger := $(BUILD_SYSTEM)/dex_preopt_config_merger.py + + $(my_dexpreopt_config): $(my_dexpreopt_dep_configs) $(my_dexpreopt_config_merger) + $(my_dexpreopt_config): PRIVATE_MODULE := $(LOCAL_MODULE) + $(my_dexpreopt_config): PRIVATE_CONTENTS := $(json_contents) + $(my_dexpreopt_config): PRIVATE_DEP_CONFIGS := $(my_dexpreopt_dep_configs) + $(my_dexpreopt_config): PRIVATE_CONFIG_MERGER := $(my_dexpreopt_config_merger) + $(my_dexpreopt_config): + @echo "$(PRIVATE_MODULE) dexpreopt.config" + echo -e -n '$(subst $(newline),\n,$(subst ','\'',$(subst \,\\,$(PRIVATE_CONTENTS))))' > $@ + $(PRIVATE_CONFIG_MERGER) $@ $(PRIVATE_DEP_CONFIGS) + +$(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing))) + +$(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing) + +# System server jars defined in Android.mk are deprecated. +ifneq (true, $(PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS)) + ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS) $(PRODUCT_APEX_SYSTEM_SERVER_JARS))) + $(error System server jars defined in Android.mk are deprecated. \ + Convert $(LOCAL_MODULE) to Android.bp or temporarily disable the error \ + with 'PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS := true') + endif +endif + +ifdef LOCAL_DEX_PREOPT + # System server jars must be copied into predefined locations expected by + # dexpreopt. Copy rule must be exposed to Ninja (as it uses these files as + # inputs), so it cannot go in dexpreopt.sh. + ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS))) + my_dexpreopt_jar_copy := $(OUT_DIR)/soong/system_server_dexjars/$(LOCAL_MODULE).jar + $(my_dexpreopt_jar_copy): PRIVATE_BUILT_MODULE := $(LOCAL_BUILT_MODULE) + $(my_dexpreopt_jar_copy): $(LOCAL_BUILT_MODULE) + @cp $(PRIVATE_BUILT_MODULE) $@ + endif + + my_dexpreopt_script := $(intermediates)/dexpreopt.sh + my_dexpreopt_zip := $(intermediates)/dexpreopt.zip + .KATI_RESTAT: $(my_dexpreopt_script) + $(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE) + $(my_dexpreopt_script): PRIVATE_GLOBAL_SOONG_CONFIG := $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) + $(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(DEX_PREOPT_CONFIG_FOR_MAKE) + $(my_dexpreopt_script): PRIVATE_MODULE_CONFIG := $(my_dexpreopt_config) + $(my_dexpreopt_script): $(DEXPREOPT_GEN) + $(my_dexpreopt_script): $(my_dexpreopt_jar_copy) + $(my_dexpreopt_script): $(my_dexpreopt_config) $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(DEX_PREOPT_CONFIG_FOR_MAKE) + @echo "$(PRIVATE_MODULE) dexpreopt gen" + $(DEXPREOPT_GEN) \ + -global_soong $(PRIVATE_GLOBAL_SOONG_CONFIG) \ + -global $(PRIVATE_GLOBAL_CONFIG) \ + -module $(PRIVATE_MODULE_CONFIG) \ + -dexpreopt_script $@ \ + -out_dir $(OUT_DIR) + + my_dexpreopt_deps := $(my_dex_jar) + my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE)) + my_dexpreopt_deps += \ + $(foreach lib, $(my_dexpreopt_libs) $(my_dexpreopt_libs_compat), \ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) + my_dexpreopt_deps += $(my_dexpreopt_images_deps) + my_dexpreopt_deps += $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES) + ifeq ($(LOCAL_ENFORCE_USES_LIBRARIES),true) + my_dexpreopt_deps += $(intermediates.COMMON)/enforce_uses_libraries.status + endif + + $(my_dexpreopt_zip): PRIVATE_MODULE := $(LOCAL_MODULE) + $(my_dexpreopt_zip): $(my_dexpreopt_deps) + $(my_dexpreopt_zip): | $(DEXPREOPT_GEN_DEPS) + $(my_dexpreopt_zip): .KATI_DEPFILE := $(my_dexpreopt_zip).d + $(my_dexpreopt_zip): PRIVATE_DEX := $(my_dex_jar) + $(my_dexpreopt_zip): PRIVATE_SCRIPT := $(my_dexpreopt_script) + $(my_dexpreopt_zip): $(my_dexpreopt_script) + @echo "$(PRIVATE_MODULE) dexpreopt" + bash $(PRIVATE_SCRIPT) $(PRIVATE_DEX) $@ + + ifdef LOCAL_POST_INSTALL_CMD + # Add a shell command separator + LOCAL_POST_INSTALL_CMD += && + endif + + LOCAL_POST_INSTALL_CMD += \ + for i in $$(zipinfo -1 $(my_dexpreopt_zip)); \ + do mkdir -p $(PRODUCT_OUT)/$$(dirname $$i); \ + done && \ + ( unzip -qoDD -d $(PRODUCT_OUT) $(my_dexpreopt_zip) 2>&1 | grep -v "zipfile is empty"; exit $${PIPESTATUS[0]} ) || \ + ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi ) + + $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD) + $(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_zip) + + $(my_all_targets): $(my_dexpreopt_zip) + + my_dexpreopt_config := + my_dexpreopt_script := + my_dexpreopt_zip := + my_dexpreopt_config_for_postprocessing := +endif # LOCAL_DEX_PREOPT +endif # my_create_dexpreopt_config \ No newline at end of file diff --git a/make/core/distdir.mk b/make/core/distdir.mk new file mode 100644 index 0000000..aad8ff3 --- /dev/null +++ b/make/core/distdir.mk @@ -0,0 +1,69 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# When specifying "dist", the user has asked that we copy the important +# files from this build into DIST_DIR. + +# list of all goals that depend on any dist files +_all_dist_goals := +# pairs of goal:distfile +_all_dist_goal_output_pairs := +# pairs of srcfile:distfile +_all_dist_src_dst_pairs := + +# Other parts of the system should use this function to associate +# certain files with certain goals. When those goals are built +# and "dist" is specified, the marked files will be copied to DIST_DIR. +# +# $(1): a list of goals (e.g. droid, sdk, ndk). These must be PHONY +# $(2): the dist files to add to those goals. If the file contains ':', +# the text following the colon is the name that the file is copied +# to under the dist directory. Subdirs are ok, and will be created +# at copy time if necessary. +define dist-for-goals +$(if $(strip $(2)), \ + $(eval _all_dist_goals += $$(1))) \ +$(foreach file,$(2), \ + $(eval src := $(call word-colon,1,$(file))) \ + $(eval dst := $(call word-colon,2,$(file))) \ + $(if $(dst),,$(eval dst := $$(notdir $$(src)))) \ + $(eval _all_dist_src_dst_pairs += $$(src):$$(dst)) \ + $(foreach goal,$(1), \ + $(eval _all_dist_goal_output_pairs += $$(goal):$$(dst)))) +endef + +#------------------------------------------------------------------ +# To be used at the end of the build to collect all the uses of +# dist-for-goals, and write them into a file for the packaging step to use. + +# $(1): The file to write +define dist-write-file +$(strip \ + $(KATI_obsolete_var dist-for-goals,Cannot be used after dist-write-file) \ + $(foreach goal,$(sort $(_all_dist_goals)), \ + $(eval $$(goal): _dist_$$(goal))) \ + $(shell mkdir -p $(dir $(1))) \ + $(file >$(1).tmp, \ + DIST_GOAL_OUTPUT_PAIRS := $(sort $(_all_dist_goal_output_pairs)) \ + $(newline)DIST_SRC_DST_PAIRS := $(sort $(_all_dist_src_dst_pairs))) \ + $(shell if ! cmp -s $(1).tmp $(1); then \ + mv $(1).tmp $(1); \ + else \ + rm $(1).tmp; \ + fi)) +endef + +.KATI_READONLY := dist-for-goals dist-write-file diff --git a/make/core/dumpconfig.mk b/make/core/dumpconfig.mk new file mode 100644 index 0000000..640fe10 --- /dev/null +++ b/make/core/dumpconfig.mk @@ -0,0 +1,143 @@ +# Read and dump the product configuration. + +# Called from the product-config tool, not from the main build system. + +# +# Ensure we are being called correctly +# +ifndef KATI + $(warning Kati must be used to call dumpconfig.mk, not make.) + $(error stopping) +endif + +ifdef DEFAULT_GOAL + $(warning Calling dumpconfig.mk from inside the make build system is not) + $(warning supported. It is only meant to be called via kati by product-confing.) + $(error stopping) +endif + +ifndef TARGET_PRODUCT + $(warning dumpconfig.mk requires TARGET_PRODUCT to be set) + $(error stopping) +endif + +ifndef TARGET_BUILD_VARIANT + $(warning dumpconfig.mk requires TARGET_BUILD_VARIANT to be set) + $(error stopping) +endif + +ifneq (build/make/core/config.mk,$(wildcard build/make/core/config.mk)) + $(warning dumpconfig must be called from the root of the source tree) + $(error stopping) +endif + +ifeq (,$(DUMPCONFIG_FILE)) + $(warning dumpconfig requires DUMPCONFIG_FILE to be set) + $(error stopping) +endif + +# Skip the second inclusion of all of the product config files, because +# we will do these checks in the product_config tool. +SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK := true + +# Before we do anything else output the format version. +$(file > $(DUMPCONFIG_FILE),dumpconfig_version,1) +$(file >> $(DUMPCONFIG_FILE),dumpconfig_file,$(DUMPCONFIG_FILE)) + +# Default goal for dumpconfig +dumpconfig: + $(file >> $(DUMPCONFIG_FILE),***DONE***) + @echo ***DONE*** + +# TODO(Remove): These need to be set externally +OUT_DIR := out +TMPDIR = /tmp/build-temp +BUILD_DATETIME_FILE := $(OUT_DIR)/build_date.txt + +# Escape quotation marks for CSV, and wraps in quotation marks. +define escape-for-csv +"$(subst ","",$1)" +endef + +# Args: +# $(1): include stack +define dump-import-start +$(eval $(file >> $(DUMPCONFIG_FILE),import,$(strip $(1)))) +endef + +# Args: +# $(1): include stack +define dump-import-done +$(eval $(file >> $(DUMPCONFIG_FILE),imported,$(strip $(1)))) +endef + +# Args: +# $(1): Current file +# $(2): Inherited file +define dump-inherit +$(eval $(file >> $(DUMPCONFIG_FILE),inherit,$(strip $(1)),$(strip $(2)))) +endef + +# Args: +# $(1): Config phase (PRODUCT, EXPAND, or DEVICE) +# $(2): Root nodes to import +# $(3): All variable names +# $(4): Single-value variables +# $(5): Makefile being processed +define dump-phase-start +$(eval $(file >> $(DUMPCONFIG_FILE),phase,$(strip $(1)),$(strip $(2)))) \ +$(foreach var,$(3), \ + $(eval $(file >> $(DUMPCONFIG_FILE),var,$(if $(filter $(4),$(var)),single,list),$(var))) \ +) \ +$(call dump-config-vals,$(strip $(5)),initial) +endef + +# Args: +# $(1): Makefile being processed +define dump-phase-end +$(call dump-config-vals,$(strip $(1)),final) +endef + +define dump-debug +$(eval $(file >> $(DUMPCONFIG_FILE),debug,$(1))) +endef + +# Skip these when dumping. They're not used and they cause a lot of noise in the dump. +DUMPCONFIG_SKIP_VARS := \ + .VARIABLES \ + .KATI_SYMBOLS \ + 1 \ + 2 \ + 3 \ + 4 \ + 5 \ + 6 \ + 7 \ + 8 \ + 9 \ + LOCAL_PATH \ + MAKEFILE_LIST \ + current_mk \ + _eiv_ev \ + _eiv_i \ + _eiv_sv \ + _eiv_tv \ + inherit_var \ + np \ + _node_import_context \ + _included \ + _include_stack \ + _in \ + _nic.% + +# Args: +# $(1): Makefile that was included +# $(2): block (before,import,after,initial,final) +define dump-config-vals +$(foreach var,$(filter-out $(DUMPCONFIG_SKIP_VARS),$(.KATI_SYMBOLS)),\ + $(eval $(file >> $(DUMPCONFIG_FILE),val,$(call escape-for-csv,$(1)),$(2),$(call escape-for-csv,$(var)),$(call escape-for-csv,$($(var))),$(call escape-for-csv,$(KATI_variable_location $(var))))) \ +) +endef + +include build/make/core/config.mk + diff --git a/make/core/dumpvar.mk b/make/core/dumpvar.mk new file mode 100644 index 0000000..6f3d14f --- /dev/null +++ b/make/core/dumpvar.mk @@ -0,0 +1,41 @@ +# --------------------------------------------------------------- +# the setpath shell function in envsetup.sh uses this to figure out +# what to add to the path given the config we have chosen. +ifeq ($(CALLED_FROM_SETUP),true) + +ifneq ($(filter /%,$(SOONG_HOST_OUT_EXECUTABLES)),) +ABP := $(SOONG_HOST_OUT_EXECUTABLES) +else +ABP := $(PWD)/$(SOONG_HOST_OUT_EXECUTABLES) +endif +ifneq ($(filter /%,$(HOST_OUT_EXECUTABLES)),) +ABP := $(ABP):$(HOST_OUT_EXECUTABLES) +else +ABP := $(ABP):$(PWD)/$(HOST_OUT_EXECUTABLES) +endif + +ANDROID_BUILD_PATHS := $(ABP) +ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG) +ANDROID_GCC_PREBUILTS := prebuilts/gcc/$(HOST_PREBUILT_TAG) +ANDROID_CLANG_PREBUILTS := prebuilts/clang/host/$(HOST_PREBUILT_TAG) + +# Dump mulitple variables to "=" pairs, one per line. +# The output may be executed as bash script. +# Input variables: +# DUMP_MANY_VARS: the list of variable names. +# DUMP_VAR_PREFIX: an optional prefix of the variable name added to the output. +# The value is printed in parts because large variables like PRODUCT_PACKAGES +# can exceed the maximum linux command line size +.PHONY: dump-many-vars +dump-many-vars : + @$(foreach v, $(DUMP_MANY_VARS),\ + printf "%s='%s" '$(DUMP_VAR_PREFIX)$(v)' '$(firstword $($(v)))'; \ + $(foreach part, $(wordlist 2, $(words $($(v))), $($(v))),\ + printf " %s" '$(part)'$(newline))\ + printf "'\n";) + +endif # CALLED_FROM_SETUP + +ifneq (,$(RBC_DUMP_CONFIG_FILE)) +$(call dump-variables-rbc,$(RBC_DUMP_CONFIG_FILE)) +endif diff --git a/make/core/dynamic_binary.mk b/make/core/dynamic_binary.mk new file mode 100644 index 0000000..0d2cd7f --- /dev/null +++ b/make/core/dynamic_binary.mk @@ -0,0 +1,152 @@ +########################################################### +## Standard rules for building any target-side binaries +## with dynamic linkage (dynamic libraries or executables +## that link with dynamic libraries) +## +## Files including this file must define a rule to build +## the target $(linked_module). +########################################################### + +# This constraint means that we can hard-code any $(TARGET_*) variables. +ifdef LOCAL_IS_HOST_MODULE +$(error This file should not be used to build host binaries. Included by (or near) $(lastword $(filter-out config/%,$(MAKEFILE_LIST)))) +endif + +# The name of the target file, without any path prepended. +# This duplicates logic from base_rules.mk because we need to +# know its results before base_rules.mk is included. +include $(BUILD_SYSTEM)/configure_module_stem.mk + +intermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX)) + +# Define the target that is the unmodified output of the linker. +# The basename of this target must be the same as the final output +# binary name, because it's used to set the "soname" in the binary. +# The includer of this file will define a rule to build this target. +linked_module := $(intermediates)/LINKED/$(notdir $(my_installed_module_stem)) + +# This tells binary.make to explicitly define the PRIVATE_ variables for +# linked_module as well as for LOCAL_BUILT_MODULE. +LOCAL_INTERMEDIATE_TARGETS := $(linked_module) + +################################### +include $(BUILD_SYSTEM)/use_lld_setup.mk +include $(BUILD_SYSTEM)/binary.mk +################################### + +ifdef LOCAL_INJECT_BSSL_HASH +inject_module := $(intermediates)/INJECT_BSSL_HASH/$(notdir $(my_installed_module_stem)) +LOCAL_INTERMEDIATE_TARGETS += $(inject_module) +$(inject_module): $(SOONG_HOST_OUT)/bin/bssl_inject_hash +$(inject_module): $(linked_module) + @echo "target inject BSSL hash: $(PRIVATE_MODULE) ($@)" + $(SOONG_HOST_OUT)/bin/bssl_inject_hash -in-object $< -o $@ +else +inject_module := $(linked_module) +endif + +########################################################### +## Store a copy with symbols for symbolic debugging +########################################################### +ifeq ($(LOCAL_UNSTRIPPED_PATH),) +my_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) +else +my_unstripped_path := $(LOCAL_UNSTRIPPED_PATH) +endif +symbolic_input := $(inject_module) +symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem) +$(eval $(call copy-unstripped-elf-file-with-mapping,$(symbolic_input),$(symbolic_output))) + +########################################################### +## Store breakpad symbols +########################################################### + +ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) +my_breakpad_path := $(TARGET_OUT_BREAKPAD)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) +breakpad_input := $(inject_module) +breakpad_output := $(my_breakpad_path)/$(my_installed_module_stem).sym +$(breakpad_output) : $(breakpad_input) | $(BREAKPAD_DUMP_SYMS) $(PRIVATE_READELF) + @echo "target breakpad: $(PRIVATE_MODULE) ($@)" + @mkdir -p $(dir $@) + $(hide) if $(PRIVATE_READELF) -S $< > /dev/null 2>&1 ; then \ + $(BREAKPAD_DUMP_SYMS) -c $< > $@ ; \ + else \ + echo "skipped for non-elf file."; \ + touch $@; \ + fi +$(LOCAL_BUILT_MODULE) : $(breakpad_output) +endif + +########################################################### +## Strip +########################################################### +strip_input := $(inject_module) +strip_output := $(LOCAL_BUILT_MODULE) + +# Use an order-only dependency to ensure the unstripped file in the symbols +# directory is copied when the module is built, but does not force the +# module to be rebuilt when the symbols directory is cleaned by installclean. +$(strip_output): | $(symbolic_output) + +my_strip_module := $(firstword \ + $(LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \ + $(LOCAL_STRIP_MODULE)) +ifeq ($(my_strip_module),) + my_strip_module := mini-debug-info +endif + +ifeq ($(my_strip_module),false) + my_strip_module := +endif + +my_strip_args := +ifeq ($(my_strip_module),mini-debug-info) + my_strip_args += --keep-mini-debug-info +else ifeq ($(my_strip_module),keep_symbols) + my_strip_args += --keep-symbols +endif + +ifeq (,$(filter no_debuglink mini-debug-info,$(my_strip_module))) + ifneq ($(TARGET_BUILD_VARIANT),user) + my_strip_args += --add-gnu-debuglink + endif +endif + +ifeq ($($(my_prefix)OS),darwin) + # llvm-strip does not support Darwin Mach-O yet. + my_strip_args += --use-gnu-strip +endif + +valid_strip := mini-debug-info keep_symbols true no_debuglink +ifneq (,$(filter-out $(valid_strip),$(my_strip_module))) + $(call pretty-error,Invalid strip value $(my_strip_module), only one of $(valid_strip) allowed) +endif + +ifneq (,$(my_strip_module)) + $(strip_output): PRIVATE_STRIP_ARGS := $(my_strip_args) + $(strip_output): PRIVATE_TOOLS_PREFIX := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)TOOLS_PREFIX) + $(strip_output): $(strip_input) $(SOONG_STRIP_PATH) $(XZ) + @echo "$($(PRIVATE_PREFIX)DISPLAY) Strip: $(PRIVATE_MODULE) ($@)" + CLANG_BIN=$(LLVM_PREBUILTS_PATH) \ + CROSS_COMPILE=$(PRIVATE_TOOLS_PREFIX) \ + XZ=$(XZ) \ + CREATE_MINIDEBUGINFO=${CREATE_MINIDEBUGINFO} \ + $(SOONG_STRIP_PATH) -i $< -o $@ -d $@.strip.d $(PRIVATE_STRIP_ARGS) + ifneq ($(HOST_OS),darwin) + $(strip_output): $(CREATE_MINIDEBUGINFO) + endif + $(call include-depfile,$(strip_output).strip.d,$(strip_output)) +else + # Don't strip the binary, just copy it. We can't skip this step + # because a copy of the binary must appear at LOCAL_BUILT_MODULE. + $(strip_output): $(strip_input) + @echo "target Unstripped: $(PRIVATE_MODULE) ($@)" + $(copy-file-to-target) +endif # my_strip_module + +$(cleantarget): PRIVATE_CLEAN_FILES += \ + $(linked_module) \ + $(inject_module) \ + $(breakpad_output) \ + $(symbolic_output) \ + $(strip_output) diff --git a/make/core/empty_test_config.xml b/make/core/empty_test_config.xml new file mode 100644 index 0000000..7c9daff --- /dev/null +++ b/make/core/empty_test_config.xml @@ -0,0 +1,19 @@ + + + + diff --git a/make/core/envsetup.mk b/make/core/envsetup.mk new file mode 100644 index 0000000..c32d380 --- /dev/null +++ b/make/core/envsetup.mk @@ -0,0 +1,1049 @@ +# Variables we check: +# HOST_BUILD_TYPE = { release debug } +# TARGET_BUILD_TYPE = { release debug } +# and we output a bunch of variables, see the case statement at +# the bottom for the full list +# OUT_DIR is also set to "out" if it's not already set. +# this allows you to set it to somewhere else if you like +# SCAN_EXCLUDE_DIRS is an optional, whitespace separated list of +# directories that will also be excluded from full checkout tree +# searches for source or make files, in addition to OUT_DIR. +# This can be useful if you set OUT_DIR to be a different directory +# than other outputs of your build system. + +# Returns all words in $1 up to and including $2 +define find_and_earlier + $(strip $(if $(1), + $(firstword $(1)) + $(if $(filter $(firstword $(1)),$(2)),, + $(call find_and_earlier,$(wordlist 2,$(words $(1)),$(1)),$(2))))) +endef + +#$(warning $(call find_and_earlier,A B C,A)) +#$(warning $(call find_and_earlier,A B C,B)) +#$(warning $(call find_and_earlier,A B C,C)) +#$(warning $(call find_and_earlier,A B C,D)) + +define version-list +$(1)P1A $(1)P1B $(1)P2A $(1)P2B $(1)D1A $(1)D1B $(1)D2A $(1)D2B $(1)Q1A $(1)Q1B $(1)Q2A $(1)Q2B $(1)Q3A $(1)Q3B +endef + +PREV_VERSIONS := OPR1 OPD1 OPD2 OPM1 OPM2 PPR1 PPD1 PPD2 PPM1 PPM2 QPR1 +ALL_VERSIONS := Q R S T U V W X Y Z +ALL_VERSIONS := $(PREV_VERSIONS) $(foreach v,$(ALL_VERSIONS),$(call version-list,$(v))) +PREV_VERSIONS := + +# Filters ALL_VERSIONS down to the range [$1, $2], and errors if $1 > $2 or $3 is +# not in [$1, $2] +# $(1): min platform version +# $(2): max platform version +# $(3): default platform version +define allowed-platform-versions +$(strip \ + $(if $(filter $(ALL_VERSIONS),$(1)),, + $(error Invalid MIN_PLATFORM_VERSION '$(1)')) + $(if $(filter $(ALL_VERSIONS),$(2)),, + $(error Invalid MAX_PLATFORM_VERSION '$(2)')) + $(if $(filter $(ALL_VERSIONS),$(3)),, + $(error Invalid DEFAULT_PLATFORM_VERSION '$(3)')) + + $(eval allowed_versions_ := $(call find_and_earlier,$(ALL_VERSIONS),$(2))) + + $(if $(filter $(allowed_versions_),$(1)),, + $(error MIN_PLATFORM_VERSION '$(1)' must be before MAX_PLATFORM_VERSION '$(2)')) + + $(eval allowed_versions_ := $(1) \ + $(filter-out $(call find_and_earlier,$(allowed_versions_),$(1)),$(allowed_versions_))) + + $(if $(filter $(allowed_versions_),$(3)),, + $(error DEFAULT_PLATFORM_VERSION '$(3)' must be between MIN_PLATFORM_VERSION '$(1)' and MAX_PLATFORM_VERSION '$(2)')) + + $(allowed_versions_)) +endef + +#$(warning $(call allowed-platform-versions,OPR1,PPR1,OPR1)) +#$(warning $(call allowed-platform-versions,OPM1,PPR1,OPR1)) + +# Set up version information. +include $(BUILD_SYSTEM)/version_defaults.mk + +ENABLED_VERSIONS := $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION)) + +$(foreach v,$(ENABLED_VERSIONS), \ + $(eval IS_AT_LEAST_$(v) := true)) + +# --------------------------------------------------------------- +# If you update the build system such that the environment setup +# or buildspec.mk need to be updated, increment this number, and +# people who haven't re-run those will have to do so before they +# can build. Make sure to also update the corresponding value in +# buildspec.mk.default and envsetup.sh. +CORRECT_BUILD_ENV_SEQUENCE_NUMBER := 13 + +# --------------------------------------------------------------- +# The product defaults to generic on hardware +ifeq ($(TARGET_PRODUCT),) +TARGET_PRODUCT := aosp_arm +endif + + +# the variant -- the set of files that are included for a build +ifeq ($(strip $(TARGET_BUILD_VARIANT)),) +TARGET_BUILD_VARIANT := eng +endif + +TARGET_BUILD_APPS ?= +TARGET_BUILD_UNBUNDLED_IMAGE ?= + +# Set to true for an unbundled build, i.e. a build without +# support for platform targets like the system image. This also +# disables consistency checks that only apply to full platform +# builds. +TARGET_BUILD_UNBUNDLED ?= + +# TARGET_BUILD_APPS implies unbundled build, otherwise we default +# to bundled (i.e. platform targets such as the system image are +# included). +ifneq ($(TARGET_BUILD_APPS),) + TARGET_BUILD_UNBUNDLED := true +endif + +# TARGET_BUILD_UNBUNDLED_IMAGE also implies unbundled build. +# (i.e. it targets to only unbundled image, such as the vendor image, +# ,or the product image). +ifneq ($(TARGET_BUILD_UNBUNDLED_IMAGE),) + TARGET_BUILD_UNBUNDLED := true +endif + +.KATI_READONLY := \ + TARGET_PRODUCT \ + TARGET_BUILD_VARIANT \ + TARGET_BUILD_APPS \ + TARGET_BUILD_UNBUNDLED \ + TARGET_BUILD_UNBUNDLED_IMAGE \ + +# --------------------------------------------------------------- +# Set up configuration for host machine. We don't do cross- +# compiles except for arm, so the HOST is whatever we are +# running on + +# HOST_OS +ifneq (,$(findstring Linux,$(UNAME))) + HOST_OS := linux +endif +ifneq (,$(findstring Darwin,$(UNAME))) + HOST_OS := darwin +endif + +HOST_OS_EXTRA := $(shell uname -rsm) +ifeq ($(HOST_OS),linux) + ifneq ($(wildcard /etc/os-release),) + HOST_OS_EXTRA += $(shell source /etc/os-release; echo $$PRETTY_NAME) + endif +else ifeq ($(HOST_OS),darwin) + HOST_OS_EXTRA += $(shell sw_vers -productVersion) +endif +HOST_OS_EXTRA := $(subst $(space),-,$(HOST_OS_EXTRA)) + +# BUILD_OS is the real host doing the build. +BUILD_OS := $(HOST_OS) + +# We can do the cross-build only on Linux +ifeq ($(HOST_OS),linux) + # Windows has been the default host_cross OS + ifeq (,$(filter-out windows,$(HOST_CROSS_OS))) + # We can only create static host binaries for Linux, so if static host + # binaries are requested, turn off Windows cross-builds. + ifeq ($(BUILD_HOST_static),) + HOST_CROSS_OS := windows + HOST_CROSS_ARCH := x86 + HOST_CROSS_2ND_ARCH := x86_64 + 2ND_HOST_CROSS_IS_64_BIT := true + endif + else ifeq ($(HOST_CROSS_OS),linux_bionic) + ifeq (,$(HOST_CROSS_ARCH)) + $(error HOST_CROSS_ARCH missing.) + endif + else + $(error Unsupported HOST_CROSS_OS $(HOST_CROSS_OS)) + endif +else ifeq ($(HOST_OS),darwin) + HOST_CROSS_OS := darwin + HOST_CROSS_ARCH := arm64 + HOST_CROSS_2ND_ARCH := +endif + +ifeq ($(HOST_OS),) +$(error Unable to determine HOST_OS from uname -sm: $(UNAME)!) +endif + +# HOST_ARCH +ifneq (,$(findstring x86_64,$(UNAME))) + HOST_ARCH := x86_64 + HOST_2ND_ARCH := x86 + HOST_IS_64_BIT := true +else +ifneq (,$(findstring i686,$(UNAME))$(findstring x86,$(UNAME))) +$(error Building on a 32-bit x86 host is not supported: $(UNAME)!) +endif +endif + +ifeq ($(HOST_OS),darwin) + # Mac no longer supports 32-bit executables + HOST_2ND_ARCH := +endif + +HOST_2ND_ARCH_VAR_PREFIX := 2ND_ +HOST_2ND_ARCH_MODULE_SUFFIX := _32 +HOST_CROSS_2ND_ARCH_VAR_PREFIX := 2ND_ +HOST_CROSS_2ND_ARCH_MODULE_SUFFIX := _64 +TARGET_2ND_ARCH_VAR_PREFIX := 2ND_ +.KATI_READONLY := \ + HOST_ARCH \ + HOST_2ND_ARCH \ + HOST_IS_64_BIT \ + HOST_2ND_ARCH_VAR_PREFIX \ + HOST_2ND_ARCH_MODULE_SUFFIX \ + HOST_CROSS_2ND_ARCH_VAR_PREFIX \ + HOST_CROSS_2ND_ARCH_MODULE_SUFFIX \ + TARGET_2ND_ARCH_VAR_PREFIX \ + +combo_target := HOST_ +combo_2nd_arch_prefix := +include $(BUILD_COMBOS)/select.mk + +ifdef HOST_2ND_ARCH + combo_2nd_arch_prefix := $(HOST_2ND_ARCH_VAR_PREFIX) + include $(BUILD_SYSTEM)/combo/select.mk +endif + +# Load the windows cross compiler under Linux +ifdef HOST_CROSS_OS + combo_target := HOST_CROSS_ + combo_2nd_arch_prefix := + include $(BUILD_SYSTEM)/combo/select.mk + + ifdef HOST_CROSS_2ND_ARCH + combo_2nd_arch_prefix := $(HOST_CROSS_2ND_ARCH_VAR_PREFIX) + include $(BUILD_SYSTEM)/combo/select.mk + endif +endif + +# on windows, the tools have .exe at the end, and we depend on the +# host config stuff being done first + +BUILD_ARCH := $(HOST_ARCH) +BUILD_2ND_ARCH := $(HOST_2ND_ARCH) + +ifeq ($(HOST_ARCH),) +$(error Unable to determine HOST_ARCH from uname -sm: $(UNAME)!) +endif + +# the host build defaults to release, and it must be release or debug +ifeq ($(HOST_BUILD_TYPE),) +HOST_BUILD_TYPE := release +endif + +ifneq ($(HOST_BUILD_TYPE),release) +ifneq ($(HOST_BUILD_TYPE),debug) +$(error HOST_BUILD_TYPE must be either release or debug, not '$(HOST_BUILD_TYPE)') +endif +endif + +# We don't want to move all the prebuilt host tools to a $(HOST_OS)-x86_64 dir. +HOST_PREBUILT_ARCH := x86 +# This is the standard way to name a directory containing prebuilt host +# objects. E.g., prebuilt/$(HOST_PREBUILT_TAG)/cc +HOST_PREBUILT_TAG := $(BUILD_OS)-$(HOST_PREBUILT_ARCH) + +# TARGET_COPY_OUT_* are all relative to the staging directory, ie PRODUCT_OUT. +# Define them here so they can be used in product config files. +TARGET_COPY_OUT_SYSTEM := system +TARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm +TARGET_COPY_OUT_SYSTEM_OTHER := system_other +TARGET_COPY_OUT_DATA := data +TARGET_COPY_OUT_ASAN := $(TARGET_COPY_OUT_DATA)/asan +TARGET_COPY_OUT_OEM := oem +TARGET_COPY_OUT_RAMDISK := ramdisk +TARGET_COPY_OUT_DEBUG_RAMDISK := debug_ramdisk +TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK := vendor_debug_ramdisk +TARGET_COPY_OUT_TEST_HARNESS_RAMDISK := test_harness_ramdisk +TARGET_COPY_OUT_ROOT := root +TARGET_COPY_OUT_RECOVERY := recovery +# The directory used for optional partitions depend on the BoardConfig, so +# they're defined to placeholder values here and swapped after reading the +# BoardConfig, to be either the partition dir, or a subdir within 'system'. +_vendor_path_placeholder := ||VENDOR-PATH-PH|| +_product_path_placeholder := ||PRODUCT-PATH-PH|| +_system_ext_path_placeholder := ||SYSTEM_EXT-PATH-PH|| +_odm_path_placeholder := ||ODM-PATH-PH|| +_vendor_dlkm_path_placeholder := ||VENDOR_DLKM-PATH-PH|| +_odm_dlkm_path_placeholder := ||ODM_DLKM-PATH-PH|| +_system_dlkm_path_placeholder := ||SYSTEM_DLKM-PATH-PH|| +TARGET_COPY_OUT_VENDOR := $(_vendor_path_placeholder) +TARGET_COPY_OUT_VENDOR_RAMDISK := vendor_ramdisk +TARGET_COPY_OUT_VENDOR_KERNEL_RAMDISK := vendor_kernel_ramdisk +TARGET_COPY_OUT_PRODUCT := $(_product_path_placeholder) +# TODO(b/135957588) TARGET_COPY_OUT_PRODUCT_SERVICES will copy the target to +# product +TARGET_COPY_OUT_PRODUCT_SERVICES := $(_product_path_placeholder) +TARGET_COPY_OUT_SYSTEM_EXT := $(_system_ext_path_placeholder) +TARGET_COPY_OUT_ODM := $(_odm_path_placeholder) +TARGET_COPY_OUT_VENDOR_DLKM := $(_vendor_dlkm_path_placeholder) +TARGET_COPY_OUT_ODM_DLKM := $(_odm_dlkm_path_placeholder) +TARGET_COPY_OUT_SYSTEM_DLKM := $(_system_dlkm_path_placeholder) + +# Returns the non-sanitized version of the path provided in $1. +define get_non_asan_path +$(patsubst $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/%,$(PRODUCT_OUT)/%,$1) +endef + +################################################################# +# Set up minimal BOOTCLASSPATH list of jars to build/execute +# java code with dalvikvm/art. +# Jars present in the ART apex. These should match exactly the list of Java +# libraries in art-bootclasspath-fragment. The APEX variant name +# (com.android.art) is the same regardless which Soong module provides the ART +# APEX. See the long comment in build/soong/java/dexprepopt_bootjars.go for +# details. +ART_APEX_JARS := \ + com.android.art:core-oj \ + com.android.art:core-libart \ + com.android.art:okhttp \ + com.android.art:bouncycastle \ + com.android.art:apache-xml +# With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco. +ifeq (true,$(EMMA_INSTRUMENT_FRAMEWORK)) + ART_APEX_JARS += com.android.art:jacocoagent +endif +################################################################# + +# Dumps all variables that match [A-Z][A-Z0-9_]* (with a few exceptions) +# to the file at $(1). It is used to print only the variables that are +# likely to be relevant to the product or board configuration. +# Soong config variables are dumped as $(call soong_config_set) calls +# instead of the raw variable values, because mk2rbc can't read the +# raw ones. +define dump-variables-rbc +$(eval _dump_variables_rbc_excluded := \ + BOARD_PLAT_PRIVATE_SEPOLICY_DIR \ + BOARD_PLAT_PUBLIC_SEPOLICY_DIR \ + BUILD_NUMBER \ + DATE \ + LOCAL_PATH \ + MAKEFILE_LIST \ + PRODUCTS \ + PRODUCT_COPY_OUT_% \ + RBC_PRODUCT_CONFIG \ + RBC_BOARD_CONFIG \ + SOONG_% \ + TOPDIR \ + TRACE_BEGIN_SOONG \ + USER) +$(file >$(OUT_DIR)/dump-variables-rbc-temp.txt,$(subst $(space),$(newline),$(sort $(filter-out $(_dump_variables_rbc_excluded),$(.VARIABLES))))) +$(file >$(1),\ +$(foreach v, $(shell grep -he "^[A-Z][A-Z0-9_]*$$" $(OUT_DIR)/dump-variables-rbc-temp.txt),\ +$(v) := $(strip $($(v)))$(newline))\ +$(foreach ns,$(sort $(SOONG_CONFIG_NAMESPACES)),\ +$(foreach v,$(sort $(SOONG_CONFIG_$(ns))),\ +$$(call soong_config_set,$(ns),$(v),$(SOONG_CONFIG_$(ns)_$(v)))$(newline)))) +endef + +# Read the product specs so we can get TARGET_DEVICE and other +# variables that we need in order to locate the output files. +include $(BUILD_SYSTEM)/product_config.mk + +build_variant := $(filter-out eng user userdebug,$(TARGET_BUILD_VARIANT)) +ifneq ($(build_variant)-$(words $(TARGET_BUILD_VARIANT)),-1) +$(warning bad TARGET_BUILD_VARIANT: $(TARGET_BUILD_VARIANT)) +$(error must be empty or one of: eng user userdebug) +endif + +SDK_HOST_ARCH := x86 +TARGET_OS := linux + +# Some board configuration files use $(PRODUCT_OUT) +TARGET_OUT_ROOT := $(OUT_DIR)/target +TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product +PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE) +.KATI_READONLY := TARGET_OUT_ROOT TARGET_PRODUCT_OUT_ROOT PRODUCT_OUT + +include $(BUILD_SYSTEM)/board_config.mk + +# the target build type defaults to release +ifneq ($(TARGET_BUILD_TYPE),debug) +TARGET_BUILD_TYPE := release +endif + +# --------------------------------------------------------------- +# figure out the output directories + +SOONG_OUT_DIR := $(OUT_DIR)/soong + +HOST_OUT_ROOT := $(OUT_DIR)/host + +.KATI_READONLY := SOONG_OUT_DIR HOST_OUT_ROOT + +# We want to avoid two host bin directories in multilib build. +HOST_OUT := $(HOST_OUT_ROOT)/$(HOST_OS)-$(HOST_PREBUILT_ARCH) + +# Soong now installs to the same directory as Make. +SOONG_HOST_OUT := $(HOST_OUT) + +HOST_CROSS_OUT := $(HOST_OUT_ROOT)/$(HOST_CROSS_OS)-$(HOST_CROSS_ARCH) + +.KATI_READONLY := HOST_OUT SOONG_HOST_OUT HOST_CROSS_OUT + +TARGET_COMMON_OUT_ROOT := $(TARGET_OUT_ROOT)/common +HOST_COMMON_OUT_ROOT := $(HOST_OUT_ROOT)/common + +.KATI_READONLY := TARGET_COMMON_OUT_ROOT HOST_COMMON_OUT_ROOT + +OUT_DOCS := $(TARGET_COMMON_OUT_ROOT)/docs +OUT_NDK_DOCS := $(TARGET_COMMON_OUT_ROOT)/ndk-docs +.KATI_READONLY := OUT_DOCS OUT_NDK_DOCS + +$(call KATI_obsolete,BUILD_OUT,Use HOST_OUT instead) + +BUILD_OUT_EXECUTABLES := $(HOST_OUT)/bin +SOONG_HOST_OUT_EXECUTABLES := $(SOONG_HOST_OUT)/bin +.KATI_READONLY := BUILD_OUT_EXECUTABLES SOONG_HOST_OUT_EXECUTABLES + +HOST_OUT_EXECUTABLES := $(HOST_OUT)/bin +HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT)/lib64 +HOST_OUT_DYLIB_LIBRARIES := $(HOST_OUT)/lib64 +HOST_OUT_RENDERSCRIPT_BITCODE := $(HOST_OUT_SHARED_LIBRARIES) +HOST_OUT_JAVA_LIBRARIES := $(HOST_OUT)/framework +HOST_OUT_SDK_ADDON := $(HOST_OUT)/sdk_addon +HOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest64 +HOST_OUT_COVERAGE := $(HOST_OUT)/coverage +HOST_OUT_TESTCASES := $(HOST_OUT)/testcases +.KATI_READONLY := \ + HOST_OUT_EXECUTABLES \ + HOST_OUT_SHARED_LIBRARIES \ + HOST_OUT_RENDERSCRIPT_BITCODE \ + HOST_OUT_JAVA_LIBRARIES \ + HOST_OUT_SDK_ADDON \ + HOST_OUT_NATIVE_TESTS \ + HOST_OUT_COVERAGE \ + HOST_OUT_TESTCASES + +HOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT)/bin +HOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib +HOST_CROSS_OUT_NATIVE_TESTS := $(HOST_CROSS_OUT)/nativetest +HOST_CROSS_OUT_COVERAGE := $(HOST_CROSS_OUT)/coverage +HOST_CROSS_OUT_TESTCASES := $(HOST_CROSS_OUT)/testcases +.KATI_READONLY := \ + HOST_CROSS_OUT_EXECUTABLES \ + HOST_CROSS_OUT_SHARED_LIBRARIES \ + HOST_CROSS_OUT_NATIVE_TESTS \ + HOST_CROSS_OUT_COVERAGE \ + HOST_CROSS_OUT_TESTCASES + +HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj +HOST_OUT_NOTICE_FILES := $(HOST_OUT_INTERMEDIATES)/NOTICE_FILES +HOST_OUT_COMMON_INTERMEDIATES := $(HOST_COMMON_OUT_ROOT)/obj +HOST_OUT_FAKE := $(HOST_OUT)/fake_packages +.KATI_READONLY := \ + HOST_OUT_INTERMEDIATES \ + HOST_OUT_NOTICE_FILES \ + HOST_OUT_COMMON_INTERMEDIATES \ + HOST_OUT_FAKE + +HOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj +HOST_CROSS_OUT_NOTICE_FILES := $(HOST_CROSS_OUT_INTERMEDIATES)/NOTICE_FILES +.KATI_READONLY := \ + HOST_CROSS_OUT_INTERMEDIATES \ + HOST_CROSS_OUT_NOTICE_FILES + +HOST_OUT_GEN := $(HOST_OUT)/gen +HOST_OUT_COMMON_GEN := $(HOST_COMMON_OUT_ROOT)/gen +.KATI_READONLY := \ + HOST_OUT_GEN \ + HOST_OUT_COMMON_GEN + +HOST_CROSS_OUT_GEN := $(HOST_CROSS_OUT)/gen +.KATI_READONLY := HOST_CROSS_OUT_GEN + +# Out for HOST_2ND_ARCH +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj32 +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT)/lib +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_EXECUTABLES := $(HOST_OUT_EXECUTABLES) +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_JAVA_LIBRARIES := $(HOST_OUT_JAVA_LIBRARIES) +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest +$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_TESTCASES := $(HOST_OUT_TESTCASES) +.KATI_READONLY := \ + $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATES \ + $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES \ + $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_EXECUTABLES \ + $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_JAVA_LIBRARIES \ + $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_NATIVE_TESTS \ + $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_TESTCASES + +# The default host library path. +# It always points to the path where we build libraries in the default bitness. +HOST_LIBRARY_PATH := $(HOST_OUT_SHARED_LIBRARIES) +.KATI_READONLY := HOST_LIBRARY_PATH + +# Out for HOST_CROSS_2ND_ARCH +$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj64 +$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib64 +$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT_EXECUTABLES) +$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_NATIVE_TESTS := $(HOST_CROSS_OUT)/nativetest64 +.KATI_READONLY := \ + $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATES \ + $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_SHARED_LIBRARIES \ + $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_EXECUTABLES \ + $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_NATIVE_TESTS + +ifneq ($(filter address,$(SANITIZE_TARGET)),) + TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_asan +else + TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj +endif +TARGET_OUT_HEADERS := $(TARGET_OUT_INTERMEDIATES)/include +.KATI_READONLY := TARGET_OUT_INTERMEDIATES TARGET_OUT_HEADERS + +ifneq ($(filter address,$(SANITIZE_TARGET)),) + TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj_asan +else + TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj +endif +.KATI_READONLY := TARGET_OUT_COMMON_INTERMEDIATES + +TARGET_OUT_GEN := $(PRODUCT_OUT)/gen +TARGET_OUT_COMMON_GEN := $(TARGET_COMMON_OUT_ROOT)/gen +.KATI_READONLY := TARGET_OUT_GEN TARGET_OUT_COMMON_GEN + +TARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM) +.KATI_READONLY := TARGET_OUT +ifneq ($(filter address,$(SANITIZE_TARGET)),) +target_out_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/system +ifeq ($(SANITIZE_LITE),true) +# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not +# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. +target_out_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/system +else +target_out_app_base := $(TARGET_OUT) +endif +else +target_out_shared_libraries_base := $(TARGET_OUT) +target_out_app_base := $(TARGET_OUT) +endif + +TARGET_OUT_EXECUTABLES := $(TARGET_OUT)/bin +TARGET_OUT_OPTIONAL_EXECUTABLES := $(TARGET_OUT)/xbin +ifeq ($(TARGET_IS_64_BIT),true) +# /system/lib always contains 32-bit libraries, +# and /system/lib64 (if present) always contains 64-bit libraries. +TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib64 +else +TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib +endif +TARGET_OUT_RENDERSCRIPT_BITCODE := $(TARGET_OUT_SHARED_LIBRARIES) +TARGET_OUT_JAVA_LIBRARIES := $(TARGET_OUT)/framework +TARGET_OUT_APPS := $(target_out_app_base)/app +TARGET_OUT_APPS_PRIVILEGED := $(target_out_app_base)/priv-app +TARGET_OUT_KEYLAYOUT := $(TARGET_OUT)/usr/keylayout +TARGET_OUT_KEYCHARS := $(TARGET_OUT)/usr/keychars +TARGET_OUT_ETC := $(TARGET_OUT)/etc +TARGET_OUT_NOTICE_FILES := $(TARGET_OUT_INTERMEDIATES)/NOTICE_FILES +TARGET_OUT_FAKE := $(PRODUCT_OUT)/fake_packages +TARGET_OUT_TESTCASES := $(PRODUCT_OUT)/testcases +.KATI_READONLY := \ + TARGET_OUT_EXECUTABLES \ + TARGET_OUT_OPTIONAL_EXECUTABLES \ + TARGET_OUT_SHARED_LIBRARIES \ + TARGET_OUT_RENDERSCRIPT_BITCODE \ + TARGET_OUT_JAVA_LIBRARIES \ + TARGET_OUT_APPS \ + TARGET_OUT_APPS_PRIVILEGED \ + TARGET_OUT_KEYLAYOUT \ + TARGET_OUT_KEYCHARS \ + TARGET_OUT_ETC \ + TARGET_OUT_NOTICE_FILES \ + TARGET_OUT_FAKE \ + TARGET_OUT_TESTCASES + +ifeq ($(SANITIZE_LITE),true) +# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not +# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. +TARGET_OUT_SYSTEM_OTHER := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_OTHER) +else +TARGET_OUT_SYSTEM_OTHER := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_OTHER) +endif +.KATI_READONLY := TARGET_OUT_SYSTEM_OTHER + +# Out for TARGET_2ND_ARCH +TARGET_2ND_ARCH_MODULE_SUFFIX := $(HOST_2ND_ARCH_MODULE_SUFFIX) +.KATI_READONLY := TARGET_2ND_ARCH_MODULE_SUFFIX + +ifneq ($(filter address,$(SANITIZE_TARGET)),) + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_$(TARGET_2ND_ARCH)_asan +else + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_$(TARGET_2ND_ARCH) +endif +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_EXECUTABLES := $(TARGET_OUT_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS := $(TARGET_OUT_APPS) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS_PRIVILEGED := $(TARGET_OUT_APPS_PRIVILEGED) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_TESTCASES := $(TARGET_OUT_TESTCASES) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_RENDERSCRIPT_BITCODE \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS_PRIVILEGED \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_TESTCASES + +MODULE_CLASS_APPS := app +MODULE_CLASS_EXECUTABLES := bin +MODULE_CLASS_JAVA_LIBRARIES := framework +MODULE_CLASS_NATIVE_TESTS := nativetest +MODULE_CLASS_METRIC_TESTS := benchmarktest +TARGET_OUT_DATA := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA) +TARGET_OUT_DATA_EXECUTABLES := $(TARGET_OUT_EXECUTABLES) +TARGET_OUT_DATA_SHARED_LIBRARIES := $(TARGET_OUT_SHARED_LIBRARIES) +TARGET_OUT_DATA_JAVA_LIBRARIES := $(TARGET_OUT_DATA)/framework +TARGET_OUT_DATA_APPS := $(TARGET_OUT_DATA)/app +TARGET_OUT_DATA_KEYLAYOUT := $(TARGET_OUT_KEYLAYOUT) +TARGET_OUT_DATA_KEYCHARS := $(TARGET_OUT_KEYCHARS) +TARGET_OUT_DATA_ETC := $(TARGET_OUT_ETC) +ifeq ($(TARGET_IS_64_BIT),true) +TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest64 +TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest64 +TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest64$(TARGET_VENDOR_TEST_SUFFIX) +TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest64$(TARGET_VENDOR_TEST_SUFFIX) +else +TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest +TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest +TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest$(TARGET_VENDOR_TEST_SUFFIX) +TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest$(TARGET_VENDOR_TEST_SUFFIX) +endif +MODULE_CLASS_FAKE := fake_packages +TARGET_OUT_DATA_FAKE := $(TARGET_OUT_DATA)/fake_packages +.KATI_READONLY := \ + TARGET_OUT_DATA \ + TARGET_OUT_DATA_EXECUTABLES \ + TARGET_OUT_DATA_SHARED_LIBRARIES \ + TARGET_OUT_DATA_JAVA_LIBRARIES \ + TARGET_OUT_DATA_APPS \ + TARGET_OUT_DATA_KEYLAYOUT \ + TARGET_OUT_DATA_KEYCHARS \ + TARGET_OUT_DATA_ETC \ + TARGET_OUT_DATA_NATIVE_TESTS \ + TARGET_OUT_DATA_METRIC_TESTS \ + TARGET_OUT_VENDOR_NATIVE_TESTS \ + TARGET_OUT_VENDOR_METRIC_TESTS \ + TARGET_OUT_DATA_FAKE \ + MODULE_CLASS_APPS \ + MODULE_CLASS_EXECUTABLES \ + MODULE_CLASS_JAVA_LIBRARIES \ + MODULE_CLASS_NATIVE_TESTS \ + MODULE_CLASS_METRIC_TESTS \ + MODULE_CLASS_FAKE + +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_EXECUTABLES := $(TARGET_OUT_DATA_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_SHARED_LIBRARIES := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_APPS := $(TARGET_OUT_DATA_APPS) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest$(TARGET_VENDOR_TEST_SUFFIX) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest$(TARGET_VENDOR_TEST_SUFFIX) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_METRIC_TESTS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_NATIVE_TESTS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_METRIC_TESTS \ + +TARGET_OUT_CACHE := $(PRODUCT_OUT)/cache +.KATI_READONLY := TARGET_OUT_CACHE + +TARGET_OUT_VENDOR := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR) +.KATI_READONLY := TARGET_OUT_VENDOR +ifneq ($(filter address,$(SANITIZE_TARGET)),) +target_out_vendor_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_VENDOR) +ifeq ($(SANITIZE_LITE),true) +# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not +# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. +target_out_vendor_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_VENDOR) +else +target_out_vendor_app_base := $(TARGET_OUT_VENDOR) +endif +else +target_out_vendor_shared_libraries_base := $(TARGET_OUT_VENDOR) +target_out_vendor_app_base := $(TARGET_OUT_VENDOR) +endif + +TARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR)/bin +TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES := $(TARGET_OUT_VENDOR)/xbin +ifeq ($(TARGET_IS_64_BIT),true) +TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib64 +else +TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib +endif +TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES) +TARGET_OUT_VENDOR_JAVA_LIBRARIES := $(TARGET_OUT_VENDOR)/framework +TARGET_OUT_VENDOR_APPS := $(target_out_vendor_app_base)/app +TARGET_OUT_VENDOR_APPS_PRIVILEGED := $(target_out_vendor_app_base)/priv-app +TARGET_OUT_VENDOR_ETC := $(TARGET_OUT_VENDOR)/etc +.KATI_READONLY := \ + TARGET_OUT_VENDOR_EXECUTABLES \ + TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES \ + TARGET_OUT_VENDOR_SHARED_LIBRARIES \ + TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE \ + TARGET_OUT_VENDOR_JAVA_LIBRARIES \ + TARGET_OUT_VENDOR_APPS \ + TARGET_OUT_VENDOR_APPS_PRIVILEGED \ + TARGET_OUT_VENDOR_ETC + +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS := $(TARGET_OUT_VENDOR_APPS) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS_PRIVILEGED := $(TARGET_OUT_VENDOR_APPS_PRIVILEGED) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS_PRIVILEGED + +TARGET_OUT_OEM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_OEM) +TARGET_OUT_OEM_EXECUTABLES := $(TARGET_OUT_OEM)/bin +ifeq ($(TARGET_IS_64_BIT),true) +TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib64 +else +TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib +endif +# We don't expect Java libraries in the oem.img. +# TARGET_OUT_OEM_JAVA_LIBRARIES:= $(TARGET_OUT_OEM)/framework +TARGET_OUT_OEM_APPS := $(TARGET_OUT_OEM)/app +TARGET_OUT_OEM_ETC := $(TARGET_OUT_OEM)/etc +.KATI_READONLY := \ + TARGET_OUT_OEM \ + TARGET_OUT_OEM_EXECUTABLES \ + TARGET_OUT_OEM_SHARED_LIBRARIES \ + TARGET_OUT_OEM_APPS \ + TARGET_OUT_OEM_ETC + +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_EXECUTABLES := $(TARGET_OUT_OEM_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_APPS := $(TARGET_OUT_OEM_APPS) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_APPS \ + +TARGET_OUT_ODM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM) +ifneq ($(filter address,$(SANITIZE_TARGET)),) +target_out_odm_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_OEM) +ifeq ($(SANITIZE_LITE),true) +# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not +# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. +target_out_odm_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_OEM) +else +target_out_odm_app_base := $(TARGET_OUT_ODM) +endif +else +target_out_odm_shared_libraries_base := $(TARGET_OUT_ODM) +target_out_odm_app_base := $(TARGET_OUT_ODM) +endif + +TARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM)/bin +TARGET_OUT_ODM_OPTIONAL_EXECUTABLES := $(TARGET_OUT_ODM)/xbin +ifeq ($(TARGET_IS_64_BIT),true) +TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib64 +else +TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib +endif +TARGET_OUT_ODM_RENDERSCRIPT_BITCODE := $(TARGET_OUT_ODM_SHARED_LIBRARIES) +TARGET_OUT_ODM_JAVA_LIBRARIES := $(TARGET_OUT_ODM)/framework +TARGET_OUT_ODM_APPS := $(target_out_odm_app_base)/app +TARGET_OUT_ODM_APPS_PRIVILEGED := $(target_out_odm_app_base)/priv-app +TARGET_OUT_ODM_ETC := $(TARGET_OUT_ODM)/etc +.KATI_READONLY := \ + TARGET_OUT_ODM \ + TARGET_OUT_ODM_EXECUTABLES \ + TARGET_OUT_ODM_OPTIONAL_EXECUTABLES \ + TARGET_OUT_ODM_SHARED_LIBRARIES \ + TARGET_OUT_ODM_RENDERSCRIPT_BITCODE \ + TARGET_OUT_ODM_JAVA_LIBRARIES \ + TARGET_OUT_ODM_APPS \ + TARGET_OUT_ODM_APPS_PRIVILEGED \ + TARGET_OUT_ODM_ETC + +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS := $(TARGET_OUT_ODM_APPS) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS_PRIVILEGED := $(TARGET_OUT_ODM_APPS_PRIVILEGED) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_RENDERSCRIPT_BITCODE \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS_PRIVILEGED + +TARGET_OUT_VENDOR_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_DLKM) + +TARGET_OUT_VENDOR_DLKM_ETC := $(TARGET_OUT_VENDOR_DLKM)/etc +.KATI_READONLY := \ + TARGET_OUT_VENDOR_DLKM_ETC + +# Unlike other partitions, vendor_dlkm should only contain kernel modules. +TARGET_OUT_VENDOR_DLKM_EXECUTABLES := +TARGET_OUT_VENDOR_DLKM_OPTIONAL_EXECUTABLES := +TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES := +TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE := +TARGET_OUT_VENDOR_DLKM_JAVA_LIBRARIES := +TARGET_OUT_VENDOR_DLKM_APPS := +TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_EXECUTABLES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED := +$(KATI_obsolete_var \ + TARGET_OUT_VENDOR_DLKM_EXECUTABLES \ + TARGET_OUT_VENDOR_DLKM_OPTIONAL_EXECUTABLES \ + TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES \ + TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE \ + TARGET_OUT_VENDOR_DLKM_JAVA_LIBRARIES \ + TARGET_OUT_VENDOR_DLKM_APPS \ + TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED \ + , vendor_dlkm should not contain any executables, libraries, or apps) + +TARGET_OUT_ODM_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM_DLKM) + +TARGET_OUT_ODM_DLKM_ETC := $(TARGET_OUT_ODM_DLKM)/etc +.KATI_READONLY := \ + TARGET_OUT_ODM_DLKM_ETC + +# Unlike other partitions, odm_dlkm should only contain kernel modules. +TARGET_OUT_ODM_DLKM_EXECUTABLES := +TARGET_OUT_ODM_DLKM_OPTIONAL_EXECUTABLES := +TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES := +TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE := +TARGET_OUT_ODM_DLKM_JAVA_LIBRARIES := +TARGET_OUT_ODM_DLKM_APPS := +TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_EXECUTABLES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED := +$(KATI_obsolete_var \ + TARGET_OUT_ODM_DLKM_EXECUTABLES \ + TARGET_OUT_ODM_DLKM_OPTIONAL_EXECUTABLES \ + TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES \ + TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE \ + TARGET_OUT_ODM_DLKM_JAVA_LIBRARIES \ + TARGET_OUT_ODM_DLKM_APPS \ + TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED \ + , odm_dlkm should not contain any executables, libraries, or apps) + +TARGET_OUT_SYSTEM_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_DLKM) + +# Unlike other partitions, system_dlkm should only contain kernel modules. +TARGET_OUT_SYSTEM_DLKM_EXECUTABLES := +TARGET_OUT_SYSTEM_DLKM_OPTIONAL_EXECUTABLES := +TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES := +TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE := +TARGET_OUT_SYSTEM_DLKM_JAVA_LIBRARIES := +TARGET_OUT_SYSTEM_DLKM_APPS := +TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_EXECUTABLES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS := +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED := +$(KATI_obsolete_var \ + TARGET_OUT_SYSTEM_DLKM_EXECUTABLES \ + TARGET_OUT_SYSTEM_DLKM_OPTIONAL_EXECUTABLES \ + TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES \ + TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE \ + TARGET_OUT_SYSTEM_DLKM_JAVA_LIBRARIES \ + TARGET_OUT_SYSTEM_DLKM_APPS \ + TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED \ + , system_dlkm should not contain any executables, libraries, or apps) + +TARGET_OUT_PRODUCT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_PRODUCT) +TARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT)/bin +.KATI_READONLY := TARGET_OUT_PRODUCT +ifneq ($(filter address,$(SANITIZE_TARGET)),) +target_out_product_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_PRODUCT) +ifeq ($(SANITIZE_LITE),true) +# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not +# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. +target_out_product_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_PRODUCT) +else +target_out_product_app_base := $(TARGET_OUT_PRODUCT) +endif +else +target_out_product_shared_libraries_base := $(TARGET_OUT_PRODUCT) +target_out_product_app_base := $(TARGET_OUT_PRODUCT) +endif + +ifeq ($(TARGET_IS_64_BIT),true) +TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib64 +else +TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib +endif +TARGET_OUT_PRODUCT_JAVA_LIBRARIES := $(TARGET_OUT_PRODUCT)/framework +TARGET_OUT_PRODUCT_APPS := $(target_out_product_app_base)/app +TARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(target_out_product_app_base)/priv-app +TARGET_OUT_PRODUCT_ETC := $(TARGET_OUT_PRODUCT)/etc +.KATI_READONLY := \ + TARGET_OUT_PRODUCT_EXECUTABLES \ + TARGET_OUT_PRODUCT_SHARED_LIBRARIES \ + TARGET_OUT_PRODUCT_JAVA_LIBRARIES \ + TARGET_OUT_PRODUCT_APPS \ + TARGET_OUT_PRODUCT_APPS_PRIVILEGED \ + TARGET_OUT_PRODUCT_ETC + +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS := $(TARGET_OUT_PRODUCT_APPS) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(TARGET_OUT_PRODUCT_APPS_PRIVILEGED) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS_PRIVILEGED + +TARGET_OUT_SYSTEM_EXT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_EXT) +ifneq ($(filter address,$(SANITIZE_TARGET)),) +target_out_system_ext_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_EXT) +ifeq ($(SANITIZE_LITE),true) +# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not +# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. +target_out_system_ext_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_EXT) +else +target_out_system_ext_app_base := $(TARGET_OUT_SYSTEM_EXT) +endif +else +target_out_system_ext_shared_libraries_base := $(TARGET_OUT_SYSTEM_EXT) +target_out_system_ext_app_base := $(TARGET_OUT_SYSTEM_EXT) +endif + +ifeq ($(TARGET_IS_64_BIT),true) +TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib64 +else +TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib +endif +TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES:= $(TARGET_OUT_SYSTEM_EXT)/framework +TARGET_OUT_SYSTEM_EXT_APPS := $(target_out_system_ext_app_base)/app +TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(target_out_system_ext_app_base)/priv-app +TARGET_OUT_SYSTEM_EXT_ETC := $(TARGET_OUT_SYSTEM_EXT)/etc +TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT)/bin +.KATI_READONLY := \ + TARGET_OUT_SYSTEM_EXT_EXECUTABLES \ + TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \ + TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES \ + TARGET_OUT_SYSTEM_EXT_APPS \ + TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED \ + TARGET_OUT_SYSTEM_EXT_ETC + +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT_EXECUTABLES) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS := $(TARGET_OUT_SYSTEM_EXT_APPS) +$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED) +.KATI_READONLY := \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS \ + $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED + +TARGET_OUT_BREAKPAD := $(PRODUCT_OUT)/breakpad +.KATI_READONLY := TARGET_OUT_BREAKPAD + +TARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols +TARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin +TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib +TARGET_OUT_VENDOR_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/$(TARGET_COPY_OUT_VENDOR)/lib +TARGET_ROOT_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED) +TARGET_ROOT_OUT_BIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/bin +TARGET_OUT_COVERAGE := $(PRODUCT_OUT)/coverage +.KATI_READONLY := \ + TARGET_OUT_UNSTRIPPED \ + TARGET_OUT_EXECUTABLES_UNSTRIPPED \ + TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED \ + TARGET_OUT_VENDOR_SHARED_LIBRARIES_UNSTRIPPED \ + TARGET_ROOT_OUT_UNSTRIPPED \ + TARGET_ROOT_OUT_BIN_UNSTRIPPED \ + TARGET_OUT_COVERAGE + +TARGET_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RAMDISK) +TARGET_RAMDISK_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED) +TARGET_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DEBUG_RAMDISK) +TARGET_VENDOR_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK) +TARGET_TEST_HARNESS_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_TEST_HARNESS_RAMDISK) + +TARGET_SYSTEM_DLKM_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_DLKM) +.KATI_READONLY := TARGET_SYSTEM_DLKM_OUT + +TARGET_VENDOR_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_RAMDISK) +TARGET_VENDOR_KERNEL_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_KERNEL_RAMDISK) + +TARGET_ROOT_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ROOT) +TARGET_ROOT_OUT_BIN := $(TARGET_ROOT_OUT)/bin +TARGET_ROOT_OUT_ETC := $(TARGET_ROOT_OUT)/etc +TARGET_ROOT_OUT_USR := $(TARGET_ROOT_OUT)/usr +.KATI_READONLY := \ + TARGET_ROOT_OUT \ + TARGET_ROOT_OUT_BIN \ + TARGET_ROOT_OUT_ETC \ + TARGET_ROOT_OUT_USR + +TARGET_RECOVERY_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RECOVERY) +TARGET_RECOVERY_ROOT_OUT := $(TARGET_RECOVERY_OUT)/root +.KATI_READONLY := \ + TARGET_RECOVERY_OUT \ + TARGET_RECOVERY_ROOT_OUT + +TARGET_SYSLOADER_OUT := $(PRODUCT_OUT)/sysloader +TARGET_SYSLOADER_ROOT_OUT := $(TARGET_SYSLOADER_OUT)/root +TARGET_SYSLOADER_SYSTEM_OUT := $(TARGET_SYSLOADER_OUT)/root/system +.KATI_READONLY := \ + TARGET_SYSLOADER_OUT \ + TARGET_SYSLOADER_ROOT_OUT \ + TARGET_SYSLOADER_SYSTEM_OUT + +TARGET_INSTALLER_OUT := $(PRODUCT_OUT)/installer +TARGET_INSTALLER_DATA_OUT := $(TARGET_INSTALLER_OUT)/data +TARGET_INSTALLER_ROOT_OUT := $(TARGET_INSTALLER_OUT)/root +TARGET_INSTALLER_SYSTEM_OUT := $(TARGET_INSTALLER_OUT)/root/system +.KATI_READONLY := \ + TARGET_INSTALLER_OUT \ + TARGET_INSTALLER_DATA_OUT \ + TARGET_INSTALLER_ROOT_OUT \ + TARGET_INSTALLER_SYSTEM_OUT + +COMMON_MODULE_CLASSES := TARGET_NOTICE_FILES HOST_NOTICE_FILES HOST_JAVA_LIBRARIES +PER_ARCH_MODULE_CLASSES := SHARED_LIBRARIES STATIC_LIBRARIES EXECUTABLES GYP RENDERSCRIPT_BITCODE NATIVE_TESTS HEADER_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES +.KATI_READONLY := COMMON_MODULE_CLASSES PER_ARCH_MODULE_CLASSES + +ifeq ($(CALLED_FROM_SETUP),true) +PRINT_BUILD_CONFIG ?= true +endif diff --git a/make/core/executable.mk b/make/core/executable.mk new file mode 100644 index 0000000..9175e0a --- /dev/null +++ b/make/core/executable.mk @@ -0,0 +1,81 @@ +# We don't automatically set up rules to build executables for both +# TARGET_ARCH and TARGET_2ND_ARCH. +# By default, an executable is built for TARGET_ARCH. +# To build it for TARGET_2ND_ARCH in a 64bit product, use "LOCAL_MULTILIB := 32" +# To build it for both set LOCAL_MULTILIB := both and specify +# LOCAL_MODULE_PATH_32 and LOCAL_MODULE_PATH_64 or LOCAL_MODULE_STEM_32 and +# LOCAL_MODULE_STEM_64 + +ifdef LOCAL_IS_HOST_MODULE + $(call pretty-error,BUILD_EXECUTABLE is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_EXECUTABLE instead.) +endif + +my_skip_this_target := +ifneq ($(filter address,$(SANITIZE_TARGET)),) + ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE)) + my_skip_this_target := true + else ifeq (false, $(LOCAL_CLANG)) + my_skip_this_target := true + else ifeq (never, $(LOCAL_SANITIZE)) + my_skip_this_target := true + endif +endif + +ifneq (true,$(my_skip_this_target)) +$(call record-module-type,EXECUTABLE) + +my_prefix := TARGET_ +include $(BUILD_SYSTEM)/multilib.mk + +ifeq ($(my_module_multilib),both) +ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) +ifeq ($(LOCAL_MODULE_PATH_32)$(LOCAL_MODULE_STEM_32),) +$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_32 or LOCAL_MODULE_PATH_32 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) +endif +ifeq ($(LOCAL_MODULE_PATH_64)$(LOCAL_MODULE_STEM_64),) +$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_64 or LOCAL_MODULE_PATH_64 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) +endif +endif +else #!LOCAL_MULTILIB == both +LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true +endif + +ifdef TARGET_2ND_ARCH +LOCAL_2ND_ARCH_VAR_PREFIX := +endif + +my_skip_non_preferred_arch := + +# check if preferred arch is supported +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# first arch is supported +include $(BUILD_SYSTEM)/executable_internal.mk +ifneq ($(my_module_multilib),both) +my_skip_non_preferred_arch := true +endif +endif + +# check if preferred arch was not supported or asked to build both +ifndef my_skip_non_preferred_arch +ifdef TARGET_2ND_ARCH + +LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) + +# check if non-preferred arch is supported +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# non-preferred arch is supported +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := +include $(BUILD_SYSTEM)/executable_internal.mk +endif +endif # TARGET_2ND_ARCH +endif # !my_skip_non_preferred_arch || LOCAL_MULTILIB +LOCAL_2ND_ARCH_VAR_PREFIX := +LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := + +my_module_arch_supported := + +endif diff --git a/make/core/executable_internal.mk b/make/core/executable_internal.mk new file mode 100644 index 0000000..fb14cce --- /dev/null +++ b/make/core/executable_internal.mk @@ -0,0 +1,113 @@ +########################################################### +## Standard rules for building an executable file. +## +## Additional inputs from base_rules.make: +## None. +########################################################### + +ifeq ($(strip $(LOCAL_MODULE_CLASS)),) +LOCAL_MODULE_CLASS := EXECUTABLES +endif +ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) +LOCAL_MODULE_SUFFIX := $(TARGET_EXECUTABLE_SUFFIX) +endif + +ifdef target-executable-hook +$(call target-executable-hook) +endif + +skip_build_from_source := +ifdef LOCAL_PREBUILT_MODULE_FILE +ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) +include $(BUILD_SYSTEM)/prebuilt_internal.mk +skip_build_from_source := true +endif +endif + +ifndef skip_build_from_source + +include $(BUILD_SYSTEM)/dynamic_binary.mk + +# Check for statically linked libc +ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) +ifneq ($(filter $(my_static_libraries),libc),) +$(error $(LOCAL_PATH): $(LOCAL_MODULE) is statically linking libc to dynamic executable, please remove libc from static libs or set LOCAL_FORCE_STATIC_EXECUTABLE := true) +endif +endif + +# Define PRIVATE_ variables from global vars +ifeq ($(LOCAL_NO_LIBCRT_BUILTINS),true) +my_target_libcrt_builtins := +else +my_target_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS) +endif +ifeq ($(LOCAL_NO_CRT),true) +my_target_crtbegin_dynamic_o := +my_target_crtbegin_static_o := +my_target_crtend_o := +else ifdef LOCAL_USE_VNDK +my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.vendor) +my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.vendor) +my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.vendor) +else +my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic) +my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static) +my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android) +endif +ifneq ($(LOCAL_SDK_VERSION),) +my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.sdk.$(my_ndk_crt_version)) +my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.sdk.$(my_ndk_crt_version)) +my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.sdk.$(my_ndk_crt_version)) +endif +$(linked_module): PRIVATE_TARGET_LIBCRT_BUILTINS := $(my_target_libcrt_builtins) +$(linked_module): PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O := $(my_target_crtbegin_dynamic_o) +$(linked_module): PRIVATE_TARGET_CRTBEGIN_STATIC_O := $(my_target_crtbegin_static_o) +$(linked_module): PRIVATE_TARGET_CRTEND_O := $(my_target_crtend_o) +$(linked_module): PRIVATE_POST_LINK_CMD := $(LOCAL_POST_LINK_CMD) + +ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) +$(linked_module): $(my_target_crtbegin_static_o) $(all_objects) $(all_libraries) $(my_target_crtend_o) $(my_target_libcrt_builtins) $(CLANG_CXX) + $(transform-o-to-static-executable) + $(PRIVATE_POST_LINK_CMD) +else +$(linked_module): $(my_target_crtbegin_dynamic_o) $(all_objects) $(all_libraries) $(my_target_crtend_o) $(my_target_libcrt_builtins) $(CLANG_CXX) + $(transform-o-to-executable) + $(PRIVATE_POST_LINK_CMD) +endif + +ifeq ($(my_native_coverage),true) +gcno_suffix := .zip + +built_whole_gcno_libraries := \ + $(foreach lib,$(my_whole_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ + $(my_host_cross))/$(lib)$(gcno_suffix)) + +built_static_gcno_libraries := \ + $(foreach lib,$(my_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ + $(my_host_cross))/$(lib)$(gcno_suffix)) + +ifdef LOCAL_IS_HOST_MODULE +my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) +else +my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) +endif + +GCNO_ARCHIVE := $(my_installed_module_stem)$(gcno_suffix) + +$(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES)) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(strip $(built_static_gcno_libraries)) +$(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(built_static_gcno_libraries) + $(package-coverage-files) + +$(my_coverage_path)/$(GCNO_ARCHIVE) : $(intermediates)/$(GCNO_ARCHIVE) + $(copy-file-to-target) + +$(LOCAL_BUILT_MODULE): $(my_coverage_path)/$(GCNO_ARCHIVE) +endif + +endif # skip_build_from_source diff --git a/make/core/executable_prefer_symlink.mk b/make/core/executable_prefer_symlink.mk new file mode 100644 index 0000000..fea0bef --- /dev/null +++ b/make/core/executable_prefer_symlink.mk @@ -0,0 +1,39 @@ +# include this makefile to create the LOCAL_MODULE symlink to the primary version binary. +# but this requires the primary version name specified via LOCAL_MODULE_STEM_32 or LOCAL_MODULE_STEM_64, +# and different with the LOCAL_MODULE value +# +# Note: now only limited to the binaries that will be installed under system/bin directory + +# Create link to the one used depending on the target +# configuration. +ifneq ($(LOCAL_IS_HOST_MODULE),true) + my_symlink := $(addprefix $(TARGET_OUT)/bin/, $(LOCAL_MODULE)) + my_src_binary_name := + ifeq ($(TARGET_IS_64_BIT),true) + ifeq ($(TARGET_SUPPORTS_64_BIT_APPS)|$(TARGET_SUPPORTS_32_BIT_APPS),true|true) + my_src_binary_name := $(LOCAL_MODULE_STEM_64) + else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true) + # We support only 64 bit apps. + my_src_binary_name := $(LOCAL_MODULE_STEM_64) + else + # We support only 32 bit apps. + my_src_binary_name := $(LOCAL_MODULE_STEM_32) + endif + else + my_src_binary_name := $(LOCAL_MODULE_STEM_32) + endif +else + my_symlink := $(addprefix $(HOST_OUT)/bin/, $(LOCAL_MODULE)) + my_src_binary_name := $(LOCAL_MODULE_STEM_64) +endif + +$(call symlink-file,$(my_module_path)/$(my_src_binary_name),$(my_src_binary_name),$(my_symlink)) + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(my_register_name).INSTALLED += $(my_symlink) + +# Create the symlink when you run mm/mmm or "make " +$(LOCAL_MODULE) : $(my_symlink) + +my_symlink := diff --git a/make/core/filter_symbols.sh b/make/core/filter_symbols.sh new file mode 100644 index 0000000..ba5057a --- /dev/null +++ b/make/core/filter_symbols.sh @@ -0,0 +1,25 @@ +NM=$1 + +shift + +PREFIX=$1 + +shift + +SUFFIX=$1 + +shift + +while test "$1" != "" +do + $NM -g -fp $1 | while read -a line + do + type=${line[1]} + # if [[ "$type" != "V" && "$type" != "U" ]]; then + #if [[ "$type" != "W" && "$type" != "V" && "$type" != "U" ]]; then + echo "$PREFIX${line[0]}$SUFFIX # ${line[1]}" + #fi + done + + shift +done diff --git a/make/core/force_aapt2.mk b/make/core/force_aapt2.mk new file mode 100644 index 0000000..5f3182a --- /dev/null +++ b/make/core/force_aapt2.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Including this makefile will force AAPT2 on, +# rewriting some properties to convert standard AAPT usage to AAPT2. + +ifeq ($(LOCAL_USE_AAPT2),false) + $(call pretty-error, LOCAL_USE_AAPT2 := false is no longer supported) +endif + +# Filter out support library resources +LOCAL_RESOURCE_DIR := $(filter-out \ + prebuilts/sdk/current/% \ + frameworks/support/%,\ + $(LOCAL_RESOURCE_DIR)) +# Filter out unnecessary aapt flags +ifneq (,$(filter --extra-packages,$(LOCAL_AAPT_FLAGS))) + LOCAL_AAPT_FLAGS := $(subst --extra-packages=,--extra-packages$(space), \ + $(filter-out \ + --extra-packages=android.support.% \ + --extra-packages=androidx.%, \ + $(subst --extra-packages$(space),--extra-packages=,$(LOCAL_AAPT_FLAGS)))) + ifeq (,$(filter --extra-packages,$(LOCAL_AAPT_FLAGS))) + LOCAL_AAPT_FLAGS := $(filter-out --auto-add-overlay,$(LOCAL_AAPT_FLAGS)) + endif +endif + +# AAPT2 is pickier about missing resources. Support library may have references to resources +# added in current, so always treat LOCAL_SDK_VERSION := as LOCAL_SDK_RES_VERSION := current. +ifneq (,$(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION))) + LOCAL_SDK_RES_VERSION := current +endif + diff --git a/make/core/fuzz_test.mk b/make/core/fuzz_test.mk new file mode 100644 index 0000000..8a4b8c3 --- /dev/null +++ b/make/core/fuzz_test.mk @@ -0,0 +1,45 @@ +########################################### +## A thin wrapper around BUILD_EXECUTABLE +## Common flags for fuzz tests are added. +########################################### +$(call record-module-type,FUZZ_TEST) + +ifdef LOCAL_SDK_VERSION + $(error $(LOCAL_PATH): $(LOCAL_MODULE): NDK fuzz tests are not supported.) +endif + +my_fuzzer:=libFuzzer +ifdef LOCAL_FUZZ_ENGINE + my_fuzzer:=$(LOCAL_FUZZ_ENGINE) +else ifdef TARGET_FUZZ_ENGINE + my_fuzzer:=$(TARGET_FUZZ_ENGINE) +endif + +LOCAL_SANITIZE += fuzzer + +ifeq ($(my_fuzzer),libFuzzer) +LOCAL_STATIC_LIBRARIES += libFuzzer +else +$(call pretty-error, Unknown fuzz engine $(my_fuzzer)) +endif + +ifdef LOCAL_MODULE_PATH +$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH when building test $(LOCAL_MODULE)) +endif + +ifdef LOCAL_MODULE_PATH_32 +$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_32 when building test $(LOCAL_MODULE)) +endif + +ifdef LOCAL_MODULE_PATH_64 +$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_64 when building test $(LOCAL_MODULE)) +endif + +LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/fuzzers/$(my_fuzzer)/$(LOCAL_MODULE) +LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/fuzzers/$(my_fuzzer)/$(LOCAL_MODULE) + +ifndef LOCAL_STRIP_MODULE +LOCAL_STRIP_MODULE := keep_symbols +endif + +include $(BUILD_EXECUTABLE) diff --git a/make/core/generate_enforce_rro.mk b/make/core/generate_enforce_rro.mk new file mode 100644 index 0000000..9079981 --- /dev/null +++ b/make/core/generate_enforce_rro.mk @@ -0,0 +1,63 @@ +include $(CLEAR_VARS) + +enforce_rro_module := $(enforce_rro_source_module)__auto_generated_rro_$(enforce_rro_partition) +LOCAL_PACKAGE_NAME := $(enforce_rro_module) + +intermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON) +rro_android_manifest_file := $(intermediates)/AndroidManifest.xml + +ifeq (true,$(enforce_rro_source_is_manifest_package_name)) + use_package_name_arg := --use-package-name +else + use_package_name_arg := +$(rro_android_manifest_file): $(enforce_rro_source_manifest_package_info) +endif + +$(rro_android_manifest_file): PRIVATE_PACKAGE_INFO := $(enforce_rro_source_manifest_package_info) +$(rro_android_manifest_file): PRIVATE_USE_PACKAGE_NAME := $(use_package_name_arg) +$(rro_android_manifest_file): PRIVATE_PARTITION := $(enforce_rro_partition) +# There should be no duplicate overrides, but just in case, set the priority of +# /product overlays to be higher than /vendor, to at least get deterministic results. +$(rro_android_manifest_file): PRIVATE_PRIORITY := $(if $(filter product,$(enforce_rro_partition)),1,0) +$(rro_android_manifest_file): build/make/tools/generate-enforce-rro-android-manifest.py + $(hide) build/make/tools/generate-enforce-rro-android-manifest.py \ + --package-info $(PRIVATE_PACKAGE_INFO) \ + $(PRIVATE_USE_PACKAGE_NAME) \ + --partition $(PRIVATE_PARTITION) \ + --priority $(PRIVATE_PRIORITY) \ + -o $@ + +LOCAL_PATH:= $(intermediates) + +# TODO(b/187404676): remove this condition when the prebuilt for packges exporting resource exists. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) +ifeq ($(enforce_rro_use_res_lib),true) + LOCAL_RES_LIBRARIES := $(enforce_rro_source_module) +endif +endif + +LOCAL_FULL_MANIFEST_FILE := $(rro_android_manifest_file) + +LOCAL_AAPT_FLAGS += --auto-add-overlay +LOCAL_RESOURCE_DIR := $(enforce_rro_source_overlays) + +ifeq (product,$(enforce_rro_partition)) + LOCAL_PRODUCT_MODULE := true +else ifeq (vendor,$(enforce_rro_partition)) + LOCAL_VENDOR_MODULE := true +else + $(error Unsupported partition. Want: [vendor/product] Got: [$(enforce_rro_partition)]) +endif +ifneq (,$(TARGET_BUILD_UNBUNDLED)) + LOCAL_SDK_VERSION := current +else ifneq (,$(LOCAL_RES_LIBRARIES)) + # Technically we are linking against the app (if only to grab its resources), + # and because it's potentially not building against the SDK, we can't either. + LOCAL_PRIVATE_PLATFORM_APIS := true +else ifeq (framework-res,$(enforce_rro_source_module)) + LOCAL_PRIVATE_PLATFORM_APIS := true +else + LOCAL_SDK_VERSION := current +endif + +include $(BUILD_RRO_PACKAGE) diff --git a/make/core/goma.mk b/make/core/goma.mk new file mode 100644 index 0000000..2b51d8b --- /dev/null +++ b/make/core/goma.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Notice: this works only with Google's Goma build infrastructure. +ifneq ($(filter-out false,$(USE_GOMA)),) + ifdef GOMA_DIR + goma_dir := $(GOMA_DIR) + else + goma_dir := $(HOME)/goma + endif + GOMA_CC := $(goma_dir)/gomacc + + # Append gomacc to existing *_WRAPPER variables so it's possible to + # use both ccache and gomacc. + CC_WRAPPER := $(strip $(CC_WRAPPER) $(GOMA_CC)) + CXX_WRAPPER := $(strip $(CXX_WRAPPER) $(GOMA_CC)) + # b/143658984: goma can't handle the --system argument to javac + #JAVAC_WRAPPER := $(strip $(JAVAC_WRAPPER) $(GOMA_CC)) + + goma_dir := +endif diff --git a/make/core/header_library.mk b/make/core/header_library.mk new file mode 100644 index 0000000..ee65111 --- /dev/null +++ b/make/core/header_library.mk @@ -0,0 +1,36 @@ +$(call record-module-type,HEADER_LIBRARY) +ifdef LOCAL_IS_HOST_MODULE + my_prefix := HOST_ + LOCAL_HOST_PREFIX := +else + my_prefix := TARGET_ +endif +include $(BUILD_SYSTEM)/multilib.mk + +ifndef my_module_multilib + # libraries default to building for both architecturess + my_module_multilib := both +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) + include $(BUILD_SYSTEM)/header_library_internal.mk +endif + +ifdef $(my_prefix)2ND_ARCH + LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX) + include $(BUILD_SYSTEM)/module_arch_supported.mk + + ifeq ($(my_module_arch_supported),true) + # Build for 2ND_ARCH + LOCAL_BUILT_MODULE := + LOCAL_INSTALLED_MODULE := + LOCAL_INTERMEDIATE_TARGETS := + include $(BUILD_SYSTEM)/header_library_internal.mk + endif + LOCAL_2ND_ARCH_VAR_PREFIX := +endif # 2ND_ARCH + +my_module_arch_supported := diff --git a/make/core/header_library_internal.mk b/make/core/header_library_internal.mk new file mode 100644 index 0000000..35ee1bc --- /dev/null +++ b/make/core/header_library_internal.mk @@ -0,0 +1,21 @@ +########################################################### +## Standard rules for building a header library. +## +## Additional inputs from base_rules.make: +## None. +########################################################### + +LOCAL_MODULE_CLASS := HEADER_LIBRARIES +LOCAL_UNINSTALLABLE_MODULE := true +ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) +$(error $(LOCAL_PATH): Cannot set module stem for a library) +endif + +include $(BUILD_SYSTEM)/binary.mk + +ifneq ($(strip $(all_objects)),) +$(call pretty-error,Header libraries may not have any sources) +endif + +$(LOCAL_BUILT_MODULE): + $(hide) touch $@ diff --git a/make/core/host_dalvik_java_library.mk b/make/core/host_dalvik_java_library.mk new file mode 100644 index 0000000..5eeb8ac --- /dev/null +++ b/make/core/host_dalvik_java_library.mk @@ -0,0 +1,191 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +$(call record-module-type,HOST_DALVIK_JAVA_LIBRARY) + +# +# Rules for building a host dalvik java library. These libraries +# are meant to be used by a dalvik VM instance running on the host. +# They will be compiled against libcore and not the host JRE. +# + +ifeq ($(HOST_OS),linux) +USE_CORE_LIB_BOOTCLASSPATH := true + +####################################### +include $(BUILD_SYSTEM)/host_java_library_common.mk +####################################### + +full_classes_turbine_jar := $(intermediates.COMMON)/classes-turbine.jar +full_classes_header_jarjar := $(intermediates.COMMON)/classes-header-jarjar.jar +full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar +full_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar +full_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar +full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar +full_classes_jar := $(intermediates.COMMON)/classes.jar +built_dex := $(intermediates.COMMON)/classes.dex +java_source_list_file := $(intermediates.COMMON)/java-source-list + +LOCAL_INTERMEDIATE_TARGETS += \ + $(full_classes_turbine_jar) \ + $(full_classes_compiled_jar) \ + $(full_classes_combined_jar) \ + $(full_classes_jarjar_jar) \ + $(full_classes_jar) \ + $(built_dex) \ + $(java_source_list_file) + +# See comment in java.mk +ifndef LOCAL_CHECKED_MODULE +ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true) +LOCAL_CHECKED_MODULE := $(full_classes_compiled_jar) +else +LOCAL_CHECKED_MODULE := $(built_dex) +endif +endif + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### +java_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) \ + $(filter %.java,$(LOCAL_GENERATED_SOURCES)) +all_java_sources := $(java_sources) + +include $(BUILD_SYSTEM)/java_common.mk + +include $(BUILD_SYSTEM)/sdk_check.mk + +$(cleantarget): PRIVATE_CLEAN_FILES += $(intermediates.COMMON) + +# List of dependencies for anything that needs all java sources in place +java_sources_deps := \ + $(java_sources) \ + $(java_resource_sources) \ + $(LOCAL_SRCJARS) \ + $(LOCAL_ADDITIONAL_DEPENDENCIES) + +$(java_source_list_file): $(java_sources_deps) + $(write-java-source-list) + +# TODO(b/143658984): goma can't handle the --system argument to javac. +#$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL) +$(full_classes_compiled_jar): PRIVATE_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 := +$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES := +$(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) +$(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list +$(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars +$(full_classes_compiled_jar): \ + $(java_source_list_file) \ + $(java_sources_deps) \ + $(full_java_header_libs) \ + $(full_java_bootclasspath_libs) \ + $(full_java_system_modules_deps) \ + $(annotation_processor_deps) \ + $(NORMALIZE_PATH) \ + $(JAR_ARGS) \ + $(ZIPSYNC) \ + $(SOONG_ZIP) \ + | $(SOONG_JAVAC_WRAPPER) + $(transform-host-java-to-dalvik-package) + +ifneq ($(TURBINE_ENABLED),false) + +$(full_classes_turbine_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags) +$(full_classes_turbine_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) +$(full_classes_turbine_jar): \ + $(java_source_list_file) \ + $(java_sources_deps) \ + $(full_java_header_libs) \ + $(full_java_bootclasspath_libs) \ + $(NORMALIZE_PATH) \ + $(JAR_ARGS) \ + $(ZIPTIME) \ + | $(TURBINE) \ + $(MERGE_ZIPS) + $(transform-java-to-header.jar) + +.KATI_RESTAT: $(full_classes_turbine_jar) + +# Run jarjar before generate classes-header.jar if necessary. +ifneq ($(strip $(LOCAL_JARJAR_RULES)),) +$(full_classes_header_jarjar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) +$(full_classes_header_jarjar): $(full_classes_turbine_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) + $(call transform-jarjar) +else +full_classes_header_jarjar := $(full_classes_turbine_jar) +endif + +$(eval $(call copy-one-file,$(full_classes_header_jarjar),$(full_classes_header_jar))) + +endif # TURBINE_ENABLED != false + +$(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF) +$(full_classes_combined_jar): $(full_classes_compiled_jar) \ + $(jar_manifest_file) \ + $(full_static_java_libs) | $(MERGE_ZIPS) + $(if $(PRIVATE_JAR_MANIFEST), $(hide) sed -e "s/%BUILD_NUMBER%/$(BUILD_NUMBER_FROM_FILE)/" \ + $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf) + $(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(dir $@)/manifest.mf) \ + $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \ + $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES) + +# Run jarjar if necessary, otherwise just copy the file. +ifneq ($(strip $(LOCAL_JARJAR_RULES)),) +$(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) +$(full_classes_jarjar_jar): $(full_classes_combined_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) + $(call transform-jarjar) +else +full_classes_jarjar_jar := $(full_classes_combined_jar) +endif + +$(eval $(call copy-one-file,$(full_classes_jarjar_jar),$(full_classes_jar))) + +ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true) +# No dex; all we want are the .class files with resources. +$(LOCAL_BUILT_MODULE) : $(java_resource_sources) +$(LOCAL_BUILT_MODULE) : $(full_classes_jar) + @echo "host Static Jar: $(PRIVATE_MODULE) ($@)" + $(copy-file-to-target) + +else # !LOCAL_IS_STATIC_JAVA_LIBRARY +$(built_dex): PRIVATE_INTERMEDIATES_DIR := $(intermediates.COMMON) +$(built_dex): PRIVATE_DX_FLAGS := $(LOCAL_DX_FLAGS) +$(built_dex): $(full_classes_jar) $(DX) $(ZIP2ZIP) + $(transform-classes.jar-to-dex) + +$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(built_dex) +$(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE := $(full_classes_jarjar_jar) +$(LOCAL_BUILT_MODULE): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP) +$(LOCAL_BUILT_MODULE): $(built_dex) $(java_resource_sources) + @echo "Host Jar: $(PRIVATE_MODULE) ($@)" + rm -rf $@.parts + mkdir -p $@.parts + $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) + $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) + $(MERGE_ZIPS) -j $@ $@.parts/dex.zip $@.parts/res.zip + rm -rf $@.parts + +endif # !LOCAL_IS_STATIC_JAVA_LIBRARY + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEFAULT_APP_TARGET_SDK := $(call module-target-sdk-version) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SDK_VERSION := $(call module-sdk-version) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MIN_SDK_VERSION := $(call codename-or-sdk-to-sdk,$(call module-min-sdk-version)) + +USE_CORE_LIB_BOOTCLASSPATH := + +endif diff --git a/make/core/host_dalvik_static_java_library.mk b/make/core/host_dalvik_static_java_library.mk new file mode 100644 index 0000000..78faf73 --- /dev/null +++ b/make/core/host_dalvik_static_java_library.mk @@ -0,0 +1,28 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +$(call record-module-type,HOST_DALVIK_STATIC_JAVA_LIBRARY) + +# +# Rules for building a host dalvik static java library. +# These libraries will be compiled against libcore and not the host +# JRE. +# +LOCAL_UNINSTALLABLE_MODULE := true +LOCAL_IS_STATIC_JAVA_LIBRARY := true + +include $(BUILD_SYSTEM)/host_dalvik_java_library.mk + +LOCAL_IS_STATIC_JAVA_LIBRARY := diff --git a/make/core/host_executable.mk b/make/core/host_executable.mk new file mode 100644 index 0000000..8d1026c --- /dev/null +++ b/make/core/host_executable.mk @@ -0,0 +1,49 @@ +$(call record-module-type,HOST_EXECUTABLE) +LOCAL_IS_HOST_MODULE := true +my_prefix := HOST_ +LOCAL_HOST_PREFIX := +include $(BUILD_SYSTEM)/multilib.mk + +ifndef LOCAL_MODULE_HOST_ARCH +ifndef my_module_multilib +# By default we only build host module for the first arch. +my_module_multilib := first +endif +endif + +ifeq ($(my_module_multilib),both) +ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) +ifeq ($(LOCAL_MODULE_PATH_32)$(LOCAL_MODULE_STEM_32),) +$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_32 or LOCAL_MODULE_PATH_32 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) +endif +ifeq ($(LOCAL_MODULE_PATH_64)$(LOCAL_MODULE_STEM_64),) +$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_64 or LOCAL_MODULE_PATH_64 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) +endif +endif +else #!LOCAL_MULTILIB == both +LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +include $(BUILD_SYSTEM)/host_executable_internal.mk +endif + +ifdef HOST_2ND_ARCH +LOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# Build for HOST_2ND_ARCH +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := + +include $(BUILD_SYSTEM)/host_executable_internal.mk +endif +LOCAL_2ND_ARCH_VAR_PREFIX := +endif # HOST_2ND_ARCH + +LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := +my_module_arch_supported := diff --git a/make/core/host_executable_internal.mk b/make/core/host_executable_internal.mk new file mode 100644 index 0000000..0cf62a4 --- /dev/null +++ b/make/core/host_executable_internal.mk @@ -0,0 +1,45 @@ +########################################################### +## Standard rules for building an executable file. +## +## Additional inputs from base_rules.make: +## None. +########################################################### + +ifeq ($(strip $(LOCAL_MODULE_CLASS)),) +LOCAL_MODULE_CLASS := EXECUTABLES +endif +ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) +LOCAL_MODULE_SUFFIX := $($(my_prefix)EXECUTABLE_SUFFIX) +endif + +ifdef host-executable-hook +$(call host-executable-hook) +endif + +skip_build_from_source := +ifdef LOCAL_PREBUILT_MODULE_FILE +ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) +include $(BUILD_SYSTEM)/prebuilt_internal.mk +skip_build_from_source := true +endif +endif + +ifndef skip_build_from_source + +include $(BUILD_SYSTEM)/binary.mk + +my_host_libprofile_rt := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT) +$(LOCAL_BUILT_MODULE): PRIVATE_HOST_LIBPROFILE_RT := $(my_host_libprofile_rt) + +my_libdir := $(notdir $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_SHARED_LIBRARIES)) +ifeq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) +$(LOCAL_BUILT_MODULE): PRIVATE_RPATHS := ../../$(my_libdir) ../../../$(my_libdir) +else +$(LOCAL_BUILT_MODULE): PRIVATE_RPATHS := ../$(my_libdir) $(my_libdir) +endif +my_libdir := + +$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries) $(CLANG_CXX) + $(transform-host-o-to-executable) + +endif # skip_build_from_source diff --git a/make/core/host_java_library.mk b/make/core/host_java_library.mk new file mode 100644 index 0000000..0f95202 --- /dev/null +++ b/make/core/host_java_library.mk @@ -0,0 +1,133 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call record-module-type,HOST_JAVA_LIBRARY) + +# +# Standard rules for building a host java library. +# + +####################################### +include $(BUILD_SYSTEM)/host_java_library_common.mk +####################################### + +# Enable emma instrumentation only if the module asks so. +ifeq (true,$(LOCAL_EMMA_INSTRUMENT)) +ifneq (true,$(EMMA_INSTRUMENT)) +LOCAL_EMMA_INSTRUMENT := +endif +endif + +full_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar +full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar +full_classes_jar := $(intermediates.COMMON)/classes.jar +java_source_list_file := $(intermediates.COMMON)/java-source-list +full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar +full_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar + +LOCAL_INTERMEDIATE_TARGETS += \ + $(full_classes_compiled_jar) \ + $(full_classes_jarjar_jar) \ + $(java_source_list_file) \ + $(full_classes_combined_jar) + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +java_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) \ + $(filter %.java,$(LOCAL_GENERATED_SOURCES)) +all_java_sources := $(java_sources) + +ALL_MODULES.$(my_register_name).SRCS := $(ALL_MODULES.$(my_register_name).SRCS) $(all_java_sources) + +include $(BUILD_SYSTEM)/java_common.mk + +# 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) \ + $(java_resource_sources) \ + $(LOCAL_SRCJARS) \ + $(LOCAL_ADDITIONAL_DEPENDENCIES) + +$(java_source_list_file): $(java_sources_deps) + $(write-java-source-list) + +# TODO(b/143658984): goma can't handle the --system argument to javac. +#$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL) +$(full_classes_compiled_jar): PRIVATE_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 := +$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES := +$(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) +$(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list +$(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars +$(full_classes_compiled_jar): \ + $(java_source_list_file) \ + $(java_sources_deps) \ + $(full_java_libs) \ + $(full_java_bootclasspath_libs) \ + $(annotation_processor_deps) \ + $(NORMALIZE_PATH) \ + $(ZIPTIME) \ + $(JAR_ARGS) \ + $(ZIPSYNC) \ + $(SOONG_ZIP) \ + | $(SOONG_JAVAC_WRAPPER) + $(transform-host-java-to-package) + $(remove-timestamps-from-package) + +javac-check : $(full_classes_compiled_jar) +javac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar) +.PHONY: javac-check-$(LOCAL_MODULE) + +$(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF) +$(full_classes_combined_jar): $(full_classes_compiled_jar) \ + $(jar_manifest_file) \ + $(full_static_java_libs) | $(MERGE_ZIPS) + $(if $(PRIVATE_JAR_MANIFEST), $(hide) sed -e "s/%BUILD_NUMBER%/$(BUILD_NUMBER_FROM_FILE)/" \ + $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf) + $(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(dir $@)/manifest.mf) \ + $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \ + $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES) + +# Run jarjar if necessary, otherwise just copy the file. +ifneq ($(strip $(LOCAL_JARJAR_RULES)),) +$(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) +$(full_classes_jarjar_jar): $(full_classes_combined_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) + $(call transform-jarjar) +else +full_classes_jarjar_jar := $(full_classes_combined_jar) +endif + + +####################################### +LOCAL_FULL_CLASSES_PRE_JACOCO_JAR := $(full_classes_jarjar_jar) + +include $(BUILD_SYSTEM)/jacoco.mk +####################################### + +$(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(LOCAL_BUILT_MODULE))) +$(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_jar))) + +ifeq ($(TURBINE_ENABLED),false) +$(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_header_jar))) +endif diff --git a/make/core/host_java_library_common.mk b/make/core/host_java_library_common.mk new file mode 100644 index 0000000..0e62f60 --- /dev/null +++ b/make/core/host_java_library_common.mk @@ -0,0 +1,50 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Common rules for building a host java library. +# + +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX) +LOCAL_IS_HOST_MODULE := true +LOCAL_BUILT_MODULE_STEM := javalib.jar + +intermediates := $(call local-intermediates-dir) +intermediates.COMMON := $(call local-intermediates-dir,COMMON) + +# base_rules.mk looks at this +all_res_assets := + +proto_sources := $(filter %.proto,$(LOCAL_SRC_FILES)) +ifneq ($(proto_sources),) +ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro) + LOCAL_JAVA_LIBRARIES += libprotobuf-java-micro +else + ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano) + LOCAL_JAVA_LIBRARIES += libprotobuf-java-nano + else + ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),full) + LOCAL_JAVA_LIBRARIES += libprotobuf-java-full + else + LOCAL_JAVA_LIBRARIES += libprotobuf-java-lite + endif + endif +endif +endif + +LOCAL_INTERMEDIATE_SOURCE_DIR := $(intermediates.COMMON)/src +LOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES)) diff --git a/make/core/host_prebuilt.mk b/make/core/host_prebuilt.mk new file mode 100644 index 0000000..79f3ffa --- /dev/null +++ b/make/core/host_prebuilt.mk @@ -0,0 +1,19 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call record-module-type,HOST_PREBUILT) +LOCAL_IS_HOST_MODULE := true +include $(BUILD_MULTI_PREBUILT) diff --git a/make/core/host_shared_library.mk b/make/core/host_shared_library.mk new file mode 100644 index 0000000..fbe6442 --- /dev/null +++ b/make/core/host_shared_library.mk @@ -0,0 +1,45 @@ +$(call record-module-type,HOST_SHARED_LIBRARY) +LOCAL_IS_HOST_MODULE := true +my_prefix := HOST_ +LOCAL_HOST_PREFIX := +include $(BUILD_SYSTEM)/multilib.mk + +ifndef LOCAL_MODULE_HOST_ARCH +ifndef my_module_multilib +# libraries default to building for both architecturess +my_module_multilib := both +endif +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +include $(BUILD_SYSTEM)/host_shared_library_internal.mk +endif + +ifdef HOST_2ND_ARCH +LOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# Build for HOST_2ND_ARCH +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := + +include $(BUILD_SYSTEM)/host_shared_library_internal.mk +endif +LOCAL_2ND_ARCH_VAR_PREFIX := +endif # HOST_2ND_ARCH + +my_module_arch_supported := + +########################################################### +## Copy headers to the install tree +########################################################### +ifdef LOCAL_COPY_HEADERS +$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ + $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ + $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) +include $(BUILD_SYSTEM)/copy_headers.mk +endif diff --git a/make/core/host_shared_library_internal.mk b/make/core/host_shared_library_internal.mk new file mode 100644 index 0000000..da20874 --- /dev/null +++ b/make/core/host_shared_library_internal.mk @@ -0,0 +1,45 @@ +########################################################### +## Standard rules for building a normal shared library. +## +## Additional inputs from base_rules.make: +## None. +## +## LOCAL_MODULE_SUFFIX will be set for you. +########################################################### + +ifeq ($(strip $(LOCAL_MODULE_CLASS)),) +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +endif +ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) +LOCAL_MODULE_SUFFIX := $($(my_prefix)SHLIB_SUFFIX) +endif +ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) +$(error $(LOCAL_PATH): Cannot set module stem for a library) +endif + +ifdef host-shared-library-hook +$(call host-shared-library-hook) +endif + +skip_build_from_source := +ifdef LOCAL_PREBUILT_MODULE_FILE +ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) +include $(BUILD_SYSTEM)/prebuilt_internal.mk +skip_build_from_source := true +endif +endif + +ifndef skip_build_from_source + +include $(BUILD_SYSTEM)/binary.mk + +my_host_libprofile_rt := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT) +$(LOCAL_BUILT_MODULE): PRIVATE_HOST_LIBPROFILE_RT := $(my_host_libprofile_rt) + +$(LOCAL_BUILT_MODULE): \ + $(all_objects) \ + $(all_libraries) \ + $(LOCAL_ADDITIONAL_DEPENDENCIES) + $(transform-host-o-to-shared-lib) + +endif # skip_build_from_source diff --git a/make/core/host_static_library.mk b/make/core/host_static_library.mk new file mode 100644 index 0000000..23d809c --- /dev/null +++ b/make/core/host_static_library.mk @@ -0,0 +1,45 @@ +$(call record-module-type,HOST_STATIC_LIBRARY) +LOCAL_IS_HOST_MODULE := true +my_prefix := HOST_ +LOCAL_HOST_PREFIX := +include $(BUILD_SYSTEM)/multilib.mk + +ifndef LOCAL_MODULE_HOST_ARCH +ifndef my_module_multilib +# libraries default to building for both architecturess +my_module_multilib := both +endif +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +include $(BUILD_SYSTEM)/host_static_library_internal.mk +endif + +ifdef HOST_2ND_ARCH +LOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# Build for HOST_2ND_ARCH +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := + +include $(BUILD_SYSTEM)/host_static_library_internal.mk +endif +LOCAL_2ND_ARCH_VAR_PREFIX := +endif # HOST_2ND_ARCH + +my_module_arch_supported := + +########################################################### +## Copy headers to the install tree +########################################################### +ifdef LOCAL_COPY_HEADERS +$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ + $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ + $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) +include $(BUILD_SYSTEM)/copy_headers.mk +endif diff --git a/make/core/host_static_library_internal.mk b/make/core/host_static_library_internal.mk new file mode 100644 index 0000000..3946aa7 --- /dev/null +++ b/make/core/host_static_library_internal.mk @@ -0,0 +1,25 @@ +########################################################### +## Standard rules for building a static library for the host. +## +## Additional inputs from base_rules.make: +## None. +## +## LOCAL_MODULE_SUFFIX will be set for you. +########################################################### + +ifeq ($(strip $(LOCAL_MODULE_CLASS)),) +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +endif +ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) +LOCAL_MODULE_SUFFIX := .a +endif +ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) +$(error $(LOCAL_PATH): Cannot set module stem for a library) +endif +LOCAL_UNINSTALLABLE_MODULE := true + +include $(BUILD_SYSTEM)/binary.mk + +$(LOCAL_BUILT_MODULE): $(built_whole_libraries) +$(LOCAL_BUILT_MODULE): $(all_objects) + $(transform-host-o-to-static-lib) diff --git a/make/core/install_jni_libs.mk b/make/core/install_jni_libs.mk new file mode 100644 index 0000000..0fec9ca --- /dev/null +++ b/make/core/install_jni_libs.mk @@ -0,0 +1,108 @@ +# Decides how to install the jni libraries needed by an apk. +# Input variables: +# my_module_multilib, LOCAL_2ND_ARCH_VAR_PREFIX (from package.mk or prebuilt.mk) +# rs_compatibility_jni_libs (from java.mk) +# my_module_path (from base_rules.mk) +# partition_tag (from base_rules.mk) +# my_prebuilt_src_file (from prebuilt_internal.mk) +# +# Output variables: +# jni_shared_libraries, jni_shared_libraries_abi, jni_shared_libraries_with_abis if we are going to embed the libraries into the apk; +# embedded_prebuilt_jni_libs, prebuilt jni libs embedded in prebuilt apk. +# + +my_embed_jni := +ifneq ($(TARGET_BUILD_APPS),) + my_embed_jni := true +endif +ifneq ($(filter tests samples, $(LOCAL_MODULE_TAGS)),) + my_embed_jni := true +endif + +# If the APK is not installed in one of the following partitions, force its libraries +# to be embedded inside the APK instead of installed to //lib[64]/. +supported_partition_patterns := \ + $(TARGET_OUT)/% \ + $(TARGET_OUT_VENDOR)/% \ + $(TARGET_OUT_OEM)/% \ + $(TARGET_OUT_PRODUCT)/% \ + $(TARGET_OUT_SYSTEM_EXT)/% \ + +ifeq ($(filter $(supported_partition_patterns),$(my_module_path)),) + my_embed_jni := true +endif + +# If we're installing this APP as a compressed module, we include all JNI libraries +# in the compressed artifact, rather than as separate files on the partition in question. +ifdef LOCAL_COMPRESSED_MODULE + my_embed_jni := true +endif + +jni_shared_libraries := +jni_shared_libraries_abis := +# jni_shared_libraries_with_abis is a list of : +jni_shared_libraries_with_abis := +embedded_prebuilt_jni_libs := + +####################################### +# For TARGET_ARCH +my_2nd_arch_prefix := +my_add_jni := +# The module is built for TARGET_ARCH +ifeq ($(my_2nd_arch_prefix),$(LOCAL_2ND_ARCH_VAR_PREFIX)) + my_add_jni := true +endif +# Or it explicitly requires both +ifeq ($(my_module_multilib),both) + my_add_jni := true +endif +ifeq ($(my_add_jni),true) + my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS_$(TARGET_ARCH)) + ifndef my_prebuilt_jni_libs + my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS) + endif + include $(BUILD_SYSTEM)/install_jni_libs_internal.mk + jni_shared_libraries += $(my_jni_shared_libraries) + jni_shared_libraries_abis += $(my_jni_shared_libraries_abi) + jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\ + $(my_jni_shared_libraries)) + embedded_prebuilt_jni_libs += $(my_embedded_prebuilt_jni_libs) + + # Include RS dynamically-generated libraries as well + # TODO: Add multilib support once RS supports generating multilib libraries. + jni_shared_libraries += $(rs_compatibility_jni_libs) + jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\ + $(rs_compatibility_jni_libs)) +endif # my_add_jni + +####################################### +# For TARGET_2ND_ARCH +ifdef TARGET_2ND_ARCH + my_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) + my_add_jni := + # The module is built for TARGET_2ND_ARCH + ifeq ($(my_2nd_arch_prefix),$(LOCAL_2ND_ARCH_VAR_PREFIX)) + my_add_jni := true + endif + # Or it explicitly requires both + ifeq ($(my_module_multilib),both) + my_add_jni := true + endif + ifeq ($(my_add_jni),true) + my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS_$(TARGET_2ND_ARCH)) + ifndef my_prebuilt_jni_libs + my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS) + endif + include $(BUILD_SYSTEM)/install_jni_libs_internal.mk + jni_shared_libraries += $(my_jni_shared_libraries) + jni_shared_libraries_abis += $(my_jni_shared_libraries_abi) + jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\ + $(my_jni_shared_libraries)) + embedded_prebuilt_jni_libs += $(my_embedded_prebuilt_jni_libs) + endif # my_add_jni +endif # TARGET_2ND_ARCH + +jni_shared_libraries := $(strip $(jni_shared_libraries)) +jni_shared_libraries_abis := $(sort $(jni_shared_libraries_abis)) +jni_shared_libraries_with_abis := $(strip $(jni_shared_libraries_with_abis)) +embedded_prebuilt_jni_libs := $(strip $(embedded_prebuilt_jni_libs)) diff --git a/make/core/install_jni_libs_internal.mk b/make/core/install_jni_libs_internal.mk new file mode 100644 index 0000000..289d16f --- /dev/null +++ b/make/core/install_jni_libs_internal.mk @@ -0,0 +1,140 @@ +# Install jni libraries for one arch. +# Input variables: +# my_2nd_arch_prefix: indicate if this is for TARGET_2ND_ARCH. +# my_embed_jni: indicate if we want to embed the jni libs in the apk. +# my_prebuilt_jni_libs +# my_installed_module_stem (from configure_module_stem.mk) +# partition_tag (from base_rules.mk) +# my_prebuilt_src_file (from prebuilt_internal.mk) +# +# Output variables: +# my_jni_shared_libraries, my_jni_shared_libraries_abi, if we are going to embed the libraries into the apk; +# my_embedded_prebuilt_jni_libs, prebuilt jni libs embedded in prebuilt apk. +# + +my_sdk_variant = $(1) +ifneq (,$(and $(my_embed_jni),$(LOCAL_SDK_VERSION))) + # Soong produces $(lib).so in $(lib).sdk_intermediates so that the library + # has the correct name for embedding in an APK. Append .sdk to the name + # of the intermediates directory, but not the .so name. + my_sdk_variant = $(call use_soong_sdk_libraries,$(1)) +endif + +my_jni_shared_libraries := $(strip \ + $(foreach lib,$(LOCAL_JNI_SHARED_LIBRARIES), \ + $(call intermediates-dir-for,SHARED_LIBRARIES,$(call my_sdk_variant,$(lib)),,,$(my_2nd_arch_prefix))/$(lib).so)) + + +# App-specific lib path. +my_app_lib_path := $(dir $(LOCAL_INSTALLED_MODULE))lib/$(TARGET_$(my_2nd_arch_prefix)ARCH) +my_embedded_prebuilt_jni_libs := + +ifdef my_embed_jni + # App explicitly requires the prebuilt NDK stl shared libraies. + # The NDK stl shared libraries should never go to the system image. + ifeq ($(LOCAL_NDK_STL_VARIANT),c++_shared) + ifndef LOCAL_SDK_VERSION + $(error LOCAL_SDK_VERSION must be defined with LOCAL_NDK_STL_VARIANT, \ + LOCAL_PACKAGE_NAME=$(LOCAL_PACKAGE_NAME)) + endif + my_jni_shared_libraries += \ + $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources/cxx-stl/llvm-libc++/libs/$(TARGET_$(my_2nd_arch_prefix)CPU_ABI)/libc++_shared.so + endif + + # Set the abi directory used by the local JNI shared libraries. + # (Doesn't change how the local shared libraries are compiled, just + # sets where they are stored in the apk.) + ifeq ($(LOCAL_JNI_SHARED_LIBRARIES_ABI),) + my_jni_shared_libraries_abi := $(TARGET_$(my_2nd_arch_prefix)CPU_ABI) + else + my_jni_shared_libraries_abi := $(LOCAL_JNI_SHARED_LIBRARIES_ABI) + endif + +else ifneq ($(my_jni_shared_libraries),) # not my_embed_jni + + # The jni libaries will be installed to the system.img. + my_jni_filenames := $(notdir $(my_jni_shared_libraries)) + # Make sure the JNI libraries get installed + my_shared_library_path := $(call get_non_asan_path,\ + $($(my_2nd_arch_prefix)TARGET_OUT$(partition_tag)_SHARED_LIBRARIES)) + + bit_suffix := $(if $(filter %64,$(TARGET_$(my_2nd_arch_prefix)ARCH)),:64,:32) + ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += $(addsuffix $(bit_suffix),$(LOCAL_JNI_SHARED_LIBRARIES)) + + # Create symlink in the app specific lib path + # Skip creating this symlink when running the second part of a target sanitization build. + ifeq ($(filter address,$(SANITIZE_TARGET)),) + my_symlink_target_dir := $(patsubst $(PRODUCT_OUT)%,%,\ + $(my_shared_library_path)) + $(foreach lib,$(my_jni_filenames),\ + $(call symlink-file, \ + $(my_shared_library_path)/$(lib), \ + $(my_symlink_target_dir)/$(lib), \ + $(my_app_lib_path)/$(lib)) \ + $(eval $$(LOCAL_INSTALLED_MODULE) : $$(my_app_lib_path)/$$(lib)) \ + $(eval ALL_MODULES.$(my_register_name).INSTALLED += $$(my_app_lib_path)/$$(lib))) + endif + + # Clear jni_shared_libraries to not embed it into the apk. + my_jni_shared_libraries := +endif # my_embed_jni + +ifdef my_prebuilt_jni_libs + # Files like @lib//libfoo.so (path inside the apk) are JNI libs embedded prebuilt apk; + # Files like path/to/libfoo.so (path relative to LOCAL_PATH) are prebuilts in the source tree. + my_embedded_prebuilt_jni_libs := $(patsubst @%,%, \ + $(filter @%, $(my_prebuilt_jni_libs))) + + # prebuilt JNI exsiting as separate source files. + my_prebuilt_jni_libs := $(addprefix $(LOCAL_PATH)/, \ + $(filter-out @%, $(my_prebuilt_jni_libs))) + ifdef my_prebuilt_jni_libs + ifdef my_embed_jni + # Embed my_prebuilt_jni_libs to the apk + my_jni_shared_libraries += $(my_prebuilt_jni_libs) + else # not my_embed_jni + # Install my_prebuilt_jni_libs as separate files. + $(foreach lib, $(my_prebuilt_jni_libs), \ + $(eval $(call copy-one-file, $(lib), $(my_app_lib_path)/$(notdir $(lib))))) + + my_installed_library := $(addprefix $(my_app_lib_path)/, $(notdir $(my_prebuilt_jni_libs))) + $(LOCAL_INSTALLED_MODULE) : $(my_installed_library) + + ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed_library) + endif # my_embed_jni + endif # inner my_prebuilt_jni_libs +endif # outer my_prebuilt_jni_libs + +# Verify that all included libraries are built against the NDK +include $(BUILD_SYSTEM)/allowed_ndk_types.mk + +ifneq ($(strip $(LOCAL_JNI_SHARED_LIBRARIES)),) + ifneq ($(LOCAL_SDK_VERSION),) + my_link_type := app:sdk + my_warn_types := native:platform $(my_warn_ndk_types) + my_allowed_types := $(my_allowed_ndk_types) + ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) + my_allowed_types += native:vendor native:vndk native:platform_vndk + else ifeq ($(LOCAL_PRODUCT_MODULE),true) + my_allowed_types += native:product native:vndk native:platform_vndk + endif + else + my_link_type := app:platform + my_warn_types := $(my_warn_ndk_types) + my_allowed_types := $(my_allowed_ndk_types) native:platform native:product native:vendor native:vndk native:vndk_private native:platform_vndk + endif + + ifeq ($(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)) + # SOONG_SDK_VARIANT_MODULES isn't complete yet while parsing Soong modules, and Soong has + # already ensured that apps link against the correct SDK variants, don't check them. + else + ifneq (,$(LOCAL_SDK_VERSION)) + my_link_deps := $(addprefix SHARED_LIBRARIES:,$(call use_soong_sdk_libraries,$(LOCAL_JNI_SHARED_LIBRARIES))) + else + my_link_deps := $(addprefix SHARED_LIBRARIES:,$(LOCAL_JNI_SHARED_LIBRARIES)) + endif + endif + + my_common := + include $(BUILD_SYSTEM)/link_type.mk +endif diff --git a/make/core/instrumentation_test_config_template.xml b/make/core/instrumentation_test_config_template.xml new file mode 100644 index 0000000..6ca964e --- /dev/null +++ b/make/core/instrumentation_test_config_template.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/make/core/jacoco.mk b/make/core/jacoco.mk new file mode 100644 index 0000000..7099526 --- /dev/null +++ b/make/core/jacoco.mk @@ -0,0 +1,123 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This file sets up Java code coverage via Jacoco +# This file is only intended to be included internally by the build system +# (at the time of authorship, it is included by java.mk and +# java_host_library.mk) + +# determine Jacoco include/exclude filters even when coverage is not enabled +# to get syntax checking on LOCAL_JACK_COVERAGE_(INCLUDE|EXCLUDE)_FILTER +# copy filters from Jack but also skip some known java packages +my_include_filter := $(strip $(LOCAL_JACK_COVERAGE_INCLUDE_FILTER)) +my_exclude_filter := $(strip $(DEFAULT_JACOCO_EXCLUDE_FILTER),$(LOCAL_JACK_COVERAGE_EXCLUDE_FILTER)) + +my_include_args := $(call jacoco-class-filter-to-file-args, $(my_include_filter)) +my_exclude_args := $(call jacoco-class-filter-to-file-args, $(my_exclude_filter)) + +# single-quote each arg of the include args so the '*' gets evaluated by zip +# don't quote the exclude args they need to be evaluated by bash for rm -rf +my_include_args := $(foreach arg,$(my_include_args),'$(arg)') + +ifeq ($(LOCAL_EMMA_INSTRUMENT),true) + my_files := $(intermediates.COMMON)/jacoco + + # make a task that unzips the classes that we want to instrument from the + # input jar + my_unzipped_path := $(my_files)/work/classes-to-instrument/classes + my_unzipped_timestamp_path := $(my_files)/work/classes-to-instrument/updated.stamp +$(my_unzipped_timestamp_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path) +$(my_unzipped_timestamp_path): PRIVATE_UNZIPPED_TIMESTAMP_PATH := $(my_unzipped_timestamp_path) +$(my_unzipped_timestamp_path): PRIVATE_INCLUDE_ARGS := $(my_include_args) +$(my_unzipped_timestamp_path): PRIVATE_EXCLUDE_ARGS := $(my_exclude_args) +$(my_unzipped_timestamp_path): PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) +$(my_unzipped_timestamp_path): $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) + rm -rf $(PRIVATE_UNZIPPED_PATH) $@ + mkdir -p $(PRIVATE_UNZIPPED_PATH) + unzip -qDD $(PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR) \ + -d $(PRIVATE_UNZIPPED_PATH) \ + $(PRIVATE_INCLUDE_ARGS) + chmod -R =rwX $(PRIVATE_UNZIPPED_PATH) + (cd $(PRIVATE_UNZIPPED_PATH) && rm -rf $(PRIVATE_EXCLUDE_ARGS)) + (cd $(PRIVATE_UNZIPPED_PATH) && find -not -name "*.class" -type f -exec rm {} \;) + touch $(PRIVATE_UNZIPPED_TIMESTAMP_PATH) +# Unfortunately in the previous task above, +# 'rm -rf $(PRIVATE_EXCLUDE_ARGS)' needs to be a separate +# shell command after 'unzip'. +# We can't just use the '-x' (exclude) option of 'unzip' because if both +# inclusions and exclusions are specified and an exclusion matches no +# inclusions, then 'unzip' exits with an error (error 11). +# We could ignore the error, but that would make the process less reliable + + + # make a task that zips only the classes that will be instrumented + # (for passing in to the report generator later) + my_classes_to_report_on_path := $(my_files)/report-resources/jacoco-report-classes.jar +$(my_classes_to_report_on_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path) +$(my_classes_to_report_on_path): $(my_unzipped_timestamp_path) + rm -f $@ + zip -q $@ \ + -r $(PRIVATE_UNZIPPED_PATH) + +# Make a rule to copy the jacoco-report-classes.jar to a packaging directory. +$(eval $(call copy-one-file,$(my_classes_to_report_on_path),\ + $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)) +$(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar) + + # make a task that invokes instrumentation + my_instrumented_path := $(my_files)/work/instrumented/classes + my_instrumented_timestamp_path := $(my_files)/work/instrumented/updated.stamp +$(my_instrumented_timestamp_path): PRIVATE_INSTRUMENTED_PATH := $(my_instrumented_path) +$(my_instrumented_timestamp_path): PRIVATE_INSTRUMENTED_TIMESTAMP_PATH := $(my_instrumented_timestamp_path) +$(my_instrumented_timestamp_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path) +$(my_instrumented_timestamp_path): $(my_unzipped_timestamp_path) $(JACOCO_CLI_JAR) + rm -rf $(PRIVATE_INSTRUMENTED_PATH) + mkdir -p $(PRIVATE_INSTRUMENTED_PATH) + java -jar $(JACOCO_CLI_JAR) \ + instrument \ + --quiet \ + --dest '$(PRIVATE_INSTRUMENTED_PATH)' \ + $(PRIVATE_UNZIPPED_PATH) + touch $(PRIVATE_INSTRUMENTED_TIMESTAMP_PATH) + + + # make a task that zips both the instrumented classes and the uninstrumented + # classes (this jar is the instrumented application to execute) + my_temp_jar_path := $(my_files)/work/usable.jar + LOCAL_FULL_CLASSES_JACOCO_JAR := $(intermediates.COMMON)/classes-jacoco.jar +$(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_TEMP_JAR_PATH := $(my_temp_jar_path) +$(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_INSTRUMENTED_PATH := $(my_instrumented_path) +$(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) +$(LOCAL_FULL_CLASSES_JACOCO_JAR): $(JAR_ARGS) +$(LOCAL_FULL_CLASSES_JACOCO_JAR): $(my_instrumented_timestamp_path) $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) + rm -f $@ $(PRIVATE_TEMP_JAR_PATH) + # copy the pre-jacoco jar (containing files excluded from instrumentation) + cp $(PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR) $(PRIVATE_TEMP_JAR_PATH) + # copy instrumented files back into the resultant jar + $(JAR) -uf $(PRIVATE_TEMP_JAR_PATH) $(call jar-args-sorted-files-in-directory,$(PRIVATE_INSTRUMENTED_PATH)) + mv $(PRIVATE_TEMP_JAR_PATH) $@ + + # this is used to trigger $(my_classes_to_report_on_path) to build + # when $(LOCAL_FULL_CLASSES_JACOCO_JAR) builds, but it isn't truly a + # dependency. +$(LOCAL_FULL_CLASSES_JACOCO_JAR): $(my_classes_to_report_on_path) + +else # LOCAL_EMMA_INSTRUMENT != true + LOCAL_FULL_CLASSES_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) +endif # LOCAL_EMMA_INSTRUMENT == true + +LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_FULL_CLASSES_JACOCO_JAR) diff --git a/make/core/java.mk b/make/core/java.mk new file mode 100644 index 0000000..a29f820 --- /dev/null +++ b/make/core/java.mk @@ -0,0 +1,550 @@ +# Target Java. +# Requires: +# LOCAL_MODULE_SUFFIX +# LOCAL_MODULE_CLASS +# all_res_assets + +LOCAL_NO_STANDARD_LIBRARIES:=$(strip $(LOCAL_NO_STANDARD_LIBRARIES)) +LOCAL_SDK_VERSION:=$(strip $(LOCAL_SDK_VERSION)) + +proto_sources := $(filter %.proto,$(LOCAL_SRC_FILES)) +ifneq ($(proto_sources),) +ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro) + LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-micro +else + ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano) + LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-nano + else + ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),stream) + # No library for stream protobufs + else + LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-lite + endif + endif +endif +endif + +# LOCAL_STATIC_JAVA_AAR_LIBRARIES and LOCAL_STATIC_ANDROID_LIBRARIES are also LOCAL_STATIC_JAVA_LIBRARIES. +LOCAL_STATIC_JAVA_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_LIBRARIES) \ + $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) \ + $(LOCAL_STATIC_ANDROID_LIBRARIES)) +# LOCAL_SHARED_ANDROID_LIBRARIES are also LOCAL_JAVA_LIBRARIES. +LOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES) $(LOCAL_SHARED_ANDROID_LIBRARIES)) + +LOCAL_BUILT_MODULE_STEM := $(strip $(LOCAL_BUILT_MODULE_STEM)) +ifeq ($(LOCAL_BUILT_MODULE_STEM),) +$(error $(LOCAL_PATH): Target java template must define LOCAL_BUILT_MODULE_STEM) +endif +ifneq ($(filter classes-compiled.jar classes.jar,$(LOCAL_BUILT_MODULE_STEM)),) +$(error LOCAL_BUILT_MODULE_STEM may not be "$(LOCAL_BUILT_MODULE_STEM)") +endif + + +############################################################################## +# Define the intermediate targets before including base_rules so they get +# the correct environment. +############################################################################## + +intermediates := $(call local-intermediates-dir) +intermediates.COMMON := $(call local-intermediates-dir,COMMON) + +ifeq ($(LOCAL_PROGUARD_ENABLED),disabled) +LOCAL_PROGUARD_ENABLED := +endif + +full_classes_turbine_jar := $(intermediates.COMMON)/classes-turbine.jar +full_classes_header_jarjar := $(intermediates.COMMON)/classes-header-jarjar.jar +full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar +full_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar +full_classes_processed_jar := $(intermediates.COMMON)/classes-processed.jar +full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar +full_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar +built_dex_intermediate := $(intermediates.COMMON)/dex/classes.dex +full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar +java_source_list_file := $(intermediates.COMMON)/java-source-list + +ifeq ($(LOCAL_MODULE_CLASS)$(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),APPS) +# If this is an apk without any Java code (e.g. framework-res), we should skip compiling Java. +full_classes_jar := +built_dex := +else +full_classes_jar := $(intermediates.COMMON)/classes.jar +built_dex := $(intermediates.COMMON)/classes.dex +endif + +LOCAL_INTERMEDIATE_TARGETS += \ + $(full_classes_turbine_jar) \ + $(full_classes_compiled_jar) \ + $(full_classes_jarjar_jar) \ + $(full_classes_jar) \ + $(full_classes_combined_jar) \ + $(built_dex_intermediate) \ + $(built_dex) \ + $(full_classes_stubs_jar) \ + $(java_source_list_file) + +LOCAL_INTERMEDIATE_SOURCE_DIR := $(intermediates.COMMON)/src + +########################################################### +## AIDL: Compile .aidl files to .java +########################################################### +aidl_sources := $(filter %.aidl,$(LOCAL_SRC_FILES)) +aidl_java_sources := + +ifneq ($(strip $(aidl_sources)),) + +aidl_preprocess_import := +ifdef LOCAL_SDK_VERSION +ifneq ($(filter current system_current test_current core_current, $(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS)),) + # LOCAL_SDK_VERSION is current and no TARGET_BUILD_USE_PREBUILT_SDKS + aidl_preprocess_import := $(FRAMEWORK_AIDL) +else + aidl_preprocess_import := $(call resolve-prebuilt-sdk-aidl-path,$(LOCAL_SDK_VERSION)) +endif # not current or system_current +else +# build against the platform. +LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) +endif # LOCAL_SDK_VERSION + +$(foreach s,$(aidl_sources),\ + $(eval $(call define-aidl-java-rule,$(s),$(intermediates.COMMON)/aidl,aidl_java_sources))) +$(foreach java,$(aidl_java_sources), \ + $(call include-depfile,$(java:%.java=%.P),$(java))) + +$(aidl_java_sources) : $(LOCAL_ADDITIONAL_DEPENDENCIES) $(aidl_preprocess_import) + +$(aidl_java_sources): PRIVATE_AIDL_FLAGS := $(addprefix -p,$(aidl_preprocess_import)) -I$(LOCAL_PATH) -I$(LOCAL_PATH)/src $(addprefix -I,$(LOCAL_AIDL_INCLUDES)) +$(aidl_java_sources): PRIVATE_MODULE := $(LOCAL_MODULE) + +endif + +########################################## + +# All of the rules after full_classes_compiled_jar are very unlikely +# to fail except for bugs in their respective tools. If you would +# like to run these rules, add the "all" modifier goal to the make +# command line. +ifndef LOCAL_CHECKED_MODULE +ifdef full_classes_jar +LOCAL_CHECKED_MODULE := $(full_classes_compiled_jar) +endif +endif + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +########################################################### +## logtags: emit java source +########################################################### +ifneq ($(strip $(logtags_sources)),) + +logtags_java_sources := $(patsubst %.logtags,%.java,$(addprefix $(intermediates.COMMON)/logtags/, $(logtags_sources))) +logtags_sources := $(addprefix $(LOCAL_PATH)/, $(logtags_sources)) + +$(logtags_java_sources): PRIVATE_MERGED_TAG := $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt +$(logtags_java_sources): $(intermediates.COMMON)/logtags/%.java: $(LOCAL_PATH)/%.logtags $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt $(JAVATAGS) build/make/tools/event_log_tags.py + $(transform-logtags-to-java) + +else +logtags_java_sources := +endif + +########################################## +java_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) $(aidl_java_sources) $(logtags_java_sources) \ + $(filter %.java,$(LOCAL_GENERATED_SOURCES)) +java_intermediate_sources := $(addprefix $(TARGET_OUT_COMMON_INTERMEDIATES)/, $(filter %.java,$(LOCAL_INTERMEDIATE_SOURCES))) +all_java_sources := $(java_sources) $(java_intermediate_sources) +ALL_MODULES.$(my_register_name).SRCS := $(ALL_MODULES.$(my_register_name).SRCS) $(all_java_sources) + +include $(BUILD_SYSTEM)/java_common.mk + +include $(BUILD_SYSTEM)/sdk_check.mk + +# Set the profile source so that the odex / profile code included from java.mk +# can find it. +# +# TODO: b/64896089, this is broken when called from package_internal.mk, since the file +# we preopt from is a temporary file. This will be addressed in a follow up, possibly +# by disabling stripping for profile guided preopt (which may be desirable for other +# reasons anyway). +# +# Note that we set this only when called from package_internal.mk and not in other cases. +ifneq (,$(called_from_package_internal) +dex_preopt_profile_src_file := $(LOCAL_BUILT_MODULE) +endif + +####################################### +# defines built_odex along with rule to install odex +my_manifest_or_apk := $(full_android_manifest) +include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk +my_manifest_or_apk := +####################################### + +# Make sure there's something to build. +ifdef full_classes_jar +ifndef need_compile_java +$(call pretty-error,Target java module does not define any source or resource files) +endif +endif + +# Since we're using intermediates.COMMON, make sure that it gets cleaned +# properly. +$(cleantarget): PRIVATE_CLEAN_FILES += $(intermediates.COMMON) + +ifdef full_classes_jar + +# Droiddoc isn't currently able to generate stubs for modules, so we're just +# allowing it to use the classes.jar as the "stubs" that would be use to link +# against, for the cases where someone needs the jar to link against. +$(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_stubs_jar))) +ALL_MODULES.$(my_register_name).STUBS := $(full_classes_stubs_jar) + +# 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. +# This intentionally depends on java_sources, not all_java_sources. +# Deps for generated source files must be handled separately, +# via deps on the target that generates the sources. + +# For user / userdebug builds, strip the local variable table and the local variable +# type table. This has no bearing on stack traces, but will leave less information +# available via JDWP. +ifneq (,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO)) +ifneq (,$(filter userdebug user,$(TARGET_BUILD_VARIANT))) +LOCAL_JAVACFLAGS+= -g:source,lines +endif +endif + +# List of dependencies for anything that needs all java sources in place +java_sources_deps := \ + $(java_sources) \ + $(java_resource_sources) \ + $(LOCAL_SRCJARS) \ + $(LOCAL_ADDITIONAL_DEPENDENCIES) + +$(java_source_list_file): $(java_sources_deps) $(NORMALIZE_PATH) + $(write-java-source-list) + +ALL_MODULES.$(my_register_name).SRCJARS := $(LOCAL_SRCJARS) + +ifneq ($(TURBINE_ENABLED),false) + +$(full_classes_turbine_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags) +$(full_classes_turbine_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) +$(full_classes_turbine_jar): \ + $(java_source_list_file) \ + $(java_sources_deps) \ + $(full_java_header_libs) \ + $(full_java_bootclasspath_libs) \ + $(full_java_system_modules_deps) \ + $(NORMALIZE_PATH) \ + $(JAR_ARGS) \ + $(ZIPTIME) \ + | $(TURBINE) \ + $(MERGE_ZIPS) + $(transform-java-to-header.jar) + +.KATI_RESTAT: $(full_classes_turbine_jar) + +# Run jarjar before generate classes-header.jar if necessary. +ifneq ($(strip $(LOCAL_JARJAR_RULES)),) +$(full_classes_header_jarjar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) +$(full_classes_header_jarjar): $(full_classes_turbine_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) + $(call transform-jarjar) +else +full_classes_header_jarjar := $(full_classes_turbine_jar) +endif + +$(eval $(call copy-one-file,$(full_classes_header_jarjar),$(full_classes_header_jar))) + +endif # TURBINE_ENABLED != false + +# TODO(b/143658984): goma can't handle the --system argument to javac. +#$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL) +$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(JAVAC_NINJA_POOL) +$(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags) +$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES := $(LOCAL_JAR_EXCLUDE_FILES) +$(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES := $(LOCAL_JAR_PACKAGES) +$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES := $(LOCAL_JAR_EXCLUDE_PACKAGES) +$(full_classes_compiled_jar): PRIVATE_JAVA_SOURCE_LIST := $(java_source_list_file) +$(full_classes_compiled_jar): PRIVATE_ALL_JAVA_HEADER_LIBRARIES := $(full_java_header_libs) +$(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) +$(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list +$(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars +$(full_classes_compiled_jar): \ + $(java_source_list_file) \ + $(full_java_header_libs) \ + $(java_sources_deps) \ + $(full_java_bootclasspath_libs) \ + $(full_java_system_modules_deps) \ + $(layers_file) \ + $(annotation_processor_deps) \ + $(NORMALIZE_PATH) \ + $(JAR_ARGS) \ + $(ZIPSYNC) \ + $(SOONG_ZIP) \ + | $(SOONG_JAVAC_WRAPPER) + @echo "Target Java: $@ + $(call compile-java,$(TARGET_JAVAC),$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) + +javac-check : $(full_classes_compiled_jar) +javac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar) +.PHONY: javac-check-$(LOCAL_MODULE) + +$(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF) +$(full_classes_combined_jar): $(full_classes_compiled_jar) \ + $(jar_manifest_file) \ + $(full_static_java_libs) | $(MERGE_ZIPS) + $(if $(PRIVATE_JAR_MANIFEST), $(hide) sed -e "s/%BUILD_NUMBER%/$(BUILD_NUMBER_FROM_FILE)/" \ + $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf) + $(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(dir $@)/manifest.mf) \ + $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \ + $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES) + +ifdef LOCAL_JAR_PROCESSOR +# LOCAL_JAR_PROCESSOR_ARGS must be evaluated here to set up the rule-local +# PRIVATE_JAR_PROCESSOR_ARGS variable, but $< and $@ are not available yet. +# Set ${in} and ${out} so they can be referenced by LOCAL_JAR_PROCESSOR_ARGS +# using deferred evaluation (LOCAL_JAR_PROCESSOR_ARGS = instead of :=). +in := $(full_classes_combined_jar) +out := $(full_classes_processed_jar).tmp +my_jar_processor := $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_JAR_PROCESSOR).jar + +$(full_classes_processed_jar): PRIVATE_JAR_PROCESSOR_ARGS := $(LOCAL_JAR_PROCESSOR_ARGS) +$(full_classes_processed_jar): PRIVATE_JAR_PROCESSOR := $(my_jar_processor) +$(full_classes_processed_jar): PRIVATE_TMP_OUT := $(out) +in := +out := + +$(full_classes_processed_jar): $(full_classes_combined_jar) $(my_jar_processor) + @echo Processing $@ with $(PRIVATE_JAR_PROCESSOR) + $(hide) rm -f $@ $(PRIVATE_TMP_OUT) + $(hide) $(JAVA) -jar $(PRIVATE_JAR_PROCESSOR) $(PRIVATE_JAR_PROCESSOR_ARGS) + $(hide) mv $(PRIVATE_TMP_OUT) $@ + +my_jar_processor := +else +full_classes_processed_jar := $(full_classes_combined_jar) +endif + +# Run jarjar if necessary +ifneq ($(strip $(LOCAL_JARJAR_RULES)),) +$(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) +$(full_classes_jarjar_jar): $(full_classes_processed_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) + $(call transform-jarjar) +else +full_classes_jarjar_jar := $(full_classes_processed_jar) +endif + +$(eval $(call copy-one-file,$(full_classes_jarjar_jar),$(full_classes_jar))) + +####################################### +LOCAL_FULL_CLASSES_PRE_JACOCO_JAR := $(full_classes_jar) + +include $(BUILD_SYSTEM)/jacoco.mk +####################################### + +# Temporarily enable --multi-dex until proguard supports v53 class files +# ( http://b/67673860 ) or we move away from proguard altogether. +LOCAL_DX_FLAGS := $(filter-out --multi-dex,$(LOCAL_DX_FLAGS)) --multi-dex + +full_classes_pre_proguard_jar := $(LOCAL_FULL_CLASSES_JACOCO_JAR) + +# Keep a copy of the jar just before proguard processing. +$(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(intermediates.COMMON)/classes-pre-proguard.jar)) + +# Run proguard if necessary +ifdef LOCAL_PROGUARD_ENABLED +ifneq ($(filter-out full custom obfuscation optimization,$(LOCAL_PROGUARD_ENABLED)),) + $(warning while processing: $(LOCAL_MODULE)) + $(error invalid value for LOCAL_PROGUARD_ENABLED: $(LOCAL_PROGUARD_ENABLED)) +endif +proguard_dictionary := $(intermediates.COMMON)/proguard_dictionary +proguard_configuration := $(intermediates.COMMON)/proguard_configuration + +# When an app contains references to APIs that are not in the SDK specified by +# its LOCAL_SDK_VERSION for example added by support library or by runtime +# classes added by desugar, we artifically raise the "SDK version" "linked" by +# ProGuard, to +# - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version. +# - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version. +# See b/20667396 +my_proguard_sdk_raise := +ifdef LOCAL_SDK_VERSION +ifdef TARGET_BUILD_APPS +ifeq (,$(filter current system_current test_current core_current, $(LOCAL_SDK_VERSION))) + my_proguard_sdk_raise := $(call java-lib-header-files, $(call resolve-prebuilt-sdk-module,current)) +endif +else + # For platform build, we can't just raise to the "current" SDK, + # that would break apps that use APIs removed from the current SDK. + my_proguard_sdk_raise := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES)) +endif +ifdef BOARD_SYSTEMSDK_VERSIONS +ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) + # But for vendor or odm apks, don't raise SDK as the apks are required to + # use SDK APIs only + my_proguard_sdk_raise := +endif +endif +endif + +legacy_proguard_flags := $(addprefix -libraryjars ,$(my_proguard_sdk_raise) \ + $(filter-out $(my_proguard_sdk_raise), \ + $(full_java_bootclasspath_libs) \ + $(full_shared_java_header_libs))) + +legacy_proguard_lib_deps := $(my_proguard_sdk_raise) \ + $(filter-out $(my_proguard_sdk_raise),$(full_java_bootclasspath_libs) $(full_shared_java_header_libs)) + +legacy_proguard_flags += -printmapping $(proguard_dictionary) +legacy_proguard_flags += -printconfiguration $(proguard_configuration) + +common_proguard_flags := +common_proguard_flag_files := $(BUILD_SYSTEM)/proguard.flags +ifneq ($(LOCAL_INSTRUMENTATION_FOR)$(filter tests,$(LOCAL_MODULE_TAGS)),) +common_proguard_flags += -dontshrink # don't shrink tests by default +endif # test package +ifneq ($(LOCAL_PROGUARD_ENABLED),custom) + common_proguard_flag_files += $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES),\ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags) +endif +ifneq ($(common_proguard_flag_files),) +common_proguard_flags += $(addprefix -include , $(common_proguard_flag_files)) +# This is included from $(BUILD_SYSTEM)/proguard.flags +common_proguard_flag_files += $(BUILD_SYSTEM)/proguard_basic_keeps.flags +endif + +ifeq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),) +# By default no obfuscation +common_proguard_flags += -dontobfuscate +endif # No obfuscation +ifeq ($(filter optimization,$(LOCAL_PROGUARD_ENABLED)),) +# By default no optimization +common_proguard_flags += -dontoptimize +endif # No optimization + +ifdef LOCAL_INSTRUMENTATION_FOR +ifeq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),) +# If no obfuscation, link in the instrmented package's classes.jar as a library. +# link_instr_classes_jar is defined in base_rule.mk +legacy_proguard_flags += -libraryjars $(link_instr_classes_jar) +legacy_proguard_lib_deps += $(link_instr_classes_jar) +else # obfuscation +# If obfuscation is enabled, the main app must be obfuscated too. +# We need to run obfuscation using the main app's dictionary, +# and treat the main app's class.jar as injars instead of libraryjars. +legacy_proguard_flags := -injars $(link_instr_classes_jar) \ + -outjars $(intermediates.COMMON)/proguard.$(LOCAL_INSTRUMENTATION_FOR).jar \ + -include $(link_instr_intermediates_dir.COMMON)/proguard_options \ + -applymapping $(link_instr_intermediates_dir.COMMON)/proguard_dictionary \ + -verbose \ + $(legacy_proguard_flags) +legacy_proguard_lib_deps += \ + $(link_instr_classes_jar) \ + $(link_instr_intermediates_dir.COMMON)/proguard_options \ + $(link_instr_intermediates_dir.COMMON)/proguard_dictionary \ + +# Sometimes (test + main app) uses different keep rules from the main app - +# apply the main app's dictionary anyway. +legacy_proguard_flags += -ignorewarnings + +endif # no obfuscation +endif # LOCAL_INSTRUMENTATION_FOR + +proguard_flag_files := $(addprefix $(LOCAL_PATH)/, $(LOCAL_PROGUARD_FLAG_FILES)) +proguard_flag_files += $(addprefix $(LOCAL_PATH)/, $(LOCAL_R8_FLAG_FILES)) +LOCAL_PROGUARD_FLAGS += $(addprefix -include , $(proguard_flag_files)) +LOCAL_PROGUARD_FLAGS_DEPS += $(proguard_flag_files) +proguard_flag_files := + +ifdef LOCAL_TEST_MODULE_TO_PROGUARD_WITH +extra_input_jar := $(call intermediates-dir-for,APPS,$(LOCAL_TEST_MODULE_TO_PROGUARD_WITH),,COMMON)/classes.jar +else +extra_input_jar := +endif + +ifneq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),) + $(built_dex_intermediate): .KATI_IMPLICIT_OUTPUTS := $(proguard_dictionary) $(proguard_configuration) + + # Make a rule to copy the proguard_dictionary to a packaging directory. + $(eval $(call copy-one-file,$(proguard_dictionary),\ + $(call local-packaging-dir,proguard_dictionary)/proguard_dictionary)) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(call local-packaging-dir,proguard_dictionary)/proguard_dictionary) + + $(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),\ + $(call local-packaging-dir,proguard_dictionary)/classes.jar)) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(call local-packaging-dir,proguard_dictionary)/classes.jar) +endif + +endif # LOCAL_PROGUARD_ENABLED defined + +ifneq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true) +$(built_dex_intermediate): PRIVATE_DX_FLAGS := $(LOCAL_DX_FLAGS) + +ifdef LOCAL_PROGUARD_ENABLED + $(built_dex_intermediate): .KATI_NINJA_POOL := $(R8_NINJA_POOL) + $(built_dex_intermediate): PRIVATE_EXTRA_INPUT_JAR := $(extra_input_jar) + $(built_dex_intermediate): PRIVATE_PROGUARD_FLAGS := $(legacy_proguard_flags) $(common_proguard_flags) $(LOCAL_PROGUARD_FLAGS) + $(built_dex_intermediate): PRIVATE_PROGUARD_DICTIONARY := $(proguard_dictionary) + $(built_dex_intermediate) : $(full_classes_pre_proguard_jar) $(extra_input_jar) $(my_proguard_sdk_raise) $(common_proguard_flag_files) $(legacy_proguard_lib_deps) $(R8_COMPAT_PROGUARD) $(LOCAL_PROGUARD_FLAGS_DEPS) + $(transform-jar-to-dex-r8) +else # !LOCAL_PROGUARD_ENABLED + $(built_dex_intermediate): .KATI_NINJA_POOL := $(D8_NINJA_POOL) + $(built_dex_intermediate): PRIVATE_D8_LIBS := $(full_java_bootclasspath_libs) $(full_shared_java_header_libs) + $(built_dex_intermediate): $(full_java_bootclasspath_libs) $(full_shared_java_header_libs) + $(built_dex_intermediate): $(full_classes_pre_proguard_jar) $(DX) $(ZIP2ZIP) + $(transform-classes.jar-to-dex) +endif + +$(foreach pair,$(PRODUCT_BOOT_JARS), \ + $(if $(filter $(LOCAL_MODULE),$(call word-colon,2,$(pair))), \ + $(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files))) + +$(built_dex): $(built_dex_intermediate) + @echo Copying: $@ + $(hide) mkdir -p $(dir $@) + $(hide) rm -f $(dir $@)/classes*.dex + $(hide) cp -fp $(dir $<)/classes*.dex $(dir $@) + +java-dex: $(built_dex) + +endif # !LOCAL_IS_STATIC_JAVA_LIBRARY + +findbugs_xml := $(intermediates.COMMON)/findbugs.xml +$(findbugs_xml): PRIVATE_AUXCLASSPATH := $(addprefix -auxclasspath ,$(strip \ + $(call normalize-path-list,$(filter %.jar,$(full_java_libs))))) +$(findbugs_xml): PRIVATE_FINDBUGS_FLAGS := $(LOCAL_FINDBUGS_FLAGS) +$(findbugs_xml) : $(full_classes_pre_proguard_jar) $(filter %.xml, $(LOCAL_FINDBUGS_FLAGS)) + @echo Findbugs: $@ + $(hide) $(FINDBUGS) -textui -effort:min -xml:withMessages \ + $(PRIVATE_AUXCLASSPATH) $(PRIVATE_FINDBUGS_FLAGS) \ + $< \ + > $@ + +ALL_FINDBUGS_FILES += $(findbugs_xml) + +findbugs_html := $(PRODUCT_OUT)/findbugs/$(LOCAL_MODULE).html +$(findbugs_html) : PRIVATE_XML_FILE := $(findbugs_xml) +$(LOCAL_MODULE)-findbugs : $(findbugs_html) +.PHONY: $(LOCAL_MODULE)-findbugs +$(findbugs_html) : $(findbugs_xml) + @mkdir -p $(dir $@) + @echo ConvertXmlToText: $@ + $(hide) $(FINDBUGS_DIR)/convertXmlToText -html:fancy.xsl $(PRIVATE_XML_FILE) \ + > $@ + +$(LOCAL_MODULE)-findbugs : $(findbugs_html) + +endif # full_classes_jar is defined + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEFAULT_APP_TARGET_SDK := $(call module-target-sdk-version) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SDK_VERSION := $(call module-sdk-version) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MIN_SDK_VERSION := $(call codename-or-sdk-to-sdk,$(call module-min-sdk-version)) diff --git a/make/core/java_common.mk b/make/core/java_common.mk new file mode 100644 index 0000000..5981b60 --- /dev/null +++ b/make/core/java_common.mk @@ -0,0 +1,555 @@ +# Common to host and target Java modules. + +my_soong_problems := + +ifneq ($(filter ../%,$(LOCAL_SRC_FILES)),) +my_soong_problems += dotdot_srcs +endif + +########################################################### +## Java version +########################################################### +# Use the LOCAL_JAVA_LANGUAGE_VERSION if it is set, otherwise +# use one based on the LOCAL_SDK_VERSION. If it is < 24 +# pass "1.7" to the tools, if it is unset, >= 24 or "current" +# pass "1.8". +# +# The LOCAL_SDK_VERSION behavior is to ensure that, by default, +# code that is expected to run on older releases of Android +# does not use any 1.8 language features that are not supported +# on earlier runtimes (like default / static interface methods). +# Modules can override this logic by specifying +# LOCAL_JAVA_LANGUAGE_VERSION explicitly. +ifeq (,$(LOCAL_JAVA_LANGUAGE_VERSION)) + ifdef LOCAL_IS_HOST_MODULE + # Host modules always default to 1.9 + LOCAL_JAVA_LANGUAGE_VERSION := 1.9 + else + ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_18_SUPPORT))) + LOCAL_JAVA_LANGUAGE_VERSION := 1.7 + else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_19_SUPPORT))) + LOCAL_JAVA_LANGUAGE_VERSION := 1.8 + else ifneq (,$(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS)) + # TODO(ccross): allow 1.9 for current and unbundled once we have SDK system modules + LOCAL_JAVA_LANGUAGE_VERSION := 1.8 + else + LOCAL_JAVA_LANGUAGE_VERSION := 1.9 + endif + endif +endif +LOCAL_JAVACFLAGS += -source $(LOCAL_JAVA_LANGUAGE_VERSION) -target $(LOCAL_JAVA_LANGUAGE_VERSION) + +########################################################### + +# OpenJDK versions up to 8 shipped with bootstrap and tools jars +# (rt.jar, jce.jar, tools.jar etc.). These are no longer part of +# OpenJDK 9, but we still make them available for host tools that +# are targeting older versions. +USE_HOST_BOOTSTRAP_JARS := true +ifeq (,$(filter $(LOCAL_JAVA_LANGUAGE_VERSION), 1.6 1.7 1.8)) +USE_HOST_BOOTSTRAP_JARS := false +endif + +########################################################### + +# Drop HOST_JDK_TOOLS_JAR from classpath when targeting versions > 9 (which don't have it). +# TODO: Remove HOST_JDK_TOOLS_JAR and all references to it once host +# bootstrap jars are no longer supported (ie. when USE_HOST_BOOTSTRAP_JARS +# is always false). http://b/38418220 +ifneq ($(USE_HOST_BOOTSTRAP_JARS),true) +LOCAL_CLASSPATH := $(filter-out $(HOST_JDK_TOOLS_JAR),$(LOCAL_CLASSPATH)) +endif + +########################################################### +## .proto files: Compile proto files to .java +########################################################### +ifeq ($(strip $(LOCAL_PROTOC_OPTIMIZE_TYPE)),) + LOCAL_PROTOC_OPTIMIZE_TYPE := lite +endif +proto_sources := $(filter %.proto,$(LOCAL_SRC_FILES)) +ifneq ($(proto_sources),) +proto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources)) + +proto_java_intemediate_dir := $(intermediates.COMMON)/proto +proto_java_sources_dir := $(proto_java_intemediate_dir)/src +proto_java_srcjar := $(intermediates.COMMON)/proto.srcjar + +LOCAL_SRCJARS += $(proto_java_srcjar) + +$(proto_java_srcjar): PRIVATE_PROTO_INCLUDES := $(TOP) +$(proto_java_srcjar): PRIVATE_PROTO_SRC_FILES := $(proto_sources_fullpath) +$(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_DIR := $(proto_java_sources_dir) +$(proto_java_srcjar): PRIVATE_PROTOC_FLAGS := $(LOCAL_PROTOC_FLAGS) +ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro) + $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javamicro_out + $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javamicro + $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javamicro +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano) + $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javanano_out + $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javanano + $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javanano +else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),stream) + $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javastream_out + $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javastream + $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javastream +else + $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --java_out +endif +$(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_PARAMS := $(if $(filter lite,$(LOCAL_PROTOC_OPTIMIZE_TYPE)),lite$(if $(LOCAL_PROTO_JAVA_OUTPUT_PARAMS),:,),)$(LOCAL_PROTO_JAVA_OUTPUT_PARAMS) +$(proto_java_srcjar) : $(proto_sources_fullpath) $(PROTOC) $(SOONG_ZIP) + $(call transform-proto-to-java) + +#TODO: protoc should output the dependencies introduced by imports. + +ALL_MODULES.$(my_register_name).PROTO_FILES := $(proto_sources_fullpath) +endif # proto_sources + +######################################### +## Java resources + +# Look for resource files in any specified directories. +# Non-java and non-doc files will be picked up as resources +# and included in the output jar file. +java_resource_file_groups := + +LOCAL_JAVA_RESOURCE_DIRS := $(strip $(LOCAL_JAVA_RESOURCE_DIRS)) +ifneq ($(LOCAL_JAVA_RESOURCE_DIRS),) + # This makes a list of words like + # ::: :: : + # where each of the files is relative to the directory it's grouped with. + # Directories that don't contain any resource files will result in groups + # that end with a colon, and they are stripped out in the next step. + java_resource_file_groups += \ + $(foreach dir,$(LOCAL_JAVA_RESOURCE_DIRS), \ + $(subst $(space),:,$(strip \ + $(LOCAL_PATH)/$(dir): \ + $(patsubst ./%,%,$(sort $(shell cd $(LOCAL_PATH)/$(dir) && \ + find . \ + -type d -a -name ".svn" -prune -o \ + -type f \ + -a \! -name "*.java" \ + -a \! -name "package.html" \ + -a \! -name "overview.html" \ + -a \! -name ".*.swp" \ + -a \! -name ".DS_Store" \ + -a \! -name "*~" \ + -print \ + ))) \ + )) \ + ) + java_resource_file_groups := $(filter-out %:,$(java_resource_file_groups)) +endif # LOCAL_JAVA_RESOURCE_DIRS + +ifneq ($(LOCAL_JAVA_RESOURCE_FILES),) + # Converts LOCAL_JAVA_RESOURCE_FILES := to $(dir $(file))::$(notdir $(file)) + # and LOCAL_JAVA_RESOURCE_FILES :=

: to :: + java_resource_file_groups += $(strip $(foreach res,$(LOCAL_JAVA_RESOURCE_FILES), \ + $(eval _file := $(call word-colon,2,$(res))) \ + $(if $(_file), \ + $(eval _base := $(call word-colon,1,$(res))), \ + $(eval _base := $(dir $(res))) \ + $(eval _file := $(notdir $(res)))) \ + $(if $(filter /%, \ + $(filter-out $(OUT_DIR)/%,$(_base) $(_file))), \ + $(call pretty-error,LOCAL_JAVA_RESOURCE_FILES may not include absolute paths: $(_base) $(_file))) \ + $(patsubst %/,%,$(_base))::$(_file))) + +endif # LOCAL_JAVA_RESOURCE_FILES + +ifdef java_resource_file_groups + # The full paths to all resources, used for dependencies. + java_resource_sources := \ + $(foreach group,$(java_resource_file_groups), \ + $(addprefix $(word 1,$(subst :,$(space),$(group)))/, \ + $(wordlist 2,9999,$(subst :,$(space),$(group))) \ + ) \ + ) + # The arguments to jar that will include these files in a jar file. + # Quote the file name to handle special characters (such as #) correctly. + extra_jar_args := \ + $(foreach group,$(java_resource_file_groups), \ + $(addprefix -C "$(word 1,$(subst :,$(space),$(group)))" , \ + $(foreach w, $(wordlist 2,9999,$(subst :,$(space),$(group))), "$(w)" ) \ + ) \ + ) + java_resource_file_groups := +else + java_resource_sources := + extra_jar_args := +endif # java_resource_file_groups + +##################################### +## Warn if there is unrecognized file in LOCAL_SRC_FILES. +my_unknown_src_files := $(filter-out \ + %.java %.aidl %.proto %.logtags, \ + $(LOCAL_SRC_FILES) $(LOCAL_INTERMEDIATE_SOURCES) $(LOCAL_GENERATED_SOURCES)) +ifneq ($(my_unknown_src_files),) +$(warning $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Unused source files: $(my_unknown_src_files)) +endif + +###################################### +## PRIVATE java vars +# LOCAL_SOURCE_FILES_ALL_GENERATED is set only if the module does not have static source files, +# but generated source files in its LOCAL_INTERMEDIATE_SOURCE_DIR. +# You have to set up the dependency in some other way. +need_compile_java := $(strip $(all_java_sources)$(LOCAL_SRCJARS)$(all_res_assets)$(java_resource_sources))$(LOCAL_STATIC_JAVA_LIBRARIES)$(filter true,$(LOCAL_SOURCE_FILES_ALL_GENERATED)) +ifdef need_compile_java + +annotation_processor_flags := +annotation_processor_deps := +annotation_processor_jars := + +# If error prone is enabled then add LOCAL_ERROR_PRONE_FLAGS to LOCAL_JAVACFLAGS +ifeq ($(RUN_ERROR_PRONE),true) +annotation_processor_jars += $(ERROR_PRONE_JARS) +LOCAL_JAVACFLAGS += $(ERROR_PRONE_FLAGS) +LOCAL_JAVACFLAGS += '-Xplugin:ErrorProne $(ERROR_PRONE_CHECKS) $(LOCAL_ERROR_PRONE_FLAGS)' +endif + +ifdef LOCAL_ANNOTATION_PROCESSORS + annotation_processor_jars += $(call java-lib-files,$(LOCAL_ANNOTATION_PROCESSORS),true) + + # b/25860419: annotation processors must be explicitly specified for grok + annotation_processor_flags += $(foreach class,$(LOCAL_ANNOTATION_PROCESSOR_CLASSES),-processor $(class)) +endif + +ifneq (,$(strip $(annotation_processor_jars))) +annotation_processor_flags += -processorpath $(call normalize-path-list,$(annotation_processor_jars)) +annotation_processor_deps += $(annotation_processor_jars) +endif + +full_static_java_libs := $(call java-lib-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE)) +full_static_java_header_libs := $(call java-lib-header-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE)) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_LIBRARIES := $(full_static_java_libs) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_HEADER_LIBRARIES := $(full_static_java_header_libs) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_ASSET_DIR) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CLASS_INTERMEDIATES_DIR := $(intermediates.COMMON)/classes +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ANNO_INTERMEDIATES_DIR := $(intermediates.COMMON)/anno +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/src +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HAS_RS_SOURCES := +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCE_LIST := $(java_source_list_file) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RMTYPEDEFS := $(LOCAL_RMTYPEDEFS) + +# Quickly check class path vars. +disallowed_deps := $(foreach sdk,$(TARGET_AVAILABLE_SDK_VERSIONS),$(call resolve-prebuilt-sdk-module,$(sdk))) +disallowed_deps += $(foreach sdk,$(TARGET_AVAILABLE_SDK_VERSIONS),\ + $(foreach sdk_lib,$(JAVA_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,$(sdk),$(sdk_lib)))) +bad_deps := $(filter $(disallowed_deps),$(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES)) +ifneq (,$(bad_deps)) + $(call pretty-error,SDK modules should not be depended on directly. Please use LOCAL_SDK_VERSION for $(bad_deps)) +endif + +full_java_bootclasspath_libs := +empty_bootclasspath := +my_system_modules := +exported_sdk_libs_files := +my_exported_sdk_libs_file := + +ifndef LOCAL_IS_HOST_MODULE + sdk_libs := + + # When an sdk lib name is listed in LOCAL_JAVA_LIBRARIES, move it to LOCAL_SDK_LIBRARIES, so that + # it is correctly redirected to the stubs library. + LOCAL_SDK_LIBRARIES += $(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES)) + LOCAL_JAVA_LIBRARIES := $(filter-out $(JAVA_SDK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES)) + + ifeq ($(LOCAL_SDK_VERSION),) + ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) + # No bootclasspath. But we still need "" to prevent javac from using default host bootclasspath. + empty_bootclasspath := "" + # Most users of LOCAL_NO_STANDARD_LIBRARIES really mean no framework libs, + # and manually add back the core libs. The ones that don't are in soong + # now, so just always assume that they want the default system modules + my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES) + else # LOCAL_NO_STANDARD_LIBRARIES + full_java_bootclasspath_libs := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES)) + LOCAL_JAVA_LIBRARIES := $(filter-out $(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES)) + my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES) + endif # LOCAL_NO_STANDARD_LIBRARIES + + ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)) + sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,system_current,$(lib_name))) + else + # When SDK libraries are referenced from modules built without SDK, provide the all APIs to them + sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name)) + endif + else + ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) + $(call pretty-error,Must not define both LOCAL_NO_STANDARD_LIBRARIES and LOCAL_SDK_VERSION) + endif + ifeq ($(strip $(filter $(LOCAL_SDK_VERSION),$(TARGET_AVAILABLE_SDK_VERSIONS))),) + $(call pretty-error,Invalid LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)' \ + Choices are: $(TARGET_AVAILABLE_SDK_VERSIONS)) + endif + + ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(filter-out %current,$(LOCAL_SDK_VERSION))) + # TARGET_BUILD_USE_PREBUILT_SDKS mode or numbered SDK. Use prebuilt modules. + sdk_module := $(call resolve-prebuilt-sdk-module,$(LOCAL_SDK_VERSION)) + sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,$(LOCAL_SDK_VERSION),$(lib_name))) + else + # Note: the lib naming scheme must be kept in sync with build/soong/java/sdk_library.go. + sdk_lib_suffix = $(call pretty-error,sdk_lib_suffix was not set correctly) + ifeq (current,$(LOCAL_SDK_VERSION)) + sdk_module := android_stubs_current + sdk_lib_suffix := .stubs + else ifeq (system_current,$(LOCAL_SDK_VERSION)) + sdk_module := android_system_stubs_current + sdk_lib_suffix := .stubs.system + else ifeq (test_current,$(LOCAL_SDK_VERSION)) + sdk_module := android_test_stubs_current + sdk_lib_suffix := .stubs.test + else ifeq (core_current,$(LOCAL_SDK_VERSION)) + sdk_module := core.current.stubs + sdk_lib_suffix = $(call pretty-error,LOCAL_SDK_LIBRARIES not supported for LOCAL_SDK_VERSION = core_current) + endif + sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name)$(sdk_lib_suffix)) + endif + full_java_bootclasspath_libs := $(call java-lib-header-files,$(sdk_module)) + endif # LOCAL_SDK_VERSION + + ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) + ifneq ($(LOCAL_MODULE),jacocoagent) + ifeq ($(EMMA_INSTRUMENT),true) + ifneq ($(EMMA_INSTRUMENT_STATIC),true) + # For instrumented build, if Jacoco is not being included statically + # in instrumented packages then include Jacoco classes into the + # bootclasspath. + full_java_bootclasspath_libs += $(call java-lib-header-files,jacocoagent) + endif # EMMA_INSTRUMENT_STATIC + endif # EMMA_INSTRUMENT + endif # LOCAL_MODULE == jacocoagent + endif # LOCAL_NO_STANDARD_LIBRARIES + + # In order to compile lambda code javac requires various invokedynamic- + # related classes to be present. This change adds stubs needed for + # javac to compile lambdas. + ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) + ifdef TARGET_BUILD_USE_PREBUILT_SDKS + full_java_bootclasspath_libs += $(call java-lib-header-files,sdk-core-lambda-stubs) + else + full_java_bootclasspath_libs += $(call java-lib-header-files,core-lambda-stubs) + endif + endif + full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES) $(sdk_libs),$(LOCAL_IS_HOST_MODULE)) + full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES) $(sdk_libs),$(LOCAL_IS_HOST_MODULE)) + sdk_libs := + + # Files that contains the names of SDK libraries exported from dependencies. These will be re-exported. + # Note: No need to consider LOCAL_*_ANDROID_LIBRARIES and LOCAL_STATIC_JAVA_AAR_LIBRARIES. They are all appended to + # LOCAL_*_JAVA_LIBRARIES in java.mk + exported_sdk_libs_files := $(call exported-sdk-libs-files,$(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES)) + # The file that contains the names of all SDK libraries that this module exports and re-exports + my_exported_sdk_libs_file := $(call local-intermediates-dir,COMMON)/exported-sdk-libs + +else # LOCAL_IS_HOST_MODULE + + ifeq ($(USE_CORE_LIB_BOOTCLASSPATH),true) + ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) + empty_bootclasspath := "" + else + full_java_bootclasspath_libs := $(call java-lib-header-files,$(addsuffix -hostdex,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES)),true) + endif + + my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES) + full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),true) + full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES),true) + else # !USE_CORE_LIB_BOOTCLASSPATH + # Give host-side tools a version of OpenJDK's standard libraries + # close to what they're targeting. As of Dec 2017, AOSP is only + # bundling OpenJDK 8 and 9, so nothing < 8 is available. + # + # When building with OpenJDK 8, the following should have no + # effect since those jars would be available by default. + # + # When building with OpenJDK 9 but targeting a version < 1.8, + # putting them on the bootclasspath means that: + # a) code can't (accidentally) refer to OpenJDK 9 specific APIs + # b) references to existing APIs are not reinterpreted in an + # OpenJDK 9-specific way, eg. calls to subclasses of + # java.nio.Buffer as in http://b/70862583 + ifeq ($(USE_HOST_BOOTSTRAP_JARS),true) + full_java_bootclasspath_libs += $(ANDROID_JAVA8_HOME)/jre/lib/jce.jar + full_java_bootclasspath_libs += $(ANDROID_JAVA8_HOME)/jre/lib/rt.jar + endif + full_shared_java_libs := $(addprefix $(HOST_OUT_JAVA_LIBRARIES)/,\ + $(addsuffix $(COMMON_JAVA_PACKAGE_SUFFIX),$(LOCAL_JAVA_LIBRARIES))) + full_shared_java_header_libs := $(full_shared_java_libs) + endif # USE_CORE_LIB_BOOTCLASSPATH +endif # !LOCAL_IS_HOST_MODULE + +# (b/204397180) Record ALL_DEPS by default. +ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) $(full_java_bootclasspath_libs) + +# Export the SDK libs. The sdk library names listed in LOCAL_SDK_LIBRARIES are first exported. +# Then sdk library names exported from dependencies are all re-exported. +$(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS_FILES := $(exported_sdk_libs_files) +$(my_exported_sdk_libs_file): PRIVATE_SDK_LIBS := $(sort $(LOCAL_SDK_LIBRARIES)) +$(my_exported_sdk_libs_file): $(exported_sdk_libs_files) + @echo "Export SDK libs $@" + $(hide) mkdir -p $(dir $@) && rm -f $@ $@.temp + $(if $(PRIVATE_SDK_LIBS),\ + echo $(PRIVATE_SDK_LIBS) | tr ' ' '\n' > $@.temp,\ + touch $@.temp) + $(if $(PRIVATE_EXPORTED_SDK_LIBS_FILES),\ + cat $(PRIVATE_EXPORTED_SDK_LIBS_FILES) >> $@.temp) + $(hide) cat $@.temp | sort -u > $@ + $(hide) rm -f $@.temp + +ifdef empty_bootclasspath + ifdef full_java_bootclasspath_libs + $(call pretty-error,internal error: empty_bootclasspath and full_java_bootclasspath_libs should not both be set) + endif +endif + +full_java_system_modules_deps := +my_system_modules_dir := +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_USE_SYSTEM_MODULES := +ifeq ($(LOCAL_JAVA_LANGUAGE_VERSION),1.9) + $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_USE_SYSTEM_MODULES := true + ifdef my_system_modules + ifneq ($(my_system_modules),none) + ifndef SOONG_SYSTEM_MODULES_$(my_system_modules) + $(call pretty-error, Invalid system modules $(my_system_modules)) + endif + full_java_system_modules_deps := $(SOONG_SYSTEM_MODULES_DEPS_$(my_system_modules)) + my_system_modules_dir := $(SOONG_SYSTEM_MODULES_$(my_system_modules)) + endif + endif +endif + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_BOOTCLASSPATH := $(full_java_bootclasspath_libs) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EMPTY_BOOTCLASSPATH := $(empty_bootclasspath) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES := $(my_system_modules) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES_DIR := $(my_system_modules_dir) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES_LIBS := $(call java-lib-files,$(SOONG_SYSTEM_MODULES_LIBS_$(my_system_modules))) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PATCH_MODULE := $(LOCAL_PATCH_MODULE) + +ifndef LOCAL_IS_HOST_MODULE +# This is set by packages that are linking to other packages that export +# shared libraries, allowing them to make use of the code in the linked apk. +apk_libraries := $(sort $(LOCAL_APK_LIBRARIES) $(LOCAL_RES_LIBRARIES)) +ifneq ($(apk_libraries),) + link_apk_libraries := $(call app-lib-files,$(apk_libraries)) + link_apk_header_libs := $(call app-lib-header-files,$(apk_libraries)) + + # link against the jar with full original names (before proguard processing). + full_shared_java_libs += $(link_apk_libraries) + full_shared_java_header_libs += $(link_apk_header_libs) +endif + +# This is set by packages that contain instrumentation, allowing them to +# link against the package they are instrumenting. Currently only one such +# package is allowed. +LOCAL_INSTRUMENTATION_FOR := $(strip $(LOCAL_INSTRUMENTATION_FOR)) +ifdef LOCAL_INSTRUMENTATION_FOR + ifneq ($(words $(LOCAL_INSTRUMENTATION_FOR)),1) + $(error \ + $(LOCAL_PATH): Multiple LOCAL_INSTRUMENTATION_FOR members defined) + endif + + link_instr_intermediates_dir.COMMON := $(call intermediates-dir-for, \ + APPS,$(LOCAL_INSTRUMENTATION_FOR),,COMMON) + # link against the jar with full original names (before proguard processing). + link_instr_classes_jar := $(link_instr_intermediates_dir.COMMON)/classes-pre-proguard.jar + ifneq ($(TURBINE_ENABLED),false) + link_instr_classes_header_jar := $(link_instr_intermediates_dir.COMMON)/classes-header.jar + else + link_instr_classes_header_jar := $(link_instr_intermediates_dir.COMMON)/classes.jar + endif + full_shared_java_libs += $(link_instr_classes_jar) + full_shared_java_header_libs += $(link_instr_classes_header_jar) +endif # LOCAL_INSTRUMENTATION_FOR +endif # LOCAL_IS_HOST_MODULE + +endif # need_compile_java + +# We may want to add jar manifest or jar resource files even if there is no java code at all. +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args) +jar_manifest_file := +ifneq ($(strip $(LOCAL_JAR_MANIFEST)),) +jar_manifest_file := $(LOCAL_PATH)/$(LOCAL_JAR_MANIFEST) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST := $(jar_manifest_file) +else +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST := +endif + +########################################################## + +full_java_libs := $(full_shared_java_libs) $(full_static_java_libs) $(LOCAL_CLASSPATH) +full_java_header_libs := $(full_shared_java_header_libs) $(full_static_java_header_libs) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_JAVA_LIBRARIES := $(full_java_libs) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_JAVA_HEADER_LIBRARIES := $(full_java_header_libs) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SHARED_JAVA_HEADER_LIBRARIES := $(full_shared_java_header_libs) + +ALL_MODULES.$(my_register_name).INTERMEDIATE_SOURCE_DIR := \ + $(ALL_MODULES.$(my_register_name).INTERMEDIATE_SOURCE_DIR) $(LOCAL_INTERMEDIATE_SOURCE_DIR) + + +########################################################## +# Copy NOTICE files of transitive static dependencies +# Don't do this in mm, since many of the targets won't exist. +installed_static_library_notice_file_targets := \ + $(foreach lib,$(LOCAL_STATIC_JAVA_LIBRARIES), \ + NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-JAVA_LIBRARIES-$(lib)) + +$(notice_target): | $(installed_static_library_notice_file_targets) +$(LOCAL_INSTALLED_MODULE): | $(notice_target) + +########################################################### +# Verify that all libraries are safe to use +########################################################### +ifndef LOCAL_IS_HOST_MODULE +ifeq ($(LOCAL_SDK_VERSION),system_current) +my_link_type := java:system +my_warn_types := +my_allowed_types := java:sdk java:system java:core +else ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) +my_link_type := java:system +my_warn_types := +my_allowed_types := java:sdk java:system java:core +else ifeq ($(LOCAL_SDK_VERSION),core_current) +my_link_type := java:core +my_warn_types := +my_allowed_types := java:core +else ifneq ($(LOCAL_SDK_VERSION),) +my_link_type := java:sdk +my_warn_types := +my_allowed_types := java:sdk java:core +else +my_link_type := java:platform +my_warn_types := +my_allowed_types := java:sdk java:system java:platform java:core +endif + +my_link_deps := $(addprefix JAVA_LIBRARIES:,$(LOCAL_STATIC_JAVA_LIBRARIES) $(LOCAL_JAVA_LIBRARIES)) +my_link_deps += $(addprefix APPS:,$(apk_libraries)) + +my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) +my_common := COMMON +include $(BUILD_SYSTEM)/link_type.mk +endif # !LOCAL_IS_HOST_MODULE + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + +SOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \ + $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems) +SOONG_CONV.$(LOCAL_MODULE).DEPS := \ + $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \ + $(LOCAL_STATIC_JAVA_LIBRARIES) \ + $(LOCAL_JAVA_LIBRARIES) \ + $(LOCAL_JNI_SHARED_LIBRARIES) +SOONG_CONV.$(LOCAL_MODULE).TYPE := java +SOONG_CONV.$(LOCAL_MODULE).MAKEFILES := \ + $(SOONG_CONV.$(LOCAL_MODULE).MAKEFILES) $(LOCAL_MODULE_MAKEFILE) +SOONG_CONV.$(LOCAL_MODULE).INSTALLED := \ + $(SOONG_CONV.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE) +SOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE) + +endif diff --git a/make/core/java_host_test_config_template.xml b/make/core/java_host_test_config_template.xml new file mode 100644 index 0000000..26c1caf --- /dev/null +++ b/make/core/java_host_test_config_template.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/make/core/java_host_unit_test_config_template.xml b/make/core/java_host_unit_test_config_template.xml new file mode 100644 index 0000000..d8795f9 --- /dev/null +++ b/make/core/java_host_unit_test_config_template.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/make/core/java_library.mk b/make/core/java_library.mk new file mode 100644 index 0000000..3ac03dc --- /dev/null +++ b/make/core/java_library.mk @@ -0,0 +1,90 @@ +########################################################### +## Standard rules for building a java library. +## +########################################################### +$(call record-module-type,JAVA_LIBRARY) + +ifdef LOCAL_IS_HOST_MODULE +$(error $(LOCAL_PATH): Host java libraries must use BUILD_HOST_JAVA_LIBRARY) +endif + +LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX) +LOCAL_MODULE_CLASS := JAVA_LIBRARIES + +ifneq (,$(LOCAL_ASSET_DIR)) +$(error $(LOCAL_PATH): Target java libraries may not set LOCAL_ASSET_DIR) +endif + +ifneq (true,$(LOCAL_IS_STATIC_JAVA_LIBRARY)) +ifneq (,$(LOCAL_RESOURCE_DIR)) +$(error $(LOCAL_PATH): Target java libraries may not set LOCAL_RESOURCE_DIR) +endif +# base_rules.mk looks at this +all_res_assets := +endif + +LOCAL_BUILT_MODULE_STEM := javalib.jar + +# For java libraries, other modules should depend on +# out/target/common/obj/JAVA_LIBRARIES/.../classes.jar. +# There are some dependencies outside the build system that assume static +# java libraries produce javalib.jar, so we will copy classes.jar there too. +intermediates.COMMON := $(call local-intermediates-dir,COMMON) +common_javalib.jar := $(intermediates.COMMON)/javalib.jar +dex_preopt_profile_src_file := $(common_javalib.jar) +LOCAL_INTERMEDIATE_TARGETS += $(common_javalib.jar) + +ifeq ($(LOCAL_PROGUARD_ENABLED),disabled) + LOCAL_PROGUARD_ENABLED := +endif + +ifeq (true,$(EMMA_INSTRUMENT)) +ifeq (true,$(LOCAL_EMMA_INSTRUMENT)) +ifeq (true,$(EMMA_INSTRUMENT_STATIC)) +LOCAL_STATIC_JAVA_LIBRARIES += jacocoagent +# Exclude jacoco classes from proguard +LOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags +LOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags +endif # LOCAL_EMMA_INSTRUMENT +endif # EMMA_INSTRUMENT_STATIC +else +LOCAL_EMMA_INSTRUMENT := false +endif # EMMA_INSTRUMENT + +my_dex_jar := $(common_javalib.jar) + +################################# +include $(BUILD_SYSTEM)/java.mk +################################# + +ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true) +# There are some dependencies outside the build system that assume classes.jar +# is available as javalib.jar so copy it there too. +$(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(common_javalib.jar))) + +$(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(LOCAL_BUILT_MODULE))) + +else # !LOCAL_IS_STATIC_JAVA_LIBRARY + +$(common_javalib.jar): PRIVATE_DEX_FILE := $(built_dex) +$(common_javalib.jar): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar) +$(common_javalib.jar): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP) +$(common_javalib.jar) : $(full_classes_pre_proguard_jar) $(built_dex) $(java_resource_sources) | $(ZIPTIME) $(ZIPALIGN) + @echo "target Jar: $(PRIVATE_MODULE) ($@)" + rm -rf $@.parts && mkdir -p $@.parts + $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) + $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) + $(MERGE_ZIPS) -j $@.tmp $@.parts/dex.zip $@.parts/res.zip + rm -rf $@.parts + $(hide) $(ZIPTIME) $@.tmp + $(call commit-change-for-toc,$@) +ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) + $(uncompress-dexs) + $(align-package) +endif # LOCAL_UNCOMPRESS_DEX + +.KATI_RESTAT: $(common_javalib.jar) + +$(eval $(call copy-one-file,$(common_javalib.jar),$(LOCAL_BUILT_MODULE))) + +endif # !LOCAL_IS_STATIC_JAVA_LIBRARY diff --git a/make/core/java_prebuilt_internal.mk b/make/core/java_prebuilt_internal.mk new file mode 100644 index 0000000..be733ff --- /dev/null +++ b/make/core/java_prebuilt_internal.mk @@ -0,0 +1,231 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################ +# Internal build rules for JAVA_LIBRARIES prebuilt modules +############################################################ + +ifneq (JAVA_LIBRARIES,$(LOCAL_MODULE_CLASS)) +$(call pretty-error,java_prebuilt_internal.mk is for JAVA_LIBRARIES modules only) +endif + +include $(BUILD_SYSTEM)/base_rules.mk +built_module := $(LOCAL_BUILT_MODULE) + +ifeq (,$(LOCAL_IS_HOST_MODULE)$(filter true,$(LOCAL_UNINSTALLABLE_MODULE))) + prebuilt_module_is_dex_javalib := true +else + prebuilt_module_is_dex_javalib := +endif + +ifeq ($(prebuilt_module_is_dex_javalib),true) +my_dex_jar := $(my_prebuilt_src_file) +# This is a target shared library, i.e. a jar with classes.dex. + +$(foreach pair,$(PRODUCT_BOOT_JARS), \ + $(if $(filter $(LOCAL_MODULE),$(call word-colon,2,$(pair))), \ + $(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files))) + +ALL_MODULES.$(my_register_name).CLASSES_JAR := $(common_classes_jar) + +####################################### +# defines built_odex along with rule to install odex +my_manifest_or_apk := $(my_prebuilt_src_file) +include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk +my_manifest_or_apk := +####################################### +$(built_module) : $(my_prebuilt_src_file) + $(call copy-file-to-target) + +else # ! prebuilt_module_is_dex_javalib +$(built_module) : $(my_prebuilt_src_file) + $(transform-prebuilt-to-target) +endif # ! prebuilt_module_is_dex_javalib + +my_src_jar := $(my_prebuilt_src_file) + +ifdef LOCAL_IS_HOST_MODULE +# for host java libraries deps should be in the common dir, so we make a copy in +# the common dir. +common_classes_jar := $(intermediates.COMMON)/classes.jar +common_header_jar := $(intermediates.COMMON)/classes-header.jar + +$(common_classes_jar): PRIVATE_MODULE := $(LOCAL_MODULE) +$(common_classes_jar): PRIVATE_PREFIX := $(my_prefix) + +$(common_classes_jar) : $(my_src_jar) + $(transform-prebuilt-to-target) + +ifneq ($(TURBINE_ENABLED),false) +$(common_header_jar) : $(my_src_jar) + $(transform-prebuilt-to-target) +endif + +else # !LOCAL_IS_HOST_MODULE +# for target java libraries, the LOCAL_BUILT_MODULE is in a product-specific dir, +# while the deps should be in the common dir, so we make a copy in the common dir. +common_classes_jar := $(intermediates.COMMON)/classes.jar +common_header_jar := $(intermediates.COMMON)/classes-header.jar +common_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar +common_javalib_jar := $(intermediates.COMMON)/javalib.jar + +$(common_classes_jar) $(common_classes_pre_proguard_jar) $(common_javalib_jar): PRIVATE_MODULE := $(LOCAL_MODULE) +$(common_classes_jar) $(common_classes_pre_proguard_jar) $(common_javalib_jar): PRIVATE_PREFIX := $(my_prefix) + +ifeq ($(LOCAL_SDK_VERSION),system_current) +my_link_type := java:system +else ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) +my_link_type := java:system +else ifeq ($(LOCAL_SDK_VERSION),core_current) +my_link_type := java:core +else ifneq ($(LOCAL_SDK_VERSION),) +my_link_type := java:sdk +else +my_link_type := java:platform +endif + +# TODO: check dependencies of prebuilt files +my_link_deps := + +my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) +my_common := COMMON +include $(BUILD_SYSTEM)/link_type.mk + +ifeq ($(prebuilt_module_is_dex_javalib),true) +# For prebuilt shared Java library we don't have classes.jar. +$(common_javalib_jar) : $(my_src_jar) + $(transform-prebuilt-to-target) + +else # ! prebuilt_module_is_dex_javalib + +my_src_aar := $(filter %.aar, $(my_prebuilt_src_file)) +ifneq ($(my_src_aar),) +# This is .aar file, archive of classes.jar and Android resources. + +# run Jetifier if needed +LOCAL_JETIFIER_INPUT_FILE := $(my_src_aar) +include $(BUILD_SYSTEM)/jetifier.mk +my_src_aar := $(LOCAL_JETIFIER_OUTPUT_FILE) + +my_src_jar := $(intermediates.COMMON)/aar/classes.jar +my_src_proguard_options := $(intermediates.COMMON)/aar/proguard.txt +my_src_android_manifest := $(intermediates.COMMON)/aar/AndroidManifest.xml + +$(my_src_jar) : .KATI_IMPLICIT_OUTPUTS := $(my_src_proguard_options) +$(my_src_jar) : .KATI_IMPLICIT_OUTPUTS += $(my_src_android_manifest) +$(my_src_jar) : $(my_src_aar) + $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@) $(dir $@)/res + $(hide) unzip -qoDD -d $(dir $@) $< + # Make sure the proguard and AndroidManifest.xml files exist + $(hide) touch $(dir $@)/proguard.txt + $(hide) touch $(dir $@)/AndroidManifest.xml + +my_prebuilt_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml +$(eval $(call copy-one-file,$(my_src_android_manifest),$(my_prebuilt_android_manifest))) +$(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_prebuilt_android_manifest)) + +else + +# run Jetifier if needed +LOCAL_JETIFIER_INPUT_FILE := $(my_src_jar) +include $(BUILD_SYSTEM)/jetifier.mk +my_src_jar := $(LOCAL_JETIFIER_OUTPUT_FILE) + +endif + +$(common_classes_jar) : $(my_src_jar) + $(transform-prebuilt-to-target) + +ifneq ($(TURBINE_ENABLED),false) +$(common_header_jar) : $(my_src_jar) + $(transform-prebuilt-to-target) +endif + +$(common_classes_pre_proguard_jar) : $(my_src_jar) + $(transform-prebuilt-to-target) + +$(common_javalib_jar) : $(common_classes_jar) + $(transform-prebuilt-to-target) + +include $(BUILD_SYSTEM)/force_aapt2.mk + +ifneq ($(my_src_aar),) + +$(intermediates.COMMON)/export_proguard_flags : $(my_src_proguard_options) + $(transform-prebuilt-to-target) + +LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION)) +ifeq ($(LOCAL_SDK_RES_VERSION),) + LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION) +endif + +framework_res_package_export := +# Please refer to package.mk +ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) +ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),) +framework_res_package_export := \ + $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION)) +else +framework_res_package_export := \ + $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk +endif +endif + +my_res_package := $(intermediates.COMMON)/package-res.apk + +# We needed only very few PRIVATE variables and aapt2.mk input variables. Reset the unnecessary ones. +$(my_res_package): PRIVATE_AAPT2_CFLAGS := +$(my_res_package): PRIVATE_AAPT_FLAGS := --static-lib --no-static-lib-packages --auto-add-overlay +$(my_res_package): PRIVATE_ANDROID_MANIFEST := $(my_src_android_manifest) +$(my_res_package): PRIVATE_AAPT_INCLUDES := $(framework_res_package_export) +$(my_res_package): PRIVATE_SOURCE_INTERMEDIATES_DIR := +$(my_res_package): PRIVATE_PROGUARD_OPTIONS_FILE := +$(my_res_package): PRIVATE_DEFAULT_APP_TARGET_SDK := +$(my_res_package): PRIVATE_DEFAULT_APP_TARGET_SDK := +$(my_res_package): PRIVATE_PRODUCT_AAPT_CONFIG := +$(my_res_package): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := +$(my_res_package): PRIVATE_TARGET_AAPT_CHARACTERISTICS := +$(my_res_package) : $(framework_res_package_export) +$(my_res_package) : $(my_src_android_manifest) + +full_android_manifest := +my_res_resources := +my_overlay_resources := +my_compiled_res_base_dir := $(intermediates.COMMON)/flat-res +R_file_stamp := +proguard_options_file := +my_generated_res_dirs := $(intermediates.COMMON)/aar/res +my_generated_res_dirs_deps := $(my_src_jar) +include $(BUILD_SYSTEM)/aapt2.mk + +# Make sure my_res_package is created when you run mm/mmm. +$(built_module) : $(my_res_package) +endif # $(my_src_aar) + +# make sure the classes.jar and javalib.jar are built before $(LOCAL_BUILT_MODULE) +$(built_module) : $(common_javalib_jar) + +my_exported_sdk_libs_file := $(intermediates.COMMON)/exported-sdk-libs +$(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS := $(LOCAL_EXPORT_SDK_LIBRARIES) +$(my_exported_sdk_libs_file): + @echo "Export SDK libs $@" + $(hide) mkdir -p $(dir $@) && rm -f $@ + $(if $(PRIVATE_EXPORTED_SDK_LIBS),\ + $(hide) echo $(PRIVATE_EXPORTED_SDK_LIBS) | tr ' ' '\n' > $@,\ + $(hide) touch $@) + +endif # ! prebuilt_module_is_dex_javalib +endif # LOCAL_IS_HOST_MODULE is not set diff --git a/make/core/java_renderscript.mk b/make/core/java_renderscript.mk new file mode 100644 index 0000000..055ff14 --- /dev/null +++ b/make/core/java_renderscript.mk @@ -0,0 +1,156 @@ +############################################################### +## Renderscript support for java +## Adds rules to convert .rscript files to .java and .bc files +############################################################### + +renderscript_sources := $(filter %.rscript,$(LOCAL_SRC_FILES)) +LOCAL_SRC_FILES := $(filter-out %.rscript,$(LOCAL_SRC_FILES)) + +rs_generated_res_zip := +rs_generated_src_jar := +rs_compatibility_jni_libs := +ifneq ($(renderscript_sources),) +renderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources)) +renderscript_intermediate.COMMON := $(intermediates.COMMON)/renderscript +rs_generated_res_zip := $(renderscript_intermediate.COMMON)/res.zip +rs_generated_src_jar := $(renderscript_intermediate.COMMON)/rs.srcjar + +LOCAL_SRCJARS += $(rs_generated_src_jar) + +# Defaulting to an empty string uses the latest available platform SDK. +renderscript_target_api := + +ifneq (,$(LOCAL_RENDERSCRIPT_TARGET_API)) + renderscript_target_api := $(LOCAL_RENDERSCRIPT_TARGET_API) +else + ifneq (,$(LOCAL_SDK_VERSION)) + # Set target-api for LOCAL_SDK_VERSIONs other than current. + ifneq (,$(filter-out current system_current test_current core_current, $(LOCAL_SDK_VERSION))) + renderscript_target_api := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)) + endif + endif # LOCAL_SDK_VERSION is set +endif # LOCAL_RENDERSCRIPT_TARGET_API is set + +# For 64-bit, we always have to upgrade to at least 21 for compat build. +ifneq ($(LOCAL_RENDERSCRIPT_COMPATIBILITY),) + ifeq ($(TARGET_IS_64_BIT),true) + ifneq ($(filter $(RSCOMPAT_32BIT_ONLY_API_LEVELS),$(renderscript_target_api)),) + renderscript_target_api := 21 + endif + endif +endif + +ifeq ($(LOCAL_RENDERSCRIPT_CC),) +LOCAL_RENDERSCRIPT_CC := $(LLVM_RS_CC) +endif + +# Turn on all warnings and warnings as errors for RS compiles. +# This can be disabled with LOCAL_RENDERSCRIPT_FLAGS := -Wno-error +renderscript_flags := -Wall -Werror +renderscript_flags += $(LOCAL_RENDERSCRIPT_FLAGS) + +# prepend the RenderScript system include path +ifneq ($(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_VERSION))),) +# if a numeric LOCAL_SDK_VERSION, or current LOCAL_SDK_VERSION with TARGET_BUILD_USE_PREBUILT_SDKS +LOCAL_RENDERSCRIPT_INCLUDES := \ + $(HISTORICAL_SDK_VERSIONS_ROOT)/renderscript/clang-include \ + $(HISTORICAL_SDK_VERSIONS_ROOT)/renderscript/include \ + $(LOCAL_RENDERSCRIPT_INCLUDES) +else +LOCAL_RENDERSCRIPT_INCLUDES := \ + $(TOPDIR)external/clang/lib/Headers \ + $(TOPDIR)frameworks/rs/script_api/include \ + $(LOCAL_RENDERSCRIPT_INCLUDES) +endif + +ifneq ($(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE),) +LOCAL_RENDERSCRIPT_INCLUDES := $(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE) +endif + +bc_files := $(patsubst %.rscript,%.bc, $(notdir $(renderscript_sources))) +bc_dep_files := $(addprefix $(renderscript_intermediate.COMMON)/,$(patsubst %.bc,%.d,$(bc_files))) + +$(rs_generated_src_jar): PRIVATE_RS_INCLUDES := $(LOCAL_RENDERSCRIPT_INCLUDES) +$(rs_generated_src_jar): PRIVATE_RS_CC := $(LOCAL_RENDERSCRIPT_CC) +$(rs_generated_src_jar): PRIVATE_RS_FLAGS := $(renderscript_flags) +$(rs_generated_src_jar): PRIVATE_RS_SOURCE_FILES := $(renderscript_sources_fullpath) +$(rs_generated_src_jar): PRIVATE_RS_OUTPUT_DIR := $(renderscript_intermediate.COMMON) +$(rs_generated_src_jar): PRIVATE_RS_TARGET_API := $(patsubst current,0,$(renderscript_target_api)) +$(rs_generated_src_jar): PRIVATE_DEP_FILES := $(bc_dep_files) +$(rs_generated_src_jar): PRIVATE_RS_OUTPUT_RES_ZIP := $(rs_generated_res_zip) +$(rs_generated_src_jar): .KATI_IMPLICIT_OUTPUTS := $(rs_generated_res_zip) +$(rs_generated_src_jar): $(renderscript_sources_fullpath) $(LOCAL_RENDERSCRIPT_CC) $(SOONG_ZIP) + $(transform-renderscripts-to-java-and-bc) + +# include the dependency files (.d) generated by llvm-rs-cc. +$(call include-depfile,$(rs_generated_src_jar).d,$(rs_generated_src_jar)) + +ifneq ($(LOCAL_RENDERSCRIPT_COMPATIBILITY),) + + +ifeq ($(filter $(RSCOMPAT_32BIT_ONLY_API_LEVELS),$(renderscript_target_api)),) +ifeq ($(TARGET_IS_64_BIT),true) +renderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/bc64/ +else +renderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/bc32/ +endif +else +renderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/ +endif + +rs_generated_bc := $(addprefix \ + $(renderscript_intermediate.bc_folder), $(bc_files)) + +renderscript_intermediate := $(intermediates)/renderscript + +# We don't need the .so files in bundled branches +# Prevent these from showing up on the device +# One exception is librsjni.so, which is needed for +# both native path and compat path. +rs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni)/librsjni.so +LOCAL_JNI_SHARED_LIBRARIES += librsjni + +ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(FORCE_BUILD_RS_COMPAT)) + +rs_compatibility_jni_libs := $(addprefix \ + $(renderscript_intermediate)/librs., \ + $(patsubst %.bc,%.so, $(bc_files))) + +$(rs_generated_src_jar): .KATI_IMPLICIT_OUTPUTS += $(rs_generated_bc) + +rs_support_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so +LOCAL_JNI_SHARED_LIBRARIES += libRSSupport + +rs_support_io_lib := +# check if the target api level support USAGE_IO +ifeq ($(filter $(RSCOMPAT_NO_USAGEIO_API_LEVELS),$(renderscript_target_api)),) +rs_support_io_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupportIO)/libRSSupportIO.so +LOCAL_JNI_SHARED_LIBRARIES += libRSSupportIO +endif + +my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) +ifneq (,$(filter arm64 x86_64,$(my_arch))) + my_min_sdk_version := 21 +else + my_min_sdk_version := $(MIN_SUPPORTED_SDK_VERSION) +endif + +$(rs_compatibility_jni_libs): $(RS_PREBUILT_CLCORE) \ + $(rs_support_lib) $(rs_support_io_lib) $(rs_jni_lib) $(rs_compiler_rt) +$(rs_compatibility_jni_libs): $(BCC_COMPAT) +$(rs_compatibility_jni_libs): PRIVATE_CXX := $(CXX_WRAPPER) $(CLANG_CXX) +$(rs_compatibility_jni_libs): PRIVATE_CXX_LINK := $(CLANG_CXX) +$(rs_compatibility_jni_libs): PRIVATE_SDK_VERSION := $(my_min_sdk_version) +$(rs_compatibility_jni_libs): $(renderscript_intermediate)/librs.%.so: \ + $(renderscript_intermediate.bc_folder)%.bc \ + $(SOONG_OUT_DIR)/ndk.timestamp + $(transform-bc-to-so) + +endif + +endif + +LOCAL_INTERMEDIATE_TARGETS += $(rs_generated_src_jar) +# Make sure the generated resource will be added to the apk. +LOCAL_RESOURCE_DIR := $(renderscript_intermediate.COMMON)/res $(LOCAL_RESOURCE_DIR) +endif diff --git a/make/core/java_test_config_template.xml b/make/core/java_test_config_template.xml new file mode 100644 index 0000000..811cf93 --- /dev/null +++ b/make/core/java_test_config_template.xml @@ -0,0 +1,33 @@ + + + + + diff --git a/make/core/jetifier.mk b/make/core/jetifier.mk new file mode 100644 index 0000000..fff4230 --- /dev/null +++ b/make/core/jetifier.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This file sets up the running of Jetifier + +# now add the rule to run jetifier +ifeq ($(strip $(LOCAL_JETIFIER_ENABLED)),true) + my_jetifier_input_path := $(LOCAL_JETIFIER_INPUT_FILE) + my_files := $(intermediates.COMMON)/jetifier + my_jetifier_output_path := $(my_files)/jetified-$(notdir $(my_jetifier_input_path)) + +$(my_jetifier_output_path) : $(my_jetifier_input_path) $(JETIFIER) + rm -rf $@ + $(JETIFIER) -l error -o $@ -i $< + + LOCAL_JETIFIER_OUTPUT_FILE := $(my_jetifier_output_path) + LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_JETIFIER_OUTPUT_FILE) +else + LOCAL_JETIFIER_OUTPUT_FILE := $(LOCAL_JETIFIER_INPUT_FILE) +endif + diff --git a/make/core/layoutlib_fonts.mk b/make/core/layoutlib_fonts.mk new file mode 100644 index 0000000..d2a814f --- /dev/null +++ b/make/core/layoutlib_fonts.mk @@ -0,0 +1,35 @@ +# Fonts for layoutlib + +FONT_TEMP := $(call intermediates-dir-for,PACKAGING,fonts,HOST,COMMON) + +# The font configuration files - system_fonts.xml, fallback_fonts.xml etc. +font_config := $(sort $(wildcard frameworks/base/data/fonts/*.xml)) +font_config := $(addprefix $(FONT_TEMP)/, $(notdir $(font_config))) + +$(font_config): $(FONT_TEMP)/%.xml: \ + frameworks/base/data/fonts/%.xml + $(hide) mkdir -p $(dir $@) + $(hide) cp -vf $< $@ + +# List of fonts on the device that we want to ship. This is all .ttf, .ttc and .otf fonts. +fonts_device := $(filter $(TARGET_OUT)/fonts/%.ttf $(TARGET_OUT)/fonts/%.ttc $(TARGET_OUT)/fonts/%.otf, $(INTERNAL_SYSTEMIMAGE_FILES)) +fonts_device := $(addprefix $(FONT_TEMP)/, $(notdir $(fonts_device))) + +# TODO: If the font file is a symlink, reuse the font renamed from the symlink +# target. +$(fonts_device): $(FONT_TEMP)/%: $(TARGET_OUT)/fonts/% + $(hide) mkdir -p $(dir $@) + $(hide) cp -vf $< $@ + +# List of all dependencies - all fonts and configuration files. +FONT_FILES := $(fonts_device) $(font_config) + +.PHONY: layoutlib layoutlib-tests +layoutlib layoutlib-tests: $(FONT_FILES) + +$(call dist-for-goals, layoutlib, $(foreach m,$(FONT_FILES), $(m):layoutlib_native/fonts/$(notdir $(m)))) + +FONT_TEMP := +font_config := +fonts_device := +FONT_FILES := diff --git a/make/core/link_type.mk b/make/core/link_type.mk new file mode 100644 index 0000000..48cd8f3 --- /dev/null +++ b/make/core/link_type.mk @@ -0,0 +1,27 @@ +# Inputs: +# LOCAL_MODULE_CLASS, LOCAL_MODULE, LOCAL_MODULE_MAKEFILE, LOCAL_BUILT_MODULE +# from base_rules.mk: my_kind, my_host_cross +# my_common: empty or COMMON, like the argument to intermediates-dir-for +# my_2nd_arch_prefix: usually LOCAL_2ND_ARCH_VAR_PREFIX, separate for JNI installation +# +# my_link_type: the tags to apply to this module +# my_warn_types: the tags to warn about in our dependencies +# my_allowed_types: the tags to allow in our dependencies +# my_link_deps: the dependencies, in the form of : +# + +my_link_prefix := LINK_TYPE:$(call find-idf-prefix,$(my_kind),$(my_host_cross)):$(if $(my_common),$(my_common):_,_:$(if $(my_2nd_arch_prefix),$(my_2nd_arch_prefix),_)) +link_type := $(my_link_prefix):$(LOCAL_MODULE_CLASS):$(LOCAL_MODULE) +ALL_LINK_TYPES += $(link_type) +$(link_type).TYPE := $(my_link_type) +$(link_type).MAKEFILE := $(LOCAL_MODULE_MAKEFILE) +$(link_type).WARN := $(my_warn_types) +$(link_type).ALLOWED := $(my_allowed_types) +$(link_type).DEPS := $(addprefix $(my_link_prefix):,$(my_link_deps)) +$(link_type).BUILT := $(LOCAL_BUILT_MODULE) + +link_type := +my_allowed_types := +my_link_prefix := +my_link_type := +my_warn_types := diff --git a/make/core/local_current_sdk.mk b/make/core/local_current_sdk.mk new file mode 100644 index 0000000..ea7da8a --- /dev/null +++ b/make/core/local_current_sdk.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +ifdef BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES + ifneq (current,$(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)) + ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) + ifeq (current,$(LOCAL_SDK_VERSION)) + LOCAL_SDK_VERSION := $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES) + else ifeq (system_current,$(LOCAL_SDK_VERSION)) + LOCAL_SDK_VERSION := system_$(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES) + endif + endif + endif +endif diff --git a/make/core/local_systemsdk.mk b/make/core/local_systemsdk.mk new file mode 100644 index 0000000..460073d --- /dev/null +++ b/make/core/local_systemsdk.mk @@ -0,0 +1,64 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ifdef BOARD_SYSTEMSDK_VERSIONS + # Apps and jars in vendor, product or odm partition are forced to build against System SDK. + _cannot_use_platform_apis := + ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) + # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already + # set correctly before this is included. + _cannot_use_platform_apis := true + else ifeq ($(LOCAL_PRODUCT_MODULE),true) + ifeq ($(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE),true) + _cannot_use_platform_apis := true + endif + endif + ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS))) + ifndef LOCAL_SDK_VERSION + ifeq ($(_cannot_use_platform_apis),true) + ifeq (,$(LOCAL_IS_RUNTIME_RESOURCE_OVERLAY)) + # Runtime resource overlays are exempted from building against System SDK. + # TODO(b/155027019): remove this, after no product/vendor apps rely on this behavior. + LOCAL_SDK_VERSION := system_current + endif + endif + endif + endif +endif + +# Ensure that the selected System SDK version is one of the supported versions. +# The range of support versions becomes narrower when BOARD_SYSTEMSDK_VERSIONS +# is set, which is a subset of PLATFORM_SYSTEMSDK_VERSIONS. +ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) + ifneq ($(_cannot_use_platform_apis),true) + # apps bundled in system partition can use all system sdk versions provided by the platform + _supported_systemsdk_versions := $(PLATFORM_SYSTEMSDK_VERSIONS) + else ifdef BOARD_SYSTEMSDK_VERSIONS + # When BOARD_SYSTEMSDK_VERSIONS is set, vendors apps are restricted to use those versions + # which is equal to or smaller than PLATFORM_SYSTEMSDK_VERSIONS + _supported_systemsdk_versions := $(BOARD_SYSTEMSDK_VERSIONS) + else + # If not, vendor apks are treated equally to system apps + _supported_systemsdk_versions := $(PLATFORM_SYSTEMSDK_VERSIONS) + endif + _system_sdk_version := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)) + ifneq ($(_system_sdk_version),$(filter $(_system_sdk_version),$(_supported_systemsdk_versions))) + $(call pretty-error,Incompatible LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)'. \ + System SDK version '$(_system_sdk_version)' is not supported. Supported versions are: $(_supported_systemsdk_versions)) + endif + _system_sdk_version := + _supported_systemsdk_versions := +endif diff --git a/make/core/local_vndk.mk b/make/core/local_vndk.mk new file mode 100644 index 0000000..befbc59 --- /dev/null +++ b/make/core/local_vndk.mk @@ -0,0 +1,48 @@ + +#Set LOCAL_USE_VNDK for modules going into product, vendor or odm partition, except for host modules +#If LOCAL_SDK_VERSION is set, thats a more restrictive set, so they dont need LOCAL_USE_VNDK +ifndef LOCAL_IS_HOST_MODULE +ifndef LOCAL_SDK_VERSION + ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_OEM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) + LOCAL_USE_VNDK:=true + LOCAL_USE_VNDK_VENDOR:=true + # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already + # set correctly before this is included. + endif + ifdef PRODUCT_PRODUCT_VNDK_VERSION + # Product modules also use VNDK when PRODUCT_PRODUCT_VNDK_VERSION is defined. + ifeq (true,$(LOCAL_PRODUCT_MODULE)) + LOCAL_USE_VNDK:=true + LOCAL_USE_VNDK_PRODUCT:=true + endif + endif +endif +endif + +# Verify LOCAL_USE_VNDK usage, and set LOCAL_SDK_VERSION if necessary + +ifdef LOCAL_IS_HOST_MODULE + ifdef LOCAL_USE_VNDK + $(shell echo $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Do not use LOCAL_USE_VNDK with host modules >&2) + $(error done) + endif +endif +ifdef LOCAL_USE_VNDK + ifneq ($(LOCAL_USE_VNDK),true) + $(shell echo '$(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): LOCAL_USE_VNDK must be "true" or empty, not "$(LOCAL_USE_VNDK)"' >&2) + $(error done) + endif + + ifdef LOCAL_SDK_VERSION + $(shell echo $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): LOCAL_USE_VNDK must not be used with LOCAL_SDK_VERSION >&2) + $(error done) + endif + + # If we're not using the VNDK, drop all restrictions + ifndef BOARD_VNDK_VERSION + LOCAL_USE_VNDK:= + LOCAL_USE_VNDK_VENDOR:= + LOCAL_USE_VNDK_PRODUCT:= + endif +endif + diff --git a/make/core/main.mk b/make/core/main.mk new file mode 100644 index 0000000..a44477c --- /dev/null +++ b/make/core/main.mk @@ -0,0 +1,2032 @@ +ifndef KATI +$(warning Calling make directly is no longer supported.) +$(warning Either use 'envsetup.sh; m' or 'build/soong/soong_ui.bash --make-mode') +$(error done) +endif + +$(info [1/1] initializing build system ...) + +# Absolute path of the present working direcotry. +# This overrides the shell variable $PWD, which does not necessarily points to +# the top of the source tree, for example when "make -C" is used in m/mm/mmm. +PWD := $(shell pwd) + +# This is the default target. It must be the first declared target. +.PHONY: droid +DEFAULT_GOAL := droid +$(DEFAULT_GOAL): droid_targets + +# Include prebuild.mk +-include device/rockchip/common/prebuild.mk + +.PHONY: droid_targets +droid_targets: + +# Set up various standard variables based on configuration +# and host information. +include build/make/core/config.mk + +ifneq ($(filter $(dont_bother_goals), $(MAKECMDGOALS)),) +dont_bother := true +endif + +.KATI_READONLY := SOONG_CONFIG_NAMESPACES +.KATI_READONLY := $(foreach n,$(SOONG_CONFIG_NAMESPACES),SOONG_CONFIG_$(n)) +.KATI_READONLY := $(foreach n,$(SOONG_CONFIG_NAMESPACES),$(foreach k,$(SOONG_CONFIG_$(n)),SOONG_CONFIG_$(n)_$(k))) + +include $(SOONG_MAKEVARS_MK) + +YACC :=$= $(BISON) -d + +include $(BUILD_SYSTEM)/clang/config.mk + +# Write the build number to a file so it can be read back in +# without changing the command line every time. Avoids rebuilds +# when using ninja. +ifdef ROCKCHIP_BUILD_NUMBER +$(shell mkdir -p $(SOONG_OUT_DIR) && \ + echo -n $(ROCKCHIP_BUILD_NUMBER) > $(SOONG_OUT_DIR)/build_number.tmp; \ + if ! cmp -s $(SOONG_OUT_DIR)/build_number.tmp $(SOONG_OUT_DIR)/build_number.txt; then \ + mv $(SOONG_OUT_DIR)/build_number.tmp $(SOONG_OUT_DIR)/build_number.txt; \ + else \ + rm $(SOONG_OUT_DIR)/build_number.tmp; \ + fi) +else +$(shell mkdir -p $(SOONG_OUT_DIR) && \ + echo -n $(BUILD_NUMBER) > $(SOONG_OUT_DIR)/build_number.tmp; \ + if ! cmp -s $(SOONG_OUT_DIR)/build_number.tmp $(SOONG_OUT_DIR)/build_number.txt; then \ + mv $(SOONG_OUT_DIR)/build_number.tmp $(SOONG_OUT_DIR)/build_number.txt; \ + else \ + rm $(SOONG_OUT_DIR)/build_number.tmp; \ + fi) +endif +BUILD_NUMBER_FILE := $(SOONG_OUT_DIR)/build_number.txt +.KATI_READONLY := BUILD_NUMBER_FILE +$(KATI_obsolete_var BUILD_NUMBER,See https://android.googlesource.com/platform/build/+/master/Changes.md#BUILD_NUMBER) +$(BUILD_NUMBER_FILE): + touch $@ + +DATE_FROM_FILE := date -d @$(BUILD_DATETIME_FROM_FILE) +.KATI_READONLY := DATE_FROM_FILE + +# Pick a reasonable string to use to identify files. +ifeq ($(strip $(HAS_BUILD_NUMBER)),false) + # BUILD_NUMBER has a timestamp in it, which means that + # it will change every time. Pick a stable value. + FILE_NAME_TAG := eng.$(BUILD_USERNAME) +else + ifdef ROCKCHIP_BUILD_NUMBER + FILE_NAME_TAG := $(ROCKCHIP_BUILD_NUMBER) + else + FILE_NAME_TAG := $(file <$(BUILD_NUMBER_FILE)) + endif +endif +.KATI_READONLY := FILE_NAME_TAG + +# Make an empty directory, which can be used to make empty jars +EMPTY_DIRECTORY := $(OUT_DIR)/empty +$(shell mkdir -p $(EMPTY_DIRECTORY) && rm -rf $(EMPTY_DIRECTORY)/*) + +# CTS-specific config. +-include cts/build/config.mk +# VTS-specific config. +-include test/vts/tools/vts-tradefed/build/config.mk +# device-tests-specific-config. +-include tools/tradefederation/build/suites/device-tests/config.mk +# general-tests-specific-config. +-include tools/tradefederation/build/suites/general-tests/config.mk +# STS-specific config. +-include test/sts/tools/sts-tradefed/build/config.mk +# CTS-Instant-specific config +-include test/suite_harness/tools/cts-instant-tradefed/build/config.mk +# MTS-specific config. +-include test/mts/tools/build/config.mk +# VTS-Core-specific config. +-include test/vts/tools/vts-core-tradefed/build/config.mk +# CSUITE-specific config. +-include test/app_compat/csuite/tools/build/config.mk +# CATBox-specific config. +-include test/catbox/tools/build/config.mk +# CTS-Root-specific config. +-include test/cts-root/tools/build/config.mk + +# Clean rules +.PHONY: clean-dex-files +clean-dex-files: + $(hide) find $(OUT_DIR) -name "*.dex" | xargs rm -f + $(hide) for i in `find $(OUT_DIR) -name "*.jar" -o -name "*.apk"` ; do ((unzip -l $$i 2> /dev/null | \ + grep -q "\.dex$$" && rm -f $$i) || continue ) ; done + @echo "All dex files and archives containing dex files have been removed." + +# Include the google-specific config +-include vendor/google/build/config.mk + +# These are the modifier targets that don't do anything themselves, but +# change the behavior of the build. +# (must be defined before including definitions.make) +INTERNAL_MODIFIER_TARGETS := all + +# EMMA_INSTRUMENT_STATIC merges the static jacoco library to each +# jacoco-enabled module. +ifeq (true,$(EMMA_INSTRUMENT_STATIC)) +EMMA_INSTRUMENT := true +endif + +ifdef TARGET_ARCH_SUITE + # TODO(b/175577370): Enable this error. + # $(error TARGET_ARCH_SUITE is not supported in kati/make builds) +endif + +# ADDITIONAL__PROPERTIES are properties that are determined by the +# build system itself. Don't let it be defined from outside of the core build +# system like Android.mk or .mk files. +_additional_prop_var_names := \ + ADDITIONAL_SYSTEM_PROPERTIES \ + ADDITIONAL_VENDOR_PROPERTIES \ + ADDITIONAL_ODM_PROPERTIES \ + ADDITIONAL_PRODUCT_PROPERTIES + +$(foreach name, $(_additional_prop_var_names),\ + $(if $($(name)),\ + $(error $(name) must not set before here. $($(name)))\ + ,)\ + $(eval $(name) :=)\ +) +_additional_prop_var_names := + +$(KATI_obsolete_var ADDITIONAL_BUILD_PROPERTIES, Please use ADDITIONAL_SYSTEM_PROPERTIES) + +# +# ----------------------------------------------------------------- +# Add the product-defined properties to the build properties. +ifneq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true) + ADDITIONAL_SYSTEM_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES) +else + ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE + ADDITIONAL_SYSTEM_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES) + endif +endif + + +# Bring in standard build system definitions. +include $(BUILD_SYSTEM)/definitions.mk + +ifneq ($(filter user userdebug eng,$(MAKECMDGOALS)),) +$(info ***************************************************************) +$(info ***************************************************************) +$(info Do not pass '$(filter user userdebug eng,$(MAKECMDGOALS))' on \ + the make command line.) +$(info Set TARGET_BUILD_VARIANT in buildspec.mk, or use lunch or) +$(info choosecombo.) +$(info ***************************************************************) +$(info ***************************************************************) +$(error stopping) +endif + +# These are the valid values of TARGET_BUILD_VARIANT. +INTERNAL_VALID_VARIANTS := user userdebug eng +ifneq ($(filter-out $(INTERNAL_VALID_VARIANTS),$(TARGET_BUILD_VARIANT)),) +$(info ***************************************************************) +$(info ***************************************************************) +$(info Invalid variant: $(TARGET_BUILD_VARIANT)) +$(info Valid values are: $(INTERNAL_VALID_VARIANTS)) +$(info ***************************************************************) +$(info ***************************************************************) +$(error stopping) +endif + +# ----------------------------------------------------------------- +# PDK builds are no longer supported, this is always platform +TARGET_BUILD_JAVA_SUPPORT_LEVEL :=$= platform + +# ----------------------------------------------------------------- + +ADDITIONAL_SYSTEM_PROPERTIES += ro.treble.enabled=${PRODUCT_FULL_TREBLE} + +$(KATI_obsolete_var PRODUCT_FULL_TREBLE,\ + Code should be written to work regardless of a device being Treble or \ + variables like PRODUCT_SEPOLICY_SPLIT should be used until that is \ + possible.) + +# Sets ro.actionable_compatible_property.enabled to know on runtime whether the +# allowed list of actionable compatible properties is enabled or not. +ADDITIONAL_SYSTEM_PROPERTIES += ro.actionable_compatible_property.enabled=true + +# Add the system server compiler filter if they are specified for the product. +ifneq (,$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)) +ADDITIONAL_PRODUCT_PROPERTIES += dalvik.vm.systemservercompilerfilter=$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER) +endif + +# Enable core platform API violation warnings on userdebug and eng builds. +ifneq ($(TARGET_BUILD_VARIANT),user) +ADDITIONAL_SYSTEM_PROPERTIES += persist.debug.dalvik.vm.core_platform_api_policy=just-warn +endif + +# Define ro.sanitize. properties for all global sanitizers. +ADDITIONAL_SYSTEM_PROPERTIES += $(foreach s,$(SANITIZE_TARGET),ro.sanitize.$(s)=true) + +# Sets the default value of ro.postinstall.fstab.prefix to /system. +# Device board config should override the value to /product when needed by: +# +# PRODUCT_PRODUCT_PROPERTIES += ro.postinstall.fstab.prefix=/product +# +# It then uses ${ro.postinstall.fstab.prefix}/etc/fstab.postinstall to +# mount system_other partition. +ADDITIONAL_SYSTEM_PROPERTIES += ro.postinstall.fstab.prefix=/system + +# ----------------------------------------------------------------- +# ADDITIONAL_VENDOR_PROPERTIES will be installed in vendor/build.prop if +# property_overrides_split_enabled is true. Otherwise it will be installed in +# /system/build.prop +ifdef BOARD_VNDK_VERSION + ifeq ($(BOARD_VNDK_VERSION),current) + ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(PLATFORM_VNDK_VERSION) + else + ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(BOARD_VNDK_VERSION) + endif +endif + +# Add cpu properties for bionic and ART. +ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.arch=$(TARGET_ARCH) +ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.cpu_variant=$(TARGET_CPU_VARIANT_RUNTIME) +ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_arch=$(TARGET_2ND_ARCH) +ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_cpu_variant=$(TARGET_2ND_CPU_VARIANT_RUNTIME) + +ADDITIONAL_VENDOR_PROPERTIES += persist.sys.dalvik.vm.lib.2=libart.so +ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).variant=$(DEX2OAT_TARGET_CPU_VARIANT_RUNTIME) +ifneq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) + ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) +endif + +ifdef TARGET_2ND_ARCH + ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_2ND_ARCH).variant=$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME) + ifneq ($($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) + ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_2ND_ARCH).features=$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) + endif +endif + +# Although these variables are prefixed with TARGET_RECOVERY_, they are also needed under charger +# mode (via libminui). +ifdef TARGET_RECOVERY_DEFAULT_ROTATION +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.minui.default_rotation=$(TARGET_RECOVERY_DEFAULT_ROTATION) +endif +ifdef TARGET_RECOVERY_OVERSCAN_PERCENT +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.minui.overscan_percent=$(TARGET_RECOVERY_OVERSCAN_PERCENT) +endif +ifdef TARGET_RECOVERY_PIXEL_FORMAT +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.minui.pixel_format=$(TARGET_RECOVERY_PIXEL_FORMAT) +endif + +ifdef PRODUCT_USE_DYNAMIC_PARTITIONS +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.boot.dynamic_partitions=$(PRODUCT_USE_DYNAMIC_PARTITIONS) +endif + +ifdef PRODUCT_RETROFIT_DYNAMIC_PARTITIONS +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.boot.dynamic_partitions_retrofit=$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS) +endif + +ifdef PRODUCT_SHIPPING_API_LEVEL +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL) +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) + ifdef PRODUCT_SET_DEBUGFS_RESTRICTIONS + ADDITIONAL_VENDOR_PROPERTIES += \ + ro.product.debugfs_restrictions.enabled=$(PRODUCT_SET_DEBUGFS_RESTRICTIONS) + endif +endif + +# Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level. +# This must not be defined for the non-GRF devices. +ifdef BOARD_SHIPPING_API_LEVEL +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.board.first_api_level=$(BOARD_SHIPPING_API_LEVEL) + +# To manually set the vendor API level of the vendor modules, BOARD_API_LEVEL can be used. +# The values of the GRF properties will be verified by post_process_props.py +ifdef BOARD_API_LEVEL +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.board.api_level=$(BOARD_API_LEVEL) +endif +endif + +# Set build prop. This prop is read by ota_from_target_files when generating OTA, +# to decide if VABC should be disabled. +ifeq ($(BOARD_DONT_USE_VABC_OTA),true) +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.vendor.build.dont_use_vabc=true +endif + +# Set the flag in vendor. So VTS would know if the new fingerprint format is in use when +# the system images are replaced by GSI. +ifeq ($(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT),true) +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.vendor.build.fingerprint_has_digest=1 +endif + +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.vendor.build.security_patch=$(VENDOR_SECURITY_PATCH) \ + ro.product.board=$(TARGET_BOOTLOADER_BOARD_NAME) \ + ro.board.platform=$(TARGET_BOARD_PLATFORM) \ + ro.hwui.use_vulkan=$(TARGET_USES_VULKAN) + +ifdef TARGET_SCREEN_DENSITY +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.sf.lcd_density=$(TARGET_SCREEN_DENSITY) +endif + +ifdef AB_OTA_UPDATER +ADDITIONAL_VENDOR_PROPERTIES += \ + ro.build.ab_update=$(AB_OTA_UPDATER) +endif + +# Set ro.product.vndk.version to know the VNDK version required by product +# modules. It uses the version in PRODUCT_PRODUCT_VNDK_VERSION. If the value +# is "current", use PLATFORM_VNDK_VERSION. +ifdef PRODUCT_PRODUCT_VNDK_VERSION +ifeq ($(PRODUCT_PRODUCT_VNDK_VERSION),current) +ADDITIONAL_PRODUCT_PROPERTIES += ro.product.vndk.version=$(PLATFORM_VNDK_VERSION) +else +ADDITIONAL_PRODUCT_PROPERTIES += ro.product.vndk.version=$(PRODUCT_PRODUCT_VNDK_VERSION) +endif +endif + +ADDITIONAL_PRODUCT_PROPERTIES += ro.build.characteristics=$(TARGET_AAPT_CHARACTERISTICS) + +ifeq ($(AB_OTA_UPDATER),true) +ADDITIONAL_PRODUCT_PROPERTIES += ro.product.ab_ota_partitions=$(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS))) +endif + +# ----------------------------------------------------------------- +### +### In this section we set up the things that are different +### between the build variants +### + +is_sdk_build := + +ifneq ($(filter sdk sdk_addon,$(MAKECMDGOALS)),) +is_sdk_build := true +endif + +## user/userdebug ## + +user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT)) +enable_target_debugging := true +tags_to_install := +ifneq (,$(user_variant)) + # Target is secure in user builds. + ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=1 + ADDITIONAL_SYSTEM_PROPERTIES += security.perf_harden=1 + + ifeq ($(user_variant),user) + ADDITIONAL_SYSTEM_PROPERTIES += ro.adb.secure=1 + endif + + ifeq ($(user_variant),userdebug) + # Pick up some extra useful tools + tags_to_install += debug + else + # Disable debugging in plain user builds. + enable_target_debugging := + endif + + # Disallow mock locations by default for user builds + ADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=0 + +else # !user_variant + # Turn on checkjni for non-user builds. + ADDITIONAL_SYSTEM_PROPERTIES += ro.kernel.android.checkjni=1 + # Set device insecure for non-user builds. + ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=0 + # Allow mock locations by default for non user builds + ADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=1 +endif # !user_variant + +ifeq (true,$(strip $(enable_target_debugging))) + # Target is more debuggable and adbd is on by default + ADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=1 + # Enable Dalvik lock contention logging. + ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.lockprof.threshold=500 +else # !enable_target_debugging + # Target is less debuggable and adbd is off by default + ADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=0 +endif # !enable_target_debugging + +## eng ## + +ifeq ($(TARGET_BUILD_VARIANT),eng) +tags_to_install := debug eng +ifneq ($(filter ro.setupwizard.mode=ENABLED, $(call collapse-pairs, $(ADDITIONAL_SYSTEM_PROPERTIES))),) + # Don't require the setup wizard on eng builds + ADDITIONAL_SYSTEM_PROPERTIES := $(filter-out ro.setupwizard.mode=%,\ + $(call collapse-pairs, $(ADDITIONAL_SYSTEM_PROPERTIES))) \ + ro.setupwizard.mode=OPTIONAL +endif +ifndef is_sdk_build + # To speedup startup of non-preopted builds, don't verify or compile the boot image. + ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.image-dex2oat-filter=extract +endif +endif + +## asan ## + +# Install some additional tools on ASAN builds IFF we are also installing debug tools +ifneq ($(filter address,$(SANITIZE_TARGET)),) +ifneq (,$(filter debug,$(tags_to_install))) + tags_to_install += asan +endif +endif + +## java coverage ## +# Install additional tools on java coverage builds +ifeq (true,$(EMMA_INSTRUMENT)) +ifneq (,$(filter debug,$(tags_to_install))) + tags_to_install += java_coverage +endif +endif + + +## sdk ## + +ifdef is_sdk_build + +# Detect if we want to build a repository for the SDK +sdk_repo_goal := $(strip $(filter sdk_repo,$(MAKECMDGOALS))) +MAKECMDGOALS := $(strip $(filter-out sdk_repo,$(MAKECMDGOALS))) + +ifneq ($(words $(sort $(filter-out $(INTERNAL_MODIFIER_TARGETS) checkbuild emulator_tests,$(MAKECMDGOALS)))),1) +$(error The 'sdk' target may not be specified with any other targets) +endif + +# TODO: this should be eng I think. Since the sdk is built from the eng +# variant. +tags_to_install := debug eng +ADDITIONAL_SYSTEM_PROPERTIES += xmpp.auto-presence=true +ADDITIONAL_SYSTEM_PROPERTIES += ro.config.nocheckin=yes +else # !sdk +endif + +BUILD_WITHOUT_PV := true + +ADDITIONAL_SYSTEM_PROPERTIES += net.bt.name=Android + +# ------------------------------------------------------------ +# Define a function that, given a list of module tags, returns +# non-empty if that module should be installed in /system. + +# For most goals, anything not tagged with the "tests" tag should +# be installed in /system. +define should-install-to-system +$(if $(filter tests,$(1)),,true) +endef + +ifdef is_sdk_build +# For the sdk goal, anything with the "samples" tag should be +# installed in /data even if that module also has "eng"/"debug"/"user". +define should-install-to-system +$(if $(filter samples tests,$(1)),,true) +endef +endif + + +# If they only used the modifier goals (all, etc), we'll actually +# build the default target. +ifeq ($(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS)),) +.PHONY: $(INTERNAL_MODIFIER_TARGETS) +$(INTERNAL_MODIFIER_TARGETS): $(DEFAULT_GOAL) +endif + +# +# Typical build; include any Android.mk files we can find. +# + +# Bring in dex_preopt.mk +# This creates some modules so it needs to be included after +# should-install-to-system is defined (in order for base_rules.mk to function +# properly), but before readonly-final-product-vars is called. +include $(BUILD_SYSTEM)/dex_preopt.mk + +# Strip and readonly a few more variables so they won't be modified. +$(readonly-final-product-vars) +ADDITIONAL_SYSTEM_PROPERTIES := $(strip $(ADDITIONAL_SYSTEM_PROPERTIES)) +.KATI_READONLY := ADDITIONAL_SYSTEM_PROPERTIES +ADDITIONAL_PRODUCT_PROPERTIES := $(strip $(ADDITIONAL_PRODUCT_PROPERTIES)) +.KATI_READONLY := ADDITIONAL_PRODUCT_PROPERTIES + +ifneq ($(PRODUCT_ENFORCE_RRO_TARGETS),) +ENFORCE_RRO_SOURCES := +endif + +# Color-coded warnings including current module info +# $(1): message to print +define pretty-warning +$(shell $(call echo-warning,$(LOCAL_MODULE_MAKEFILE),$(LOCAL_MODULE): $(1))) +endef + +# Color-coded errors including current module info +# $(1): message to print +define pretty-error +$(shell $(call echo-error,$(LOCAL_MODULE_MAKEFILE),$(LOCAL_MODULE): $(1))) +$(error done) +endef + +subdir_makefiles_inc := . +FULL_BUILD := + +ifneq ($(dont_bother),true) +FULL_BUILD := true +# +# Include all of the makefiles in the system +# + +subdir_makefiles := $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT).mk $(SOONG_ANDROID_MK) +# Android.mk files are only used on Linux builds, Mac only supports Android.bp +ifeq ($(HOST_OS),linux) + subdir_makefiles += $(file <$(OUT_DIR)/.module_paths/Android.mk.list) +endif +subdir_makefiles += $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk +subdir_makefiles_total := $(words int $(subdir_makefiles) post finish) +.KATI_READONLY := subdir_makefiles_total + +$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk))) + +# For an unbundled image, we can skip blueprint_tools because unbundled image +# aims to remove a large number framework projects from the manifest, the +# sources or dependencies for these tools may be missing from the tree. +ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE)) +droid_targets : blueprint_tools +endif + +endif # dont_bother + +ifndef subdir_makefiles_total +subdir_makefiles_total := $(words init post finish) +endif + +$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] finishing build rules ...) + +# ------------------------------------------------------------------- +# All module makefiles have been included at this point. +# ------------------------------------------------------------------- + +# ------------------------------------------------------------------- +# Use basic warning/error messages now that LOCAL_MODULE_MAKEFILE +# and LOCAL_MODULE aren't useful anymore. +# ------------------------------------------------------------------- +define pretty-warning +$(warning $(1)) +endef + +define pretty-error +$(error $(1)) +endef + +# ------------------------------------------------------------------- +# Enforce to generate all RRO packages for modules having resource +# overlays. +# ------------------------------------------------------------------- +ifneq ($(PRODUCT_ENFORCE_RRO_TARGETS),) +$(call generate_all_enforce_rro_packages) +endif + +# ------------------------------------------------------------------- +# Sort ALL_MODULES to remove duplicate entries. +# ------------------------------------------------------------------- +ALL_MODULES := $(sort $(ALL_MODULES)) +# Cannot set to readonly because Makefile extends ALL_MODULES +# .KATI_READONLY := ALL_MODULES + +# ------------------------------------------------------------------- +# Fix up CUSTOM_MODULES to refer to installed files rather than +# just bare module names. Leave unknown modules alone in case +# they're actually full paths to a particular file. +known_custom_modules := $(filter $(ALL_MODULES),$(CUSTOM_MODULES)) +unknown_custom_modules := $(filter-out $(ALL_MODULES),$(CUSTOM_MODULES)) +CUSTOM_MODULES := \ + $(call module-installed-files,$(known_custom_modules)) \ + $(unknown_custom_modules) + +# ------------------------------------------------------------------- +# Define dependencies for modules that require other modules. +# This can only happen now, after we've read in all module makefiles. +# +# TODO: deal with the fact that a bare module name isn't +# unambiguous enough. Maybe declare short targets like +# APPS:Quake or HOST:SHARED_LIBRARIES:libutils. +# BUG: the system image won't know to depend on modules that are +# brought in as requirements of other modules. +# +# Resolve the required module name to 32-bit or 64-bit variant. + +# Get a list of corresponding module names for the second arch, if they exist. +# $(1): TARGET, HOST or HOST_CROSS +# $(2): A list of module names +define get-modules-for-2nd-arch +$(strip \ + $(foreach m,$(2), \ + $(if $(filter true,$(ALL_MODULES.$(m)$($(1)_2ND_ARCH_MODULE_SUFFIX).FOR_2ND_ARCH)), \ + $(m)$($(1)_2ND_ARCH_MODULE_SUFFIX) \ + ) \ + ) \ +) +endef + +# Resolves module bitness for PRODUCT_PACKAGES and PRODUCT_HOST_PACKAGES. +# The returned list of module names can be used to access +# ALL_MODULES..<*> variables. +# Name resolution for PRODUCT_PACKAGES / PRODUCT_HOST_PACKAGES: +# foo:32 resolves to foo_32; +# foo:64 resolves to foo; +# foo resolves to both foo and foo_32 (if foo_32 is defined). +# +# Name resolution for HOST_CROSS modules: +# foo:32 resolves to foo; +# foo:64 resolves to foo_64; +# foo resolves to both foo and foo_64 (if foo_64 is defined). +# +# $(1): TARGET, HOST or HOST_CROSS +# $(2): A list of simple module names with :32 and :64 suffix +define resolve-bitness-for-modules +$(strip \ + $(eval modules_32 := $(patsubst %:32,%,$(filter %:32,$(2)))) \ + $(eval modules_64 := $(patsubst %:64,%,$(filter %:64,$(2)))) \ + $(eval modules_both := $(filter-out %:32 %:64,$(2))) \ + $(eval ### if 2ND_HOST_CROSS_IS_64_BIT, then primary/secondary are reversed for HOST_CROSS modules) \ + $(if $(filter HOST_CROSS_true,$(1)_$(2ND_HOST_CROSS_IS_64_BIT)), \ + $(eval modules_1st_arch := $(modules_32)) \ + $(eval modules_2nd_arch := $(modules_64)), \ + $(eval modules_1st_arch := $(modules_64)) \ + $(eval modules_2nd_arch := $(modules_32))) \ + $(eval ### Note for 32-bit product, 32 and 64 will be added as their original module names.) \ + $(eval modules := $(modules_1st_arch)) \ + $(if $($(1)_2ND_ARCH), \ + $(eval modules += $(call get-modules-for-2nd-arch,$(1),$(modules_2nd_arch))), \ + $(eval modules += $(modules_2nd_arch))) \ + $(eval ### For the rest we add both) \ + $(eval modules += $(modules_both)) \ + $(if $($(1)_2ND_ARCH), \ + $(eval modules += $(call get-modules-for-2nd-arch,$(1),$(modules_both)))) \ + $(modules) \ +) +endef + +# Resolve the required module names to 32-bit or 64-bit variant for: +# ALL_MODULES.<*>.REQUIRED_FROM_TARGET +# ALL_MODULES.<*>.REQUIRED_FROM_HOST +# ALL_MODULES.<*>.REQUIRED_FROM_HOST_CROSS +# +# If a module is for cross host OS, the required modules are also for that OS. +# Required modules explicitly suffixed with :32 or :64 resolve to that bitness. +# Otherwise if the requiring module is native and the required module is shared +# library or native test, then the required module resolves to the same bitness. +# Otherwise the required module resolves to both variants, if they exist. +# $(1): TARGET, HOST or HOST_CROSS +define select-bitness-of-required-modules +$(foreach m,$(ALL_MODULES), \ + $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_$(1))) \ + $(if $(r), \ + $(if $(filter HOST_CROSS,$(1)), \ + $(if $(ALL_MODULES.$(m).FOR_HOST_CROSS),,$(error Only expected REQUIRED_FROM_HOST_CROSS on FOR_HOST_CROSS modules - $(m))) \ + $(eval r := $(addprefix host_cross_,$(r)))) \ + $(eval module_is_native := \ + $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(ALL_MODULES.$(m).CLASS))) \ + $(eval r_r := \ + $(foreach r_i,$(r), \ + $(if $(filter %:32 %:64,$(r_i)), \ + $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))), \ + $(eval r_m := \ + $(eval r_i_2nd := $(call get-modules-for-2nd-arch,$(1),$(r_i))) \ + $(eval required_is_shared_library_or_native_test := \ + $(filter SHARED_LIBRARIES NATIVE_TESTS, \ + $(ALL_MODULES.$(r_i).CLASS) $(ALL_MODULES.$(r_i_2nd).CLASS))) \ + $(if $(and $(module_is_native),$(required_is_shared_library_or_native_test)), \ + $(if $(ALL_MODULES.$(m).FOR_2ND_ARCH),$(r_i_2nd),$(r_i)), \ + $(r_i) $(r_i_2nd)))) \ + $(eval r_m := $(foreach r_j,$(r_m),$(if $(ALL_MODULES.$(r_j).PATH),$(r_j)))) \ + $(if $(r_m),,$(eval _nonexistent_required += $(1)$(comma)$(m)$(comma)$(1)$(comma)$(r_i))) \ + $(r_m))) \ + $(eval ALL_MODULES.$(m).REQUIRED_FROM_$(1) := $(sort $(r_r))) \ + ) \ +) +endef + +# Resolve the required module names to 32-bit or 64-bit variant for: +# ALL_MODULES.<*>.TARGET_REQUIRED_FROM_HOST +# ALL_MODULES.<*>.HOST_REQUIRED_FROM_TARGET +# +# This is like select-bitness-of-required-modules, but it doesn't have +# complicated logic for various module types. +# Calls resolve-bitness-for-modules to resolve module names. +# $(1): TARGET or HOST +# $(2): HOST or TARGET +define select-bitness-of-target-host-required-modules +$(foreach m,$(ALL_MODULES), \ + $(eval r := $(ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2))) \ + $(if $(r), \ + $(eval r_r := \ + $(foreach r_i,$(r), \ + $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))) \ + $(eval r_m := $(foreach r_j,$(r_m),$(if $(ALL_MODULES.$(r_j).PATH),$(r_j)))) \ + $(if $(r_m),,$(eval _nonexistent_required += $(2)$(comma)$(m)$(comma)$(1)$(comma)$(r_i))) \ + $(r_m))) \ + $(eval ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2) := $(sort $(r_r))) \ + ) \ +) +endef + +_nonexistent_required := +$(call select-bitness-of-required-modules,TARGET) +$(call select-bitness-of-required-modules,HOST) +$(call select-bitness-of-required-modules,HOST_CROSS) +$(call select-bitness-of-target-host-required-modules,TARGET,HOST) +$(call select-bitness-of-target-host-required-modules,HOST,TARGET) +_nonexistent_required := $(sort $(_nonexistent_required)) + +check_missing_required_modules := true +ifneq (,$(filter true,$(ALLOW_MISSING_DEPENDENCIES) $(BUILD_BROKEN_MISSING_REQUIRED_MODULES))) + check_missing_required_modules := +endif # ALLOW_MISSING_DEPENDENCIES == true || BUILD_BROKEN_MISSING_REQUIRED_MODULES == true + +# Some executables are skipped in ASAN SANITIZE_TARGET build, thus breaking their dependencies. +ifneq (,$(filter address,$(SANITIZE_TARGET))) + check_missing_required_modules := +endif # SANITIZE_TARGET has ASAN + +# HOST OS darwin build is broken, disable this check for darwin for now. +# TODO(b/162102724): Remove this when darwin host has no broken dependency. +ifneq (,$(filter $(HOST_OS),darwin)) + check_missing_required_modules := +endif # HOST_OS == darwin + +ifeq (true,$(check_missing_required_modules)) +ifneq (,$(_nonexistent_required)) + $(warning Missing required dependencies:) + $(foreach r_i,$(_nonexistent_required), \ + $(eval r := $(subst $(comma),$(space),$(r_i))) \ + $(info $(word 1,$(r)) module $(word 2,$(r)) requires non-existent $(word 3,$(r)) module: $(word 4,$(r))) \ + ) + $(warning Set BUILD_BROKEN_MISSING_REQUIRED_MODULES := true to bypass this check if this is intentional) + $(error Build failed) +endif # _nonexistent_required != empty +endif # check_missing_required_modules == true + +define add-required-deps +$(1): | $(2) +endef + +# Use a normal dependency instead of an order-only dependency when installing +# host dynamic binaries so that the timestamp of the final binary always +# changes, even if the toc optimization has skipped relinking the binary +# and its dependant shared libraries. +define add-required-host-so-deps +$(1): $(2) +endef + +# Sets up dependencies such that whenever a host module is installed, +# any other host modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_HOST) will also be installed +define add-all-host-to-host-required-modules-deps +$(foreach m,$(ALL_MODULES), \ + $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_HOST)) \ + $(if $(r), \ + $(eval r := $(call module-installed-files,$(r))) \ + $(eval h_m := $(filter $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \ + $(eval h_r := $(filter $(HOST_OUT)/%, $(r))) \ + $(eval h_r := $(filter-out $(h_m), $(h_r))) \ + $(if $(h_m), $(eval $(call add-required-deps, $(h_m),$(h_r)))) \ + ) \ +) +endef +$(call add-all-host-to-host-required-modules-deps) + +# Sets up dependencies such that whenever a host cross module is installed, +# any other host cross modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_HOST_CROSS) will also be installed +define add-all-host-cross-to-host-cross-required-modules-deps +$(foreach m,$(ALL_MODULES), \ + $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_HOST_CROSS)) \ + $(if $(r), \ + $(eval r := $(call module-installed-files,$(r))) \ + $(eval hc_m := $(filter $(HOST_CROSS_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \ + $(eval hc_r := $(filter $(HOST_CROSS_OUT)/%, $(r))) \ + $(eval hc_r := $(filter-out $(hc_m), $(hc_r))) \ + $(if $(hc_m), $(eval $(call add-required-deps, $(hc_m),$(hc_r)))) \ + ) \ +) +endef +$(call add-all-host-cross-to-host-cross-required-modules-deps) + +# Sets up dependencies such that whenever a target module is installed, +# any other target modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET) will also be installed +define add-all-target-to-target-required-modules-deps +$(foreach m,$(ALL_MODULES), \ + $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET)) \ + $(if $(r), \ + $(eval r := $(call module-installed-files,$(r))) \ + $(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \ + $(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \ + $(eval t_r := $(filter-out $(t_m), $(t_r))) \ + $(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \ + ) \ +) +endef +$(call add-all-target-to-target-required-modules-deps) + +# Sets up dependencies such that whenever a host module is installed, +# any target modules listed in $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST) will also be installed +define add-all-host-to-target-required-modules-deps +$(foreach m,$(ALL_MODULES), \ + $(eval req_mods := $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))\ + $(if $(req_mods), \ + $(eval req_files := )\ + $(foreach req_mod,$(req_mods), \ + $(eval req_file := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(req_mod)))) \ + $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \ + , \ + $(if $(strip $(req_file)), \ + , \ + $(error $(m).LOCAL_TARGET_REQUIRED_MODULES : illegal value $(req_mod) : not a device module. If you want to specify host modules to be required to be installed along with your host module, add those module names to LOCAL_REQUIRED_MODULES instead) \ + ) \ + ) \ + $(eval req_files := $(req_files)$(space)$(req_file))\ + )\ + $(eval req_files := $(strip $(req_files)))\ + $(eval mod_files := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(m)))) \ + $(if $(mod_files),\ + $(eval $(call add-required-deps, $(mod_files),$(req_files))) \ + )\ + )\ +) +endef +$(call add-all-host-to-target-required-modules-deps) + +# Sets up dependencies such that whenever a target module is installed, +# any host modules listed in $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET) will also be installed +define add-all-target-to-host-required-modules-deps +$(foreach m,$(ALL_MODULES), \ + $(eval req_mods := $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))\ + $(if $(req_mods), \ + $(eval req_files := )\ + $(foreach req_mod,$(req_mods), \ + $(eval req_file := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(req_mod)))) \ + $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \ + , \ + $(if $(strip $(req_file)), \ + , \ + $(error $(m).LOCAL_HOST_REQUIRED_MODULES : illegal value $(req_mod) : not a host module. If you want to specify target modules to be required to be installed along with your target module, add those module names to LOCAL_REQUIRED_MODULES instead) \ + ) \ + ) \ + $(eval req_files := $(req_files)$(space)$(req_file))\ + )\ + $(eval req_files := $(strip $(req_files)))\ + $(eval mod_files := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(m))))\ + $(if $(mod_files),\ + $(eval $(call add-required-deps, $(mod_files),$(req_files))) \ + )\ + )\ +) +endef +$(call add-all-target-to-host-required-modules-deps) + +t_m := +h_m := +hc_m := +t_r := +h_r := +hc_r := + +# Establish the dependencies on the shared libraries. +# It also adds the shared library module names to ALL_MODULES.$(m).REQUIRED_FROM_(TARGET|HOST|HOST_CROSS), +# so they can be expanded to product_MODULES later. +# $(1): TARGET_ or HOST_ or HOST_CROSS_. +# $(2): non-empty for 2nd arch. +# $(3): non-empty for host cross compile. +define resolve-shared-libs-depes +$(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\ + $(eval p := $(subst :,$(space),$(m)))\ + $(eval mod := $(firstword $(p)))\ + $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\ + $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\ + $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\ + $(if $(3),$(eval deps := $(addprefix host_cross_,$(deps))))\ + $(eval r := $(filter $($(root))/%,$(call module-installed-files,\ + $(deps))))\ + $(if $(filter $(1),HOST_),\ + $(eval ALL_MODULES.$(mod).HOST_SHARED_LIBRARY_FILES := $$(ALL_MODULES.$(mod).HOST_SHARED_LIBRARY_FILES) $(word 2,$(p)) $(r))\ + $(eval ALL_MODULES.$(mod).HOST_SHARED_LIBRARIES := $$(ALL_MODULES.$(mod).HOST_SHARED_LIBRARIES) $(deps))\ + $(eval $(call add-required-host-so-deps,$(word 2,$(p)),$(r))),\ + $(eval $(call add-required-deps,$(word 2,$(p)),$(r))))\ + $(eval ALL_MODULES.$(mod).REQUIRED_FROM_$(patsubst %_,%,$(1)) += $(deps))) +endef + +# Recursively resolve host shared library dependency for a given module. +# $(1): module name +# Returns all dependencies of shared library. +define get-all-shared-libs-deps +$(if $(_all_deps_for_$(1)_set_),$(_all_deps_for_$(1)_),\ + $(eval _all_deps_for_$(1)_ :=) \ + $(foreach dep,$(ALL_MODULES.$(1).HOST_SHARED_LIBRARIES),\ + $(foreach m,$(call get-all-shared-libs-deps,$(dep)),\ + $(eval _all_deps_for_$(1)_ := $$(_all_deps_for_$(1)_) $(m))\ + $(eval _all_deps_for_$(1)_ := $(sort $(_all_deps_for_$(1)_))))\ + $(eval _all_deps_for_$(1)_ := $$(_all_deps_for_$(1)_) $(dep))\ + $(eval _all_deps_for_$(1)_ := $(sort $(_all_deps_for_$(1)_) $(dep)))\ + $(eval _all_deps_for_$(1)_set_ := true))\ +$(_all_deps_for_$(1)_)) +endef + +# Scan all modules in general-tests, device-tests and other selected suites and +# flatten the shared library dependencies. +define update-host-shared-libs-deps-for-suites +$(foreach suite,general-tests device-tests vts tvts art-host-tests host-unit-tests,\ + $(foreach m,$(COMPATIBILITY.$(suite).MODULES),\ + $(eval my_deps := $(call get-all-shared-libs-deps,$(m)))\ + $(foreach dep,$(my_deps),\ + $(foreach f,$(ALL_MODULES.$(dep).HOST_SHARED_LIBRARY_FILES),\ + $(if $(filter $(suite),device-tests general-tests),\ + $(eval my_testcases := $(HOST_OUT_TESTCASES)),\ + $(eval my_testcases := $$(COMPATIBILITY_TESTCASES_OUT_$(suite))))\ + $(eval target := $(my_testcases)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\ + $(if $(strip $(ALL_TARGETS.$(target).META_LIC)),,$(eval ALL_TARGETS.$(target).META_LIC:=$(module_license_metadata)))\ + $(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \ + $$(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES) $(f):$(target))\ + $(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \ + $(sort $(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES))))))) +endef + +$(call resolve-shared-libs-depes,TARGET_) +ifdef TARGET_2ND_ARCH +$(call resolve-shared-libs-depes,TARGET_,true) +endif +$(call resolve-shared-libs-depes,HOST_) +ifdef HOST_2ND_ARCH +$(call resolve-shared-libs-depes,HOST_,true) +endif +# Update host side shared library dependencies for tests in suite device-tests and general-tests. +# This should be called after calling resolve-shared-libs-depes for HOST_2ND_ARCH. +$(call update-host-shared-libs-deps-for-suites) +ifdef HOST_CROSS_OS +$(call resolve-shared-libs-depes,HOST_CROSS_,,true) +ifdef HOST_CROSS_2ND_ARCH +$(call resolve-shared-libs-depes,HOST_CROSS_,true,true) +endif +endif + +# Pass the shared libraries dependencies to prebuilt ELF file check. +define add-elf-file-check-shared-lib +$(1): PRIVATE_SHARED_LIBRARY_FILES += $(2) +$(1): $(2) +endef + +define resolve-shared-libs-for-elf-file-check +$(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\ + $(eval p := $(subst :,$(space),$(m)))\ + $(eval mod := $(firstword $(p)))\ + \ + $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\ + $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\ + $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\ + $(eval deps := $(filter $($(root))/%$($(1)SHLIB_SUFFIX),$(call module-built-files,$(deps))))\ + \ + $(eval r := $(firstword $(filter \ + $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/EXECUTABLES/%\ + $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/NATIVE_TESTS/%\ + $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/SHARED_LIBRARIES/%,\ + $(call module-built-files,$(mod)))))\ + \ + $(if $(and $(r),$(deps)),\ + $(eval stamp := $(dir $(r))check_elf_files.timestamp)\ + $(if $(CHECK_ELF_FILES.$(stamp)),\ + $(eval $(call add-elf-file-check-shared-lib,$(stamp),$(deps))))\ + )) +endef + +$(call resolve-shared-libs-for-elf-file-check,TARGET_) +ifdef TARGET_2ND_ARCH +$(call resolve-shared-libs-for-elf-file-check,TARGET_,true) +endif + +m := +r := +p := +stamp := +deps := +add-required-deps := + +################################################################################ +# Link type checking +# +# ALL_LINK_TYPES contains a list of all link type prefixes (generally one per +# module, but APKs can "link" to both java and native code). The link type +# prefix consists of all the information needed by intermediates-dir-for: +# +# LINK_TYPE:TARGET:_:2ND:STATIC_LIBRARIES:libfoo +# +# 1: LINK_TYPE literal +# 2: prefix +# - TARGET +# - HOST +# - HOST_CROSS +# 3: Whether to use the common intermediates directory or not +# - _ +# - COMMON +# 4: Whether it's the second arch or not +# - _ +# - 2ND_ +# 5: Module Class +# - STATIC_LIBRARIES +# - SHARED_LIBRARIES +# - ... +# 6: Module Name +# +# Then fields under that are separated by a period and the field name: +# - TYPE: the link types for this module +# - MAKEFILE: Where this module was defined +# - BUILT: The built module location +# - DEPS: the link type prefixes for the module's dependencies +# - ALLOWED: the link types to allow in this module's dependencies +# - WARN: the link types to warn about in this module's dependencies +# +# All of the dependency link types not listed in ALLOWED or WARN will become +# errors. +################################################################################ + +link_type_error := + +define link-type-prefix +$(word 2,$(subst :,$(space),$(1))) +endef +define link-type-common +$(patsubst _,,$(word 3,$(subst :,$(space),$(1)))) +endef +define link-type-2ndarchprefix +$(patsubst _,,$(word 4,$(subst :,$(space),$(1)))) +endef +define link-type-class +$(word 5,$(subst :,$(space),$(1))) +endef +define link-type-name +$(word 6,$(subst :,$(space),$(1))) +endef +define link-type-os +$(strip $(eval _p := $(link-type-prefix))\ + $(if $(filter HOST HOST_CROSS,$(_p)),\ + $($(_p)_OS),\ + android)) +endef +define link-type-arch +$($(link-type-prefix)_$(link-type-2ndarchprefix)ARCH) +endef +define link-type-name-variant +$(link-type-name) ($(link-type-class) $(link-type-os)-$(link-type-arch)) +endef + +# $(1): the prefix of the module doing the linking +# $(2): the prefix of the linked module +define link-type-warning +$(shell $(call echo-warning,$($(1).MAKEFILE),"$(call link-type-name,$(1)) ($($(1).TYPE)) should not link against $(call link-type-name,$(2)) ($(3))")) +endef + +# $(1): the prefix of the module doing the linking +# $(2): the prefix of the linked module +define link-type-error +$(shell $(call echo-error,$($(1).MAKEFILE),"$(call link-type-name,$(1)) ($($(1).TYPE)) can not link against $(call link-type-name,$(2)) ($(3))"))\ +$(eval link_type_error := true) +endef + +link-type-missing := +ifneq ($(ALLOW_MISSING_DEPENDENCIES),true) + # Print an error message if the linked-to module is missing + # $(1): the prefix of the module doing the linking + # $(2): the prefix of the missing module + define link-type-missing + $(shell $(call echo-error,$($(1).MAKEFILE),"$(call link-type-name-variant,$(1)) missing $(call link-type-name-variant,$(2))"))\ + $(eval available_variants := $(filter %:$(call link-type-name,$(2)),$(ALL_LINK_TYPES)))\ + $(if $(available_variants),\ + $(info Available variants:)\ + $(foreach v,$(available_variants),$(info $(space)$(space)$(call link-type-name-variant,$(v)))))\ + $(info You can set ALLOW_MISSING_DEPENDENCIES=true in your environment if this is intentional, but that may defer real problems until later in the build.)\ + $(eval link_type_error := true) + endef +else + define link-type-missing + $(eval $$(1).MISSING := true) + endef +endif + +# Verify that $(1) can link against $(2) +# Both $(1) and $(2) are the link type prefix defined above +define verify-link-type +$(foreach t,$($(2).TYPE),\ + $(if $(filter-out $($(1).ALLOWED),$(t)),\ + $(if $(filter $(t),$($(1).WARN)),\ + $(call link-type-warning,$(1),$(2),$(t)),\ + $(call link-type-error,$(1),$(2),$(t))))) +endef + +$(foreach lt,$(ALL_LINK_TYPES),\ + $(foreach d,$($(lt).DEPS),\ + $(if $($(d).TYPE),\ + $(call verify-link-type,$(lt),$(d)),\ + $(call link-type-missing,$(lt),$(d))))) + +ifdef link_type_error + $(error exiting from previous errors) +endif + +# ------------------------------------------------------------------- +# Handle exported/imported includes + +# Recursively calculate flags +$(foreach export,$(EXPORTS_LIST), \ + $(eval EXPORTS.$$(export) = $$(EXPORTS.$(export).FLAGS) \ + $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORTS.$(dep))))) + +# Recursively calculate dependencies +$(foreach export,$(EXPORTS_LIST), \ + $(eval EXPORT_DEPS.$$(export) = $$(EXPORTS.$(export).DEPS) \ + $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORT_DEPS.$(dep))))) + +# Converts the recursive variables to simple variables so that we don't have to +# evaluate them for every .o rule +$(foreach export,$(EXPORTS_LIST),$(eval EXPORTS.$$(export) := $$(strip $$(EXPORTS.$$(export))))) +$(foreach export,$(EXPORTS_LIST),$(eval EXPORT_DEPS.$$(export) := $$(sort $$(EXPORT_DEPS.$$(export))))) + +# Add dependencies +$(foreach export,$(EXPORTS_LIST),$(eval $(call add-dependency,$$(EXPORTS.$$(export).USERS),$$(EXPORT_DEPS.$$(export))))) + +# ------------------------------------------------------------------- +# Figure out our module sets. +# +# Of the modules defined by the component makefiles, +# determine what we actually want to build. + + +# Expand a list of modules to the modules that they override (if any) +# $(1): The list of modules. +define module-overrides +$(foreach m,$(1),\ + $(eval _mo_overrides := $(PACKAGES.$(m).OVERRIDES) $(EXECUTABLES.$(m).OVERRIDES) $(SHARED_LIBRARIES.$(m).OVERRIDES) $(ETC.$(m).OVERRIDES))\ + $(if $(filter $(m),$(_mo_overrides)),\ + $(error Module $(m) cannot override itself),\ + $(_mo_overrides))) +endef + +########################################################### +## Expand a module name list with REQUIRED modules +########################################################### +# $(1): The variable name that holds the initial module name list. +# the variable will be modified to hold the expanded results. +# $(2): The initial module name list. +# $(3): The list of overridden modules. +# Returns empty string (maybe with some whitespaces). +define expand-required-modules +$(eval _erm_req := $(foreach m,$(2),$(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))) \ +$(eval _erm_new_modules := $(sort $(filter-out $($(1)),$(_erm_req)))) \ +$(eval _erm_new_overrides := $(call module-overrides,$(_erm_new_modules))) \ +$(eval _erm_all_overrides := $(3) $(_erm_new_overrides)) \ +$(eval _erm_new_modules := $(filter-out $(_erm_all_overrides), $(_erm_new_modules))) \ +$(eval $(1) := $(filter-out $(_erm_new_overrides),$($(1)))) \ +$(eval $(1) += $(_erm_new_modules)) \ +$(if $(_erm_new_modules),\ + $(call expand-required-modules,$(1),$(_erm_new_modules),$(_erm_all_overrides))) +endef + +# Same as expand-required-modules above, but does not handle module overrides, as +# we don't intend to support them on the host. +# $(1): The variable name that holds the initial module name list. +# the variable will be modified to hold the expanded results. +# $(2): The initial module name list. +# $(3): HOST or HOST_CROSS depending on whether we're expanding host or host cross modules +# Returns empty string (maybe with some whitespaces). +define expand-required-host-modules +$(eval _erm_req := $(foreach m,$(2),$(ALL_MODULES.$(m).REQUIRED_FROM_$(3)))) \ +$(eval _erm_new_modules := $(sort $(filter-out $($(1)),$(_erm_req)))) \ +$(eval $(1) += $(_erm_new_modules)) \ +$(if $(_erm_new_modules),\ + $(call expand-required-host-modules,$(1),$(_erm_new_modules),$(3))) +endef + +# Transforms paths relative to PRODUCT_OUT to absolute paths. +# $(1): list of relative paths +# $(2): optional suffix to append to paths +define resolve-product-relative-paths + $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),\ + $(subst $(_product_path_placeholder),$(TARGET_COPY_OUT_PRODUCT),\ + $(subst $(_system_ext_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_EXT),\ + $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),\ + $(subst $(_vendor_dlkm_path_placeholder),$(TARGET_COPY_OUT_VENDOR_DLKM),\ + $(subst $(_odm_dlkm_path_placeholder),$(TARGET_COPY_OUT_ODM_DLKM),\ + $(subst $(_system_dlkm_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_DLKM),\ + $(foreach p,$(1),$(call append-path,$(PRODUCT_OUT),$(p)$(2)))))))))) +endef + +# Returns modules included automatically as a result of certain BoardConfig +# variables being set. +define auto-included-modules + $(if $(BOARD_VNDK_VERSION),vndk_package) \ + $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \ + $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \ + $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \ + $(if $(ODM_MANIFEST_SKUS),$(foreach sku, $(ODM_MANIFEST_SKUS),odm_manifest_$(sku).xml)) \ + +endef + +# Lists most of the files a particular product installs, including: +# - PRODUCT_PACKAGES, and their LOCAL_REQUIRED_MODULES +# - PRODUCT_COPY_FILES +# The base list of modules to build for this product is specified +# by the appropriate product definition file, which was included +# by product_config.mk. +# Name resolution for PRODUCT_PACKAGES: +# foo:32 resolves to foo_32; +# foo:64 resolves to foo; +# foo resolves to both foo and foo_32 (if foo_32 is defined). +# +# Name resolution for LOCAL_REQUIRED_MODULES: +# See the select-bitness-of-required-modules definition. +# $(1): product makefile + +define product-installed-files + $(eval _pif_modules := \ + $(call get-product-var,$(1),PRODUCT_PACKAGES) \ + $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \ + $(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \ + $(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \ + $(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \ + $(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \ + $(call auto-included-modules) \ + ) \ + $(eval ### Filter out the overridden packages and executables before doing expansion) \ + $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \ + $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \ + $(eval ### Resolve the :32 :64 module name) \ + $(eval _pif_modules := $(sort $(call resolve-bitness-for-modules,TARGET,$(_pif_modules)))) \ + $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \ + $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \ + $(call resolve-product-relative-paths,\ + $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf)))) +endef + +# Similar to product-installed-files above, but handles PRODUCT_HOST_PACKAGES instead +# This does support the :32 / :64 syntax, but does not support module overrides. +define host-installed-files + $(eval _hif_modules := $(call get-product-var,$(1),PRODUCT_HOST_PACKAGES)) \ + $(eval ### Split host vs host cross modules) \ + $(eval _hcif_modules := $(filter host_cross_%,$(_hif_modules))) \ + $(eval _hif_modules := $(filter-out host_cross_%,$(_hif_modules))) \ + $(eval ### Resolve the :32 :64 module name) \ + $(eval _hif_modules := $(sort $(call resolve-bitness-for-modules,HOST,$(_hif_modules)))) \ + $(eval _hcif_modules := $(sort $(call resolve-bitness-for-modules,HOST_CROSS,$(_hcif_modules)))) \ + $(call expand-required-host-modules,_hif_modules,$(_hif_modules),HOST) \ + $(call expand-required-host-modules,_hcif_modules,$(_hcif_modules),HOST_CROSS) \ + $(filter $(HOST_OUT)/%,$(call module-installed-files, $(_hif_modules))) \ + $(filter $(HOST_CROSS_OUT)/%,$(call module-installed-files, $(_hcif_modules))) +endef + +# Fails the build if the given list is non-empty, and prints it entries (stripping PRODUCT_OUT). +# $(1): list of files to print +# $(2): heading to print on failure +define maybe-print-list-and-error +$(if $(strip $(1)), \ + $(warning $(2)) \ + $(info Offending entries:) \ + $(foreach e,$(sort $(1)),$(info $(patsubst $(PRODUCT_OUT)/%,%,$(e)))) \ + $(error Build failed) \ +) +endef + +ifeq ($(HOST_OS),darwin) + # Target builds are not supported on Mac + product_target_FILES := + product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT)) +else ifdef FULL_BUILD + ifneq (true,$(ALLOW_MISSING_DEPENDENCIES)) + # Check to ensure that all modules in PRODUCT_PACKAGES exist (opt in per product) + ifeq (true,$(PRODUCT_ENFORCE_PACKAGES_EXIST)) + _allow_list := $(PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST) + _modules := $(PRODUCT_PACKAGES) + # Strip :32 and :64 suffixes + _modules := $(patsubst %:32,%,$(_modules)) + _modules := $(patsubst %:64,%,$(_modules)) + # Quickly check all modules in PRODUCT_PACKAGES exist. We check for the + # existence if either or the _32 variant. + _nonexistent_modules := $(foreach m,$(_modules), \ + $(if $(or $(ALL_MODULES.$(m).PATH),$(call get-modules-for-2nd-arch,TARGET,$(m))),,$(m))) + $(call maybe-print-list-and-error,$(filter-out $(_allow_list),$(_nonexistent_modules)),\ + $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_PACKAGES) + # TODO(b/182105280): Consider re-enabling this check when the ART modules + # have been cleaned up from the allowed_list in target/product/generic.mk. + #$(call maybe-print-list-and-error,$(filter-out $(_nonexistent_modules),$(_allow_list)),\ + # $(INTERNAL_PRODUCT) includes redundant allow list entries for non-existent PRODUCT_PACKAGES) + endif + + # Check to ensure that all modules in PRODUCT_HOST_PACKAGES exist + # + # Many host modules are Linux-only, so skip this check on Mac. If we ever have Mac-only modules, + # maybe it would make sense to have PRODUCT_HOST_PACKAGES_LINUX/_DARWIN? + ifneq ($(HOST_OS),darwin) + _modules := $(PRODUCT_HOST_PACKAGES) + # Strip :32 and :64 suffixes + _modules := $(patsubst %:32,%,$(_modules)) + _modules := $(patsubst %:64,%,$(_modules)) + _nonexistent_modules := $(foreach m,$(_modules),\ + $(if $(ALL_MODULES.$(m).REQUIRED_FROM_HOST)$(filter $(HOST_OUT_ROOT)/%,$(ALL_MODULES.$(m).INSTALLED)),,$(m))) + $(call maybe-print-list-and-error,$(_nonexistent_modules),\ + $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_HOST_PACKAGES) + endif + endif + + # Modules may produce only host installed files in unbundled builds. + ifeq (,$(TARGET_BUILD_UNBUNDLED)) + _modules := $(call resolve-bitness-for-modules,TARGET, \ + $(PRODUCT_PACKAGES) \ + $(PRODUCT_PACKAGES_DEBUG) \ + $(PRODUCT_PACKAGES_DEBUG_ASAN) \ + $(PRODUCT_PACKAGES_ENG) \ + $(PRODUCT_PACKAGES_TESTS)) + _host_modules := $(foreach m,$(_modules), \ + $(if $(ALL_MODULES.$(m).INSTALLED),\ + $(if $(filter-out $(HOST_OUT_ROOT)/%,$(ALL_MODULES.$(m).INSTALLED)),,\ + $(m)))) + $(call maybe-print-list-and-error,$(sort $(_host_modules)),\ + Host modules should be in PRODUCT_HOST_PACKAGES$(comma) not PRODUCT_PACKAGES) + endif + + product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT)) + product_target_FILES := $(call product-installed-files, $(INTERNAL_PRODUCT)) + # WARNING: The product_MODULES variable is depended on by external files. + product_MODULES := $(_pif_modules) + + # Verify the artifact path requirements made by included products. + is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true) + ifeq (,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS))) + include $(BUILD_SYSTEM)/artifact_path_requirements.mk + endif +else + # We're not doing a full build, and are probably only including + # a subset of the module makefiles. Don't try to build any modules + # requested by the product, because we probably won't have rules + # to build them. + product_target_FILES := + product_host_FILES := +endif + +# TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES +# and get rid of it from this list. +modules_to_install := $(sort \ + $(ALL_DEFAULT_INSTALLED_MODULES) \ + $(product_target_FILES) \ + $(product_host_FILES) \ + $(CUSTOM_MODULES) \ + ) + +ifdef FULL_BUILD +# +# Used by the cleanup logic in soong_ui to remove files that should no longer +# be installed. +# + +# Include all tests, so that we remove them from the test suites / testcase +# folders when they are removed. +test_files := $(foreach ts,$(ALL_COMPATIBILITY_SUITES),$(COMPATIBILITY.$(ts).FILES)) + +$(shell mkdir -p $(PRODUCT_OUT) $(HOST_OUT)) + +$(file >$(PRODUCT_OUT)/.installable_files$(if $(filter address,$(SANITIZE_TARGET)),_asan), \ + $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%, \ + $(modules_to_install) $(test_files))))) + +$(file >$(HOST_OUT)/.installable_test_files,$(sort \ + $(patsubst $(HOST_OUT)/%,%,$(filter $(HOST_OUT)/%, \ + $(test_files))))) + +test_files := +endif + +# Dedpulicate compatibility suite dist files across modules and packages before +# copying them to their requested locations. Assign the eval result to an unused +# var to prevent Make from trying to make a sense of it. +_unused := $(call copy-many-files, $(sort $(ALL_COMPATIBILITY_DIST_FILES))) + +# Don't include any GNU General Public License shared objects or static +# libraries in SDK images. GPL executables (not static/dynamic libraries) +# are okay if they don't link against any closed source libraries (directly +# or indirectly) + +# It's ok (and necessary) to build the host tools, but nothing that's +# going to be installed on the target (including static libraries). + +ifdef is_sdk_build + target_gnu_MODULES := \ + $(filter \ + $(TARGET_OUT_INTERMEDIATES)/% \ + $(TARGET_OUT)/% \ + $(TARGET_OUT_DATA)/%, \ + $(sort $(call get-tagged-modules,gnu))) + target_gnu_MODULES := $(filter-out $(TARGET_OUT_EXECUTABLES)/%,$(target_gnu_MODULES)) + target_gnu_MODULES := $(filter-out %/libopenjdkjvmti.so,$(target_gnu_MODULES)) + target_gnu_MODULES := $(filter-out %/libopenjdkjvmtid.so,$(target_gnu_MODULES)) + $(info Removing from sdk:)$(foreach d,$(target_gnu_MODULES),$(info : $(d))) + modules_to_install := \ + $(filter-out $(target_gnu_MODULES),$(modules_to_install)) + + # Ensure every module listed in PRODUCT_PACKAGES* gets something installed + # TODO: Should we do this for all builds and not just the sdk? + dangling_modules := + $(foreach m, $(PRODUCT_PACKAGES), \ + $(if $(strip $(ALL_MODULES.$(m).INSTALLED) $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).INSTALLED)),,\ + $(eval dangling_modules += $(m)))) + ifneq ($(dangling_modules),) + $(warning: Modules '$(dangling_modules)' in PRODUCT_PACKAGES have nothing to install!) + endif + $(foreach m, $(PRODUCT_PACKAGES_DEBUG), \ + $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\ + $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_DEBUG has nothing to install!))) + $(foreach m, $(PRODUCT_PACKAGES_ENG), \ + $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\ + $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_ENG has nothing to install!))) + $(foreach m, $(PRODUCT_PACKAGES_TESTS), \ + $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\ + $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_TESTS has nothing to install!))) +endif + +# build/make/core/Makefile contains extra stuff that we don't want to pollute this +# top-level makefile with. It expects that ALL_DEFAULT_INSTALLED_MODULES +# contains everything that's built during the current make, but it also further +# extends ALL_DEFAULT_INSTALLED_MODULES. +ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install) +ifeq ($(HOST_OS),linux) + include $(BUILD_SYSTEM)/Makefile +endif +modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES)) +ALL_DEFAULT_INSTALLED_MODULES := + + +# Some notice deps refer to module names without prefix or arch suffix where +# only the variants with them get built. +# fix-notice-deps replaces those unadorned module names with every built variant. +$(call fix-notice-deps) + +# These are additional goals that we build, in order to make sure that there +# is as little code as possible in the tree that doesn't build. +modules_to_check := $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).CHECKED)) + +# If you would like to build all goals, and not skip any intermediate +# steps, you can pass the "all" modifier goal on the commandline. +ifneq ($(filter all,$(MAKECMDGOALS)),) +modules_to_check += $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).BUILT)) +endif + +# Build docs as part of checkbuild to catch more breakages. +modules_to_check += $(ALL_DOCS) + +# for easier debugging +modules_to_check := $(sort $(modules_to_check)) +#$(error modules_to_check $(modules_to_check)) + +# ------------------------------------------------------------------- +# This is used to to get the ordering right, you can also use these, +# but they're considered undocumented, so don't complain if their +# behavior changes. +# An internal target that depends on all copied headers +# (see copy_headers.make). Other targets that need the +# headers to be copied first can depend on this target. +.PHONY: all_copied_headers +all_copied_headers: ; + +$(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers + +# All the droid stuff, in directories +.PHONY: files +files: $(modules_to_install) \ + $(INSTALLED_ANDROID_INFO_TXT_TARGET) + +# ------------------------------------------------------------------- + +.PHONY: checkbuild +checkbuild: $(modules_to_check) droid_targets check-elf-files + +ifeq (true,$(ANDROID_BUILD_EVERYTHING_BY_DEFAULT)) +droid: checkbuild +endif + +.PHONY: ramdisk +ramdisk: $(INSTALLED_RAMDISK_TARGET) + +.PHONY: ramdisk_debug +ramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET) + +.PHONY: ramdisk_test_harness +ramdisk_test_harness: $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) + +.PHONY: userdataimage +userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET) + +ifneq (,$(filter userdataimage, $(MAKECMDGOALS))) +$(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAGE_TARGET)) +endif + +.PHONY: cacheimage +cacheimage: $(INSTALLED_CACHEIMAGE_TARGET) + +.PHONY: bptimage +bptimage: $(INSTALLED_BPTIMAGE_TARGET) + +.PHONY: vendorimage +vendorimage: $(INSTALLED_VENDORIMAGE_TARGET) + +.PHONY: vendorbootimage +vendorbootimage: $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) + +.PHONY: vendorkernelbootimage +vendorkernelbootimage: $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) + +.PHONY: vendorbootimage_debug +vendorbootimage_debug: $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) + +.PHONY: vendorbootimage_test_harness +vendorbootimage_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) + +.PHONY: vendorramdisk +vendorramdisk: $(INSTALLED_VENDOR_RAMDISK_TARGET) + +.PHONY: vendorkernelramdisk +vendorkernelramdisk: $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) + +.PHONY: vendorramdisk_debug +vendorramdisk_debug: $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) + +.PHONY: vendorramdisk_test_harness +vendorramdisk_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) + +.PHONY: productimage +productimage: $(INSTALLED_PRODUCTIMAGE_TARGET) + +.PHONY: systemextimage +systemextimage: $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) + +.PHONY: odmimage +odmimage: $(INSTALLED_ODMIMAGE_TARGET) + +.PHONY: vendor_dlkmimage +vendor_dlkmimage: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) + +.PHONY: odm_dlkmimage +odm_dlkmimage: $(INSTALLED_ODM_DLKMIMAGE_TARGET) + +.PHONY: system_dlkmimage +system_dlkmimage: $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) + +.PHONY: systemotherimage +systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) + +.PHONY: superimage_empty +superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) + +.PHONY: bootimage +bootimage: $(INSTALLED_BOOTIMAGE_TARGET) + +.PHONY: initbootimage +initbootimage: $(INSTALLED_INIT_BOOT_IMAGE_TARGET) + +ifeq (true,$(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST)) +$(call dist-for-goals, bootimage, $(INSTALLED_BOOTIMAGE_TARGET)) +endif + +.PHONY: bootimage_debug +bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) + +.PHONY: bootimage_test_harness +bootimage_test_harness: $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) + +.PHONY: vbmetaimage +vbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET) + +.PHONY: vbmetasystemimage +vbmetasystemimage: $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) + +.PHONY: vbmetavendorimage +vbmetavendorimage: $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) + +# The droidcore-unbundled target depends on the subset of targets necessary to +# perform a full system build (either unbundled or not). +.PHONY: droidcore-unbundled +droidcore-unbundled: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \ + $(INSTALLED_FILES_OUTSIDE_IMAGES) \ + $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ + $(INSTALLED_RADIOIMAGE_TARGET) \ + $(INSTALLED_DEBUG_RAMDISK_TARGET) \ + $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \ + $(INSTALLED_RECOVERYIMAGE_TARGET) \ + $(INSTALLED_VBMETAIMAGE_TARGET) \ + $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \ + $(INSTALLED_USERDATAIMAGE_TARGET) \ + $(INSTALLED_CACHEIMAGE_TARGET) \ + $(INSTALLED_BPTIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \ + $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_RAMDISK_TARGET) \ + $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \ + $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \ + $(INSTALLED_ODMIMAGE_TARGET) \ + $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ + $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ + $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \ + $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \ + $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \ + $(INSTALLED_FILES_FILE) \ + $(INSTALLED_FILES_JSON) \ + $(INSTALLED_FILES_FILE_VENDOR) \ + $(INSTALLED_FILES_JSON_VENDOR) \ + $(INSTALLED_FILES_FILE_ODM) \ + $(INSTALLED_FILES_JSON_ODM) \ + $(INSTALLED_FILES_FILE_VENDOR_DLKM) \ + $(INSTALLED_FILES_JSON_VENDOR_DLKM) \ + $(INSTALLED_FILES_FILE_ODM_DLKM) \ + $(INSTALLED_FILES_JSON_ODM_DLKM) \ + $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \ + $(INSTALLED_FILES_JSON_SYSTEM_DLKM) \ + $(INSTALLED_FILES_FILE_PRODUCT) \ + $(INSTALLED_FILES_JSON_PRODUCT) \ + $(INSTALLED_FILES_FILE_SYSTEM_EXT) \ + $(INSTALLED_FILES_JSON_SYSTEM_EXT) \ + $(INSTALLED_FILES_FILE_SYSTEMOTHER) \ + $(INSTALLED_FILES_JSON_SYSTEMOTHER) \ + $(INSTALLED_FILES_FILE_RAMDISK) \ + $(INSTALLED_FILES_JSON_RAMDISK) \ + $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_FILE_VENDOR_RAMDISK) \ + $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) \ + $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK) \ + $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) \ + $(INSTALLED_FILES_FILE_ROOT) \ + $(INSTALLED_FILES_JSON_ROOT) \ + $(INSTALLED_FILES_FILE_RECOVERY) \ + $(INSTALLED_FILES_JSON_RECOVERY) \ + $(INSTALLED_ANDROID_INFO_TXT_TARGET) + +# The droidcore target depends on the droidcore-unbundled subset and any other +# targets for a non-unbundled (full source) full system build. +.PHONY: droidcore +droidcore: droidcore-unbundled + +# dist_files only for putting your library into the dist directory with a full build. +.PHONY: dist_files + +ifeq ($(SOONG_COLLECT_JAVA_DEPS), true) + $(call dist-for-goals, dist_files, $(SOONG_OUT_DIR)/module_bp_java_deps.json) + $(call dist-for-goals, dist_files, $(PRODUCT_OUT)/module-info.json) +endif + +.PHONY: apps_only +ifeq ($(HOST_OS),darwin) + # Mac only supports building host modules + droid_targets: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) dist_files + +else ifneq ($(TARGET_BUILD_APPS),) + # If this build is just for apps, only build apps and not the full system by default. + + unbundled_build_modules := + ifneq ($(filter all,$(TARGET_BUILD_APPS)),) + # If they used the magic goal "all" then build all apps in the source tree. + unbundled_build_modules := $(foreach m,$(sort $(ALL_MODULES)),$(if $(filter APPS,$(ALL_MODULES.$(m).CLASS)),$(m))) + else + unbundled_build_modules := $(TARGET_BUILD_APPS) + endif + + # Dist the installed files if they exist. + apps_only_installed_files := $(foreach m,$(unbundled_build_modules),$(ALL_MODULES.$(m).INSTALLED)) + $(call dist-for-goals,apps_only, $(apps_only_installed_files)) + + # Dist the bundle files if they exist. + apps_only_bundle_files := $(foreach m,$(unbundled_build_modules),\ + $(if $(ALL_MODULES.$(m).BUNDLE),$(ALL_MODULES.$(m).BUNDLE):$(m)-base.zip)) + $(call dist-for-goals,apps_only, $(apps_only_bundle_files)) + + # Dist the lint reports if they exist. + apps_only_lint_report_files := $(foreach m,$(unbundled_build_modules),\ + $(foreach report,$(ALL_MODULES.$(m).LINT_REPORTS),\ + $(report):$(m)-$(notdir $(report)))) + .PHONY: lint-check + lint-check: $(foreach f, $(apps_only_lint_report_files), $(call word-colon,1,$(f))) + $(call dist-for-goals,lint-check, $(apps_only_lint_report_files)) + + # For uninstallable modules such as static Java library, we have to dist the built file, + # as . + apps_only_dist_built_files := $(foreach m,$(unbundled_build_modules),$(if $(ALL_MODULES.$(m).INSTALLED),,\ + $(if $(ALL_MODULES.$(m).BUILT),$(ALL_MODULES.$(m).BUILT):$(m)$(suffix $(ALL_MODULES.$(m).BUILT)))\ + $(if $(ALL_MODULES.$(m).AAR),$(ALL_MODULES.$(m).AAR):$(m).aar)\ + )) + $(call dist-for-goals,apps_only, $(apps_only_dist_built_files)) + + ifeq ($(EMMA_INSTRUMENT),true) + $(JACOCO_REPORT_CLASSES_ALL) : $(apps_only_installed_files) + $(call dist-for-goals,apps_only, $(JACOCO_REPORT_CLASSES_ALL)) + endif + + $(PROGUARD_DICT_ZIP) : $(apps_only_installed_files) + $(call dist-for-goals,apps_only, $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_MAPPING)) + $(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) + + $(PROGUARD_USAGE_ZIP) : $(apps_only_installed_files) + $(call dist-for-goals,apps_only, $(PROGUARD_USAGE_ZIP)) + $(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) + + $(SYMBOLS_ZIP) : $(apps_only_installed_files) + $(call dist-for-goals,apps_only, $(SYMBOLS_ZIP) $(SYMBOLS_MAPPING)) + $(call declare-container-license-deps,$(SYMBOLS_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) + + $(COVERAGE_ZIP) : $(apps_only_installed_files) + $(call dist-for-goals,apps_only, $(COVERAGE_ZIP)) + $(call declare-container-license-deps,$(COVERAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) + +apps_only: $(unbundled_build_modules) + +droid_targets: apps_only + +# NOTICE files for a apps_only build +$(eval $(call html-notice-rule,$(target_notice_file_html_or_xml),"Apps","Notices for files for apps:",$(unbundled_build_modules),$(PRODUCT_OUT)/ $(HOST_OUT)/)) + +$(eval $(call text-notice-rule,$(target_notice_file_txt),"Apps","Notices for files for apps:",$(unbundled_build_modules),$(PRODUCT_OUT)/ $(HOST_OUT)/)) + +$(call declare-0p-target,$(target_notice_file_txt)) +$(call declare-0p-target,$(target_notice_html_or_xml)) + + +else ifeq ($(TARGET_BUILD_UNBUNDLED),$(TARGET_BUILD_UNBUNDLED_IMAGE)) + + # Truth table for entering this block of code: + # TARGET_BUILD_UNBUNDLED | TARGET_BUILD_UNBUNDLED_IMAGE | Action + # -----------------------|------------------------------|------------------------- + # not set | not set | droidcore path + # not set | true | invalid + # true | not set | skip + # true | true | droidcore-unbundled path + + # We dist the following targets only for droidcore full build. These items + # can include java-related targets that would cause building framework java + # sources in a droidcore full build. + + $(call dist-for-goals, droidcore, \ + $(BUILT_OTATOOLS_PACKAGE) \ + $(APPCOMPAT_ZIP) \ + $(DEXPREOPT_TOOLS_ZIP) \ + ) + + # We dist the following targets for droidcore-unbundled (and droidcore since + # droidcore depends on droidcore-unbundled). The droidcore-unbundled target + # is a subset of droidcore. It can be used used for an unbundled build to + # avoid disting targets that would cause building framework java sources, + # which we want to avoid in an unbundled build. + + $(call dist-for-goals, droidcore-unbundled, \ + $(INTERNAL_UPDATE_PACKAGE_TARGET) \ + $(INTERNAL_OTA_PACKAGE_TARGET) \ + $(INTERNAL_OTA_METADATA) \ + $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) \ + $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) \ + $(SYMBOLS_ZIP) \ + $(SYMBOLS_MAPPING) \ + $(PROGUARD_DICT_ZIP) \ + $(PROGUARD_DICT_MAPPING) \ + $(PROGUARD_USAGE_ZIP) \ + $(COVERAGE_ZIP) \ + $(INSTALLED_FILES_FILE) \ + $(INSTALLED_FILES_JSON) \ + $(INSTALLED_FILES_FILE_VENDOR) \ + $(INSTALLED_FILES_JSON_VENDOR) \ + $(INSTALLED_FILES_FILE_ODM) \ + $(INSTALLED_FILES_JSON_ODM) \ + $(INSTALLED_FILES_FILE_VENDOR_DLKM) \ + $(INSTALLED_FILES_JSON_VENDOR_DLKM) \ + $(INSTALLED_FILES_FILE_ODM_DLKM) \ + $(INSTALLED_FILES_JSON_ODM_DLKM) \ + $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \ + $(INSTALLED_FILES_JSON_SYSTEM_DLKM) \ + $(INSTALLED_FILES_FILE_PRODUCT) \ + $(INSTALLED_FILES_JSON_PRODUCT) \ + $(INSTALLED_FILES_FILE_SYSTEM_EXT) \ + $(INSTALLED_FILES_JSON_SYSTEM_EXT) \ + $(INSTALLED_FILES_FILE_SYSTEMOTHER) \ + $(INSTALLED_FILES_JSON_SYSTEMOTHER) \ + $(INSTALLED_FILES_FILE_RECOVERY) \ + $(INSTALLED_FILES_JSON_RECOVERY) \ + $(INSTALLED_BUILD_PROP_TARGET):build.prop \ + $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor \ + $(INSTALLED_PRODUCT_BUILD_PROP_TARGET):build.prop-product \ + $(INSTALLED_ODM_BUILD_PROP_TARGET):build.prop-odm \ + $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET):build.prop-system_ext \ + $(INSTALLED_RAMDISK_BUILD_PROP_TARGET):build.prop-ramdisk \ + $(BUILT_TARGET_FILES_PACKAGE) \ + $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ + $(INSTALLED_MISC_INFO_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(DEXPREOPT_CONFIG_ZIP) \ + ) + + # Put a copy of the radio/bootloader files in the dist dir. + $(foreach f,$(INSTALLED_RADIOIMAGE_TARGET), \ + $(call dist-for-goals, droidcore-unbundled, $(f))) + + ifneq ($(ANDROID_BUILD_EMBEDDED),true) + $(call dist-for-goals, droidcore, \ + $(APPS_ZIP) \ + $(INTERNAL_EMULATOR_PACKAGE_TARGET) \ + ) + endif + + $(call dist-for-goals, droidcore-unbundled, \ + $(INSTALLED_FILES_FILE_ROOT) \ + $(INSTALLED_FILES_JSON_ROOT) \ + ) + + ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + $(call dist-for-goals, droidcore-unbundled, \ + $(INSTALLED_FILES_FILE_RAMDISK) \ + $(INSTALLED_FILES_JSON_RAMDISK) \ + $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_FILE_VENDOR_RAMDISK) \ + $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) \ + $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK) \ + $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) \ + $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) \ + $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) \ + $(INSTALLED_DEBUG_RAMDISK_TARGET) \ + $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \ + $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \ + $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \ + $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \ + $(INSTALLED_VENDOR_RAMDISK_TARGET) \ + $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \ + $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \ + ) + endif + + ifeq ($(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST),true) + $(call dist-for-goals, droidcore-unbundled, $(INSTALLED_BOOTIMAGE_TARGET)) + endif + + ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) + $(call dist-for-goals, droidcore-unbundled, \ + $(recovery_ramdisk) \ + ) + endif + + ifeq ($(EMMA_INSTRUMENT),true) + $(call dist-for-goals, dist_files, $(JACOCO_REPORT_CLASSES_ALL)) + endif + + # Put XML formatted API files in the dist dir. + $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml: $(call java-lib-files,android_stubs_current) $(APICHECK) + $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml: $(call java-lib-files,android_system_stubs_current) $(APICHECK) + $(TARGET_OUT_COMMON_INTERMEDIATES)/module-lib-api.xml: $(call java-lib-files,android_module_lib_stubs_current) $(APICHECK) + $(TARGET_OUT_COMMON_INTERMEDIATES)/system-server-api.xml: $(call java-lib-files,android_system_server_stubs_current) $(APICHECK) + $(TARGET_OUT_COMMON_INTERMEDIATES)/test-api.xml: $(call java-lib-files,android_test_stubs_current) $(APICHECK) + + api_xmls := $(addprefix $(TARGET_OUT_COMMON_INTERMEDIATES)/,api.xml system-api.xml module-lib-api.xml system-server-api.xml test-api.xml) + $(api_xmls): + $(hide) echo "Converting API file to XML: $@" + $(hide) mkdir -p $(dir $@) + $(hide) $(APICHECK_COMMAND) --input-api-jar $< --api-xml $@ + + $(foreach xml,$(sort $(api_xmls)),$(call declare-1p-target,$(xml),)) + + $(call dist-for-goals, dist_files, $(api_xmls)) + api_xmls := + + ifdef CLANG_COVERAGE + $(foreach f,$(SOONG_NDK_API_XML), \ + $(call dist-for-goals,droidcore,$(f):ndk_apis/$(notdir $(f)))) + $(foreach f,$(SOONG_CC_API_XML), \ + $(call dist-for-goals,droidcore,$(f):cc_apis/$(notdir $(f)))) + endif + + # For full system build (whether unbundled or not), we configure + # droid_targets to depend on droidcore-unbundled, which will set up the full + # system dependencies and also dist the subset of targets that correspond to + # an unbundled build (exclude building some framework sources). + + droid_targets: droidcore-unbundled + + ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE)) + + # If we're building a full system (including the framework sources excluded + # by droidcore-unbundled), we configure droid_targets also to depend on + # droidcore, which includes all dist for droidcore, and will build the + # necessary framework sources. + + droid_targets: droidcore dist_files + + endif + +endif # TARGET_BUILD_UNBUNDLED == TARGET_BUILD_UNBUNDLED_IMAGE + +.PHONY: docs +docs: $(ALL_DOCS) + +.PHONY: sdk sdk_addon +ifeq ($(HOST_OS),linux) +ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET) +sdk: $(ALL_SDK_TARGETS) +$(call dist-for-goals,sdk, \ + $(ALL_SDK_TARGETS) \ + $(INSTALLED_BUILD_PROP_TARGET) \ +) +endif + +# umbrella targets to assit engineers in verifying builds +.PHONY: java native target host java-host java-target native-host native-target \ + java-host-tests java-target-tests native-host-tests native-target-tests \ + java-tests native-tests host-tests target-tests tests java-dex \ + native-host-cross +# some synonyms +.PHONY: host-java target-java host-native target-native \ + target-java-tests target-native-tests +host-java : java-host +target-java : java-target +host-native : native-host +target-native : native-target +target-java-tests : java-target-tests +target-native-tests : native-target-tests +tests : host-tests target-tests + +# Phony target to run all java compilations that use javac +.PHONY: javac-check + +.PHONY: findbugs +findbugs: $(INTERNAL_FINDBUGS_HTML_TARGET) $(INTERNAL_FINDBUGS_XML_TARGET) + +LSDUMP_PATHS_FILE := $(PRODUCT_OUT)/lsdump_paths.txt + +.PHONY: findlsdumps +# LSDUMP_PATHS is a list of tag:path. +findlsdumps: $(LSDUMP_PATHS_FILE) $(foreach p,$(LSDUMP_PATHS),$(call word-colon,2,$(p))) + +$(LSDUMP_PATHS_FILE): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS) +$(LSDUMP_PATHS_FILE): + @echo "Generate $@" + @rm -rf $@ && echo -e "$(subst :,:$(space),$(subst $(space),\n,$(PRIVATE_LSDUMP_PATHS)))" > $@ + +.PHONY: check-elf-files +check-elf-files: + +#xxx scrape this from ALL_MODULE_NAME_TAGS +.PHONY: modules +modules: + @echo "Available sub-modules:" + @echo "$(call module-names-for-tag-list,$(ALL_MODULE_TAGS))" | \ + tr -s ' ' '\n' | sort -u + +.PHONY: dump-files +dump-files: + @echo "Target files for $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) ($(INTERNAL_PRODUCT)):" + @echo $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%,$(modules_to_install)))) | tr -s ' ' '\n' + @echo Successfully dumped product target file list. + +.PHONY: nothing +nothing: + @echo Successfully read the makefiles. + +.PHONY: tidy_only +tidy_only: + @echo Successfully make tidy_only. + +ndk: $(SOONG_OUT_DIR)/ndk.timestamp +.PHONY: ndk + +# Checks that allowed_deps.txt remains up to date +ifneq ($(UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK),true) + droidcore: ${APEX_ALLOWED_DEPS_CHECK} +endif + +# Create a license metadata rule per module. Could happen in base_rules.mk or +# notice_files.mk; except, it has to happen after fix-notice-deps to avoid +# missing dependency errors. +$(call build-license-metadata) + +$(call dist-write-file,$(KATI_PACKAGE_MK_DIR)/dist.mk) + +$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing build rules ...) diff --git a/make/core/misc_prebuilt_internal.mk b/make/core/misc_prebuilt_internal.mk new file mode 100644 index 0000000..921ea52 --- /dev/null +++ b/make/core/misc_prebuilt_internal.mk @@ -0,0 +1,38 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################ +# Internal build rules for misc prebuilt modules that don't need additional processing +############################################################ + +prebuilt_module_classes := SCRIPT ETC DATA RENDERSCRIPT_BITCODE +ifeq ($(filter $(prebuilt_module_classes),$(LOCAL_MODULE_CLASS)),) +$(call pretty-error,misc_prebuilt_internal.mk is for $(prebuilt_module_classes) modules only) +endif + +include $(BUILD_SYSTEM)/base_rules.mk + +ifneq ($(filter init%rc,$(notdir $(LOCAL_INSTALLED_MODULE)))$(filter %/etc/init,$(dir $(LOCAL_INSTALLED_MODULE))),) + $(eval $(call copy-init-script-file-checked,$(my_prebuilt_src_file),$(LOCAL_BUILT_MODULE))) +else ifneq ($(LOCAL_PREBUILT_STRIP_COMMENTS),) +$(LOCAL_BUILT_MODULE) : $(my_prebuilt_src_file) + $(transform-prebuilt-to-target-strip-comments) +else +$(LOCAL_BUILT_MODULE) : $(my_prebuilt_src_file) + $(transform-prebuilt-to-target) +endif + +built_module := $(LOCAL_BUILT_MODULE) diff --git a/make/core/module_arch_supported.mk b/make/core/module_arch_supported.mk new file mode 100644 index 0000000..7ebc8f9 --- /dev/null +++ b/make/core/module_arch_supported.mk @@ -0,0 +1,72 @@ +########################################################### +## Determine if a module can be built for an arch +## +## Inputs from module makefile: +## my_prefix TARGET_ or HOST_ +## my_module_multilib +## LOCAL_MODULE_$(my_prefix)ARCH +## LOCAL_MODULE_$(my_prefix)ARCH_WARN +## LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH +## LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH_WARN +## LOCAL_IS_HOST_MODULE +## LOCAL_MODULE_HOST_OS +## +## Inputs from build system: +## $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT +## LOCAL_2ND_ARCH_VAR_PREFIX +## +## Outputs: +## my_module_arch_supported := (true|false) +########################################################### + +my_module_arch_supported := true + +ifeq ($(my_module_multilib),none) +my_module_arch_supported := false +endif + +ifeq ($($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT)|$(my_module_multilib),true|32) +my_module_arch_supported := false +endif +ifeq ($($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT)|$(my_module_multilib),|64) +my_module_arch_supported := false +endif + +ifneq ($(LOCAL_2ND_ARCH_VAR_PREFIX),) +ifeq ($(my_module_multilib),first) +my_module_arch_supported := false +endif +endif + +ifneq (,$(LOCAL_MODULE_$(my_prefix)ARCH)) +ifeq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH))) +my_module_arch_supported := false +endif +endif + +ifneq (,$(LOCAL_MODULE_$(my_prefix)ARCH_WARN)) +ifeq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH_WARN))) +my_module_arch_supported := false +$(warning $(LOCAL_MODULE): architecture $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) not supported) +endif +endif + +ifneq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH))) +my_module_arch_supported := false +endif + +ifneq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH_WARN))) +my_module_arch_supported := false +$(warning $(LOCAL_MODULE): architecture $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) unsupported) +endif + +ifdef LOCAL_IS_HOST_MODULE +ifneq (,$(LOCAL_MODULE_HOST_OS)) + ifneq (,$(filter windows,$(LOCAL_MODULE_HOST_OS))) + $(call pretty-error,Windows is only supported in Android.bp files) + endif + ifeq (,$(filter $($(my_prefix)OS),$(LOCAL_MODULE_HOST_OS))) + my_module_arch_supported := false + endif +endif +endif diff --git a/make/core/multi_prebuilt.mk b/make/core/multi_prebuilt.mk new file mode 100644 index 0000000..c97d481 --- /dev/null +++ b/make/core/multi_prebuilt.mk @@ -0,0 +1,134 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call record-module-type,MULTI_PREBUILT) +ifneq ($(LOCAL_MODULE)$(LOCAL_MODULE_CLASS),) +$(error $(LOCAL_PATH): LOCAL_MODULE or LOCAL_MODULE_CLASS not needed by \ + BUILD_MULTI_PREBUILT, use BUILD_PREBUILT instead!) +endif + +# Save these before they get cleared by CLEAR_VARS. +prebuilt_static_libs := $(filter %.a,$(LOCAL_PREBUILT_LIBS)) +prebuilt_shared_libs := $(filter-out %.a,$(LOCAL_PREBUILT_LIBS)) +prebuilt_executables := $(LOCAL_PREBUILT_EXECUTABLES) +prebuilt_java_libraries := $(LOCAL_PREBUILT_JAVA_LIBRARIES) +prebuilt_static_java_libraries := $(LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES) +prebuilt_is_host := $(LOCAL_IS_HOST_MODULE) +prebuilt_module_tags := $(LOCAL_MODULE_TAGS) +prebuilt_strip_module := $(LOCAL_STRIP_MODULE) + + +ifndef multi_prebuilt_once +multi_prebuilt_once := true + +# $(1): file list +# $(2): IS_HOST_MODULE +# $(3): MODULE_CLASS +# $(4): MODULE_TAGS +# $(6): UNINSTALLABLE_MODULE +# $(7): BUILT_MODULE_STEM +# $(8): LOCAL_STRIP_MODULE +# +# Elements in the file list may be bare filenames, +# or of the form ":". +# If the module name is not specified, the module +# name will be the filename with the suffix removed. +# +define auto-prebuilt-boilerplate +$(if $(filter %: :%,$(1)), \ + $(error $(LOCAL_PATH): Leading or trailing colons in "$(1)")) \ +$(foreach t,$(1), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_IS_HOST_MODULE := $(2)) \ + $(eval LOCAL_MODULE_CLASS := $(3)) \ + $(eval LOCAL_MODULE_TAGS := $(4)) \ + $(eval LOCAL_UNINSTALLABLE_MODULE := $(6)) \ + $(eval tw := $(subst :, ,$(strip $(t)))) \ + $(if $(word 3,$(tw)),$(error $(LOCAL_PATH): Bad prebuilt filename '$(t)')) \ + $(if $(word 2,$(tw)), \ + $(eval LOCAL_MODULE := $(word 1,$(tw))) \ + $(eval LOCAL_SRC_FILES := $(word 2,$(tw))) \ + , \ + $(eval LOCAL_MODULE := $(basename $(notdir $(t)))) \ + $(eval LOCAL_SRC_FILES := $(t)) \ + ) \ + $(if $(7), \ + $(eval LOCAL_BUILT_MODULE_STEM := $(7)) \ + , \ + $(if $(word 2,$(tw)), \ + $(eval LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)$(suffix $(LOCAL_SRC_FILES))) \ + , \ + $(eval LOCAL_BUILT_MODULE_STEM := $(notdir $(LOCAL_SRC_FILES))) \ + ) \ + ) \ + $(eval LOCAL_MODULE_SUFFIX := $(suffix $(LOCAL_SRC_FILES))) \ + $(eval LOCAL_STRIP_MODULE := $(8)) \ + $(eval include $(BUILD_PREBUILT)) \ + ) +endef + +endif # multi_prebuilt_once + + +$(call auto-prebuilt-boilerplate, \ + $(prebuilt_static_libs), \ + $(prebuilt_is_host), \ + STATIC_LIBRARIES, \ + $(prebuilt_module_tags), \ + , \ + true) + +$(call auto-prebuilt-boilerplate, \ + $(prebuilt_shared_libs), \ + $(prebuilt_is_host), \ + SHARED_LIBRARIES, \ + $(prebuilt_module_tags), \ + , \ + , \ + , \ + $(prebuilt_strip_module)) + +$(call auto-prebuilt-boilerplate, \ + $(prebuilt_executables), \ + $(prebuilt_is_host), \ + EXECUTABLES, \ + $(prebuilt_module_tags)) + +$(call auto-prebuilt-boilerplate, \ + $(prebuilt_java_libraries), \ + $(prebuilt_is_host), \ + JAVA_LIBRARIES, \ + $(prebuilt_module_tags), \ + , \ + , \ + javalib.jar) + +$(call auto-prebuilt-boilerplate, \ + $(prebuilt_static_java_libraries), \ + $(prebuilt_is_host), \ + JAVA_LIBRARIES, \ + $(prebuilt_module_tags), \ + , \ + true, \ + javalib.jar) + +prebuilt_static_libs := +prebuilt_shared_libs := +prebuilt_executables := +prebuilt_java_libraries := +prebuilt_static_java_libraries := +prebuilt_is_host := +prebuilt_module_tags := diff --git a/make/core/multilib.mk b/make/core/multilib.mk new file mode 100644 index 0000000..a3ced65 --- /dev/null +++ b/make/core/multilib.mk @@ -0,0 +1,15 @@ +# Translate LOCAL_32_BIT_ONLY to LOCAL_MULTILIB, +# and check LOCAL_MULTILIB is a valid value. Returns module's multilib +# setting in my_module_multilib, or empty if not set. + +my_module_multilib := $(strip $(LOCAL_MULTILIB)) + +ifndef my_module_multilib +ifeq ($(LOCAL_32_BIT_ONLY),true) +my_module_multilib := 32 +endif +else # my_module_multilib defined +ifeq (,$(filter 32 64 first both none,$(my_module_multilib))) +$(error $(LOCAL_PATH): Invalid LOCAL_MULTILIB specified for module $(LOCAL_MODULE)) +endif +endif # my_module_multilib defined diff --git a/make/core/native_benchmark_test_config_template.xml b/make/core/native_benchmark_test_config_template.xml new file mode 100644 index 0000000..d1f0199 --- /dev/null +++ b/make/core/native_benchmark_test_config_template.xml @@ -0,0 +1,31 @@ + + + + + diff --git a/make/core/native_host_test_config_template.xml b/make/core/native_host_test_config_template.xml new file mode 100644 index 0000000..818b9b9 --- /dev/null +++ b/make/core/native_host_test_config_template.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/make/core/native_test.mk b/make/core/native_test.mk new file mode 100644 index 0000000..8b49fbd --- /dev/null +++ b/make/core/native_test.mk @@ -0,0 +1,23 @@ +########################################### +## A thin wrapper around BUILD_EXECUTABLE +## Common flags for native tests are added. +########################################### +$(call record-module-type,NATIVE_TEST) + +ifdef LOCAL_MODULE_CLASS +ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) +$(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must be NATIVE_TESTS with BUILD_HOST_NATIVE_TEST) +endif +endif + +LOCAL_MODULE_CLASS := NATIVE_TESTS + +include $(BUILD_SYSTEM)/target_test_internal.mk + +ifndef LOCAL_MULTILIB +ifndef LOCAL_32_BIT_ONLY +LOCAL_MULTILIB := both +endif +endif + +include $(BUILD_EXECUTABLE) diff --git a/make/core/native_test_config_template.xml b/make/core/native_test_config_template.xml new file mode 100644 index 0000000..ea982cf --- /dev/null +++ b/make/core/native_test_config_template.xml @@ -0,0 +1,32 @@ + + + + + diff --git a/make/core/ninja_config.mk b/make/core/ninja_config.mk new file mode 100644 index 0000000..e436b2c --- /dev/null +++ b/make/core/ninja_config.mk @@ -0,0 +1,59 @@ +ifeq ($(filter address,$(SANITIZE_HOST)),) +NINJA ?= prebuilts/build-tools/$(HOST_PREBUILT_TAG)/bin/ninja +else +NINJA ?= prebuilts/build-tools/$(HOST_PREBUILT_TAG)/asan/bin/ninja +endif + +KATI_OUTPUT_PATTERNS := $(OUT_DIR)/build%.ninja $(OUT_DIR)/ninja%.sh + +# Modifier goals we don't need to pass to Ninja. +NINJA_EXCLUDE_GOALS := all + +# A list of goals which affect parsing of makefiles and we need to pass to Kati. +PARSE_TIME_MAKE_GOALS := \ + $(PARSE_TIME_MAKE_GOALS) \ + $(dont_bother_goals) \ + all \ + ECLIPSE-% \ + brillo_tests \ + btnod \ + build-art% \ + build_kernel-nodeps \ + clean-oat% \ + continuous_instrumentation_tests \ + continuous_native_tests \ + cts \ + custom_images \ + dicttool_aosp \ + eng \ + oem_image \ + online-system-api-sdk-docs \ + product-graph \ + samplecode \ + sdk \ + sdk_addon \ + sdk_repo \ + stnod \ + test-art% \ + user \ + userdataimage \ + userdebug + +include $(wildcard vendor/*/build/ninja_config.mk) + +# Any Android goals that need to be built. +ANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS),\ + $(sort $(ORIGINAL_MAKECMDGOALS) $(MAKECMDGOALS))) +# Temporary compatibility support until the build server configs are updated +ANDROID_GOALS := $(patsubst win_sdk,sdk,$(ANDROID_GOALS)) +ifneq ($(HOST_OS),linux) + ANDROID_GOALS := $(filter-out sdk,$(ANDROID_GOALS)) + ANDROID_GOALS := $(patsubst sdk_repo,sdk-repo-build-tools sdk-repo-platform-tools,$(ANDROID_GOALS)) +endif +# Goals we need to pass to Ninja. +NINJA_GOALS := $(filter-out $(NINJA_EXCLUDE_GOALS), $(ANDROID_GOALS)) +ifndef NINJA_GOALS + NINJA_GOALS := droid +endif +# Goals we need to pass to Kati. +KATI_GOALS := $(filter $(PARSE_TIME_MAKE_GOALS), $(ANDROID_GOALS)) diff --git a/make/core/node_fns.mk b/make/core/node_fns.mk new file mode 100644 index 0000000..2243cd7 --- /dev/null +++ b/make/core/node_fns.mk @@ -0,0 +1,273 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Clears a list of variables using ":=". +# +# E.g., +# $(call clear-var-list,A B C) +# would be the same as: +# A := +# B := +# C := +# +# $(1): list of variable names to clear +# +define clear-var-list +$(foreach v,$(1),$(eval $(v):=)) +endef + +# +# Copies a list of variables into another list of variables. +# The target list is the same as the source list, but has +# a dotted prefix affixed to it. +# +# E.g., +# $(call copy-var-list, PREFIX, A B) +# would be the same as: +# PREFIX.A := $(A) +# PREFIX.B := $(B) +# +# $(1): destination prefix +# $(2): list of variable names to copy +# +define copy-var-list +$(foreach v,$(2),$(eval $(strip $(1)).$(v):=$($(v)))) +endef + +# +# Moves a list of variables into another list of variables. +# The variable names differ by a prefix. After moving, the +# source variable is cleared. +# +# NOTE: Spaces are not allowed around the prefixes. +# +# E.g., +# $(call move-var-list,SRC,DST,A B) +# would be the same as: +# DST.A := $(SRC.A) +# SRC.A := +# DST.B := $(SRC.B) +# SRC.B := +# +# $(1): source prefix +# $(2): destination prefix +# $(3): list of variable names to move +# +define move-var-list +$(foreach v,$(3), \ + $(eval $(2).$(v) := $($(1).$(v))) \ + $(eval $(1).$(v) :=) \ + ) +endef + +# +# $(1): haystack +# $(2): needle +# +# Guarantees that needle appears at most once in haystack, +# without changing the order of other elements in haystack. +# If needle appears multiple times, only the first occurrance +# will survive. +# +# How it works: +# +# - Stick everything in haystack into a single word, +# with "|||" separating the words. +# - Replace occurrances of "|||$(needle)|||" with "||| |||", +# breaking haystack back into multiple words, with spaces +# where needle appeared. +# - Add needle between the first and second words of haystack. +# - Replace "|||" with spaces, breaking haystack back into +# individual words. +# +define uniq-word +$(strip \ + $(if $(filter-out 0 1,$(words $(filter $(2),$(1)))), \ + $(eval h := |||$(subst $(space),|||,$(strip $(1)))|||) \ + $(eval h := $(subst |||$(strip $(2))|||,|||$(space)|||,$(h))) \ + $(eval h := $(word 1,$(h)) $(2) $(wordlist 2,9999,$(h))) \ + $(subst |||,$(space),$(h)) \ + , \ + $(1) \ + )) +endef + +INHERIT_TAG := @inherit: + +# +# Walks through the list of variables, each qualified by the prefix, +# and finds instances of words beginning with INHERIT_TAG. Scrape +# off INHERIT_TAG from each matching word, and return the sorted, +# unique set of those words. +# +# E.g., given +# PREFIX.A := A $(INHERIT_TAG)aaa B C +# PREFIX.B := B $(INHERIT_TAG)aaa C $(INHERIT_TAG)bbb D E +# Then +# $(call get-inherited-nodes,PREFIX,A B) +# returns +# aaa bbb +# +# $(1): variable prefix +# $(2): list of variables to check +# +define get-inherited-nodes +$(sort \ + $(subst $(INHERIT_TAG),, \ + $(filter $(INHERIT_TAG)%, \ + $(foreach v,$(2),$($(1).$(v))) \ + ))) +endef + +# +# for each variable ( (prefix + name) * vars ): +# get list of inherited words; if not empty: +# for each inherit: +# replace the first occurrence with (prefix + inherited + var) +# clear the source var so we can't inherit the value twice +# +# $(1): context prefix +# $(2): name of this node +# $(3): list of node variable names +# $(4): list of single value variable names (subset of $(3)) +# +define _expand-inherited-values + $(foreach v,$(3), \ + $(eval ### "Shorthand for the name of the target variable") \ + $(eval _eiv_tv := $(1).$(2).$(v)) \ + $(eval ### "Get the list of nodes that this variable inherits") \ + $(eval _eiv_i := \ + $(sort \ + $(patsubst $(INHERIT_TAG)%,%, \ + $(filter $(INHERIT_TAG)%, $($(_eiv_tv)) \ + )))) \ + $(eval ### "Whether this variable should only take a single value") \ + $(eval _eiv_sv := $(filter $(v),$(4))) \ + $(foreach i,$(_eiv_i), \ + $(eval ### "Make sure that this inherit appears only once") \ + $(eval $(_eiv_tv) := \ + $(call uniq-word,$($(_eiv_tv)),$(INHERIT_TAG)$(i))) \ + $(eval ### "The expanded value, empty if we want a single value and have one") \ + $(eval _eiv_ev := \ + $(if $(and $(_eiv_sv),$(filter-out $(INHERIT_TAG)%,$($(_eiv_tv)))),,\ + $($(1).$(i).$(v)) \ + ) \ + ) \ + $(eval ### "Expand the inherit tag") \ + $(eval $(_eiv_tv) := \ + $(strip $(patsubst $(INHERIT_TAG)$(i),$(_eiv_ev),$($(_eiv_tv))))) \ + $(eval ### "Clear the child so DAGs don't create duplicate entries" ) \ + $(eval $(1).$(i).$(v) :=) \ + $(eval ### "If we just inherited ourselves, it's a cycle.") \ + $(if $(filter $(INHERIT_TAG)$(2),$($(_eiv_tv))), \ + $(warning Cycle detected between "$(2)" and "$(i)" for context "$(1)") \ + $(error import of "$(2)" failed) \ + ) \ + ) \ + ) \ + $(eval _eiv_tv :=) \ + $(eval _eiv_i :=) +endef + +# +# $(1): context prefix +# $(2): makefile representing this node +# $(3): list of node variable names +# $(4): list of single value variable names (subset of $(3)) +# +# _include_stack contains the list of included files, with the most recent files first. +define _import-node + $(eval _include_stack := $(2) $$(_include_stack)) + $(call clear-var-list, $(3)) + $(eval LOCAL_PATH := $(patsubst %/,%,$(dir $(2)))) + $(eval MAKEFILE_LIST :=) + $(call dump-import-start,$(_include_stack)) + $(call dump-config-vals,$(2),before) + $(eval include $(2)) + $(call dump-import-done,$(_include_stack)) + $(call dump-config-vals,$(2),after) + $(eval _included := $(filter-out $(2),$(MAKEFILE_LIST))) + $(eval MAKEFILE_LIST :=) + $(eval LOCAL_PATH :=) + $(call copy-var-list, $(1).$(2), $(3)) + $(call clear-var-list, $(3)) + + $(eval $(1).$(2).inherited := \ + $(call get-inherited-nodes,$(1).$(2),$(3))) + $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3),$(4)) + + $(call _expand-inherited-values,$(1),$(2),$(3),$(4)) + + $(eval $(1).$(2).inherited :=) + $(eval _include_stack := $(wordlist 2,9999,$$(_include_stack))) +endef + +# +# This will generate a warning for _included above +# $(if $(_included), \ +# $(eval $(warning product spec file: $(2)))\ +# $(foreach _inc,$(_included),$(eval $(warning $(space)$(space)$(space)includes: $(_inc)))),) +# + +# +# $(1): context prefix +# $(2): list of makefiles representing nodes to import +# $(3): list of node variable names +# $(4): list of single value variable names (subset of $(3)) +# +#TODO: Make the "does not exist" message more helpful; +# should print out the name of the file trying to include it. +define _import-nodes-inner + $(foreach _in,$(2), \ + $(if $(wildcard $(_in)), \ + $(if $($(1).$(_in).seen), \ + $(eval ### "skipping already-imported $(_in)") \ + , \ + $(eval $(1).$(_in).seen := true) \ + $(call _import-node,$(1),$(strip $(_in)),$(3),$(4)) \ + ) \ + , \ + $(error $(1): "$(_in)" does not exist) \ + ) \ + ) +endef + +# +# $(1): output list variable name, like "PRODUCTS" or "DEVICES" +# $(2): list of makefiles representing nodes to import +# $(3): list of node variable names +# $(4): list with subset of variable names that take only a single value, instead +# of the default list semantics +# +define import-nodes +$(call dump-phase-start,$(1),$(2),$(3),$(4),build/make/core/node_fns.mk) \ +$(if \ + $(foreach _in,$(2), \ + $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \ + $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ + should be empty here: $(_include_stack))),) \ + $(eval _include_stack := ) \ + $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3),$(4)) \ + $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \ + $(eval _node_import_context :=) \ + $(eval $(1) := $($(1)) $(_in)) \ + $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ + should be empty here: $(_include_stack))),) \ + ) \ +,) \ +$(call dump-phase-end,build/make/core/node_fns.mk) +endef diff --git a/make/core/notice_files.mk b/make/core/notice_files.mk new file mode 100644 index 0000000..c05d4ea --- /dev/null +++ b/make/core/notice_files.mk @@ -0,0 +1,160 @@ +########################################################### +## Track NOTICE files +########################################################### +$(call record-module-type,NOTICE_FILE) + +ifneq ($(LOCAL_NOTICE_FILE),) +notice_file:=$(strip $(LOCAL_NOTICE_FILE)) +else +notice_file:=$(strip $(wildcard $(LOCAL_PATH)/LICENSE $(LOCAL_PATH)/LICENCE $(LOCAL_PATH)/NOTICE)) +endif + +ifneq (,$(strip $(LOCAL_LICENSE_PACKAGE_NAME))) +license_package_name:=$(strip $(LOCAL_LICENSE_PACKAGE_NAME)) +endif + +ifneq (,$(strip $(LOCAL_LICENSE_INSTALL_MAP))) +install_map:=$(strip $(LOCAL_LICENSE_INSTALL_MAP)) +else +install_map:= +endif + +ifneq (,$(strip $(LOCAL_LICENSE_KINDS))) +license_kinds:=$(strip $(LOCAL_LICENSE_KINDS)) +else +license_kinds:=legacy_by_exception_only +endif + +ifneq (,$(strip $(LOCAL_LICENSE_CONDITIONS))) +license_conditions:=$(strip $(LOCAL_LICENSE_CONDITIONS)) +else +license_conditions:=by_exception_only +endif + +ifeq ($(LOCAL_MODULE_CLASS),GYP) + # We ignore NOTICE files for modules of type GYP. + notice_file := +endif + +ifeq ($(LOCAL_MODULE_CLASS),FAKE) + # We ignore NOTICE files for modules of type FAKE. + notice_file := +endif + +# Soong generates stub libraries that don't need NOTICE files +ifdef LOCAL_NO_NOTICE_FILE + ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,LOCAL_NO_NOTICE_FILE should not be used by Android.mk files) + endif + notice_file := +endif + +ifeq ($(LOCAL_MODULE_CLASS),NOTICE_FILES) +# If this is a NOTICE-only module, we don't include base_rule.mk, +# so my_prefix is not set at this point. +ifeq ($(LOCAL_IS_HOST_MODULE),true) + my_prefix := HOST_ + LOCAL_HOST_PREFIX := +else + my_prefix := TARGET_ +endif +endif + +installed_notice_file := + +is_container:=$(strip $(LOCAL_MODULE_IS_CONTAINER)) +ifeq (,$(is_container)) +ifneq (,$(strip $(filter %.zip %.tar %.tgz %.tar.gz %.apk %.img %.srcszip %.apex, $(LOCAL_BUILT_MODULE)))) +is_container:=true +else +is_container:=false +endif +else ifneq (,$(strip $(filter-out true false,$(is_container)))) +$(error Unrecognized value '$(is_container)' for LOCAL_MODULE_IS_CONTAINER) +endif + +ifeq (true,$(is_container)) +# Include shared libraries' notices for "container" types, but not for binaries etc. +notice_deps := \ + $(strip \ + $(foreach d, \ + $(LOCAL_REQUIRED_MODULES) \ + $(LOCAL_STATIC_LIBRARIES) \ + $(LOCAL_WHOLE_STATIC_LIBRARIES) \ + $(LOCAL_SHARED_LIBRARIES) \ + $(LOCAL_DYLIB_LIBRARIES) \ + $(LOCAL_RLIB_LIBRARIES) \ + $(LOCAL_PROC_MACRO_LIBRARIES) \ + $(LOCAL_HEADER_LIBRARIES) \ + $(LOCAL_STATIC_JAVA_LIBRARIES) \ + $(LOCAL_JAVA_LIBRARIES) \ + $(LOCAL_JNI_SHARED_LIBRARIES) \ + ,$(subst :,_,$(d)):static \ + ) \ + ) +else +notice_deps := \ + $(strip \ + $(foreach d, \ + $(LOCAL_REQUIRED_MODULES) \ + $(LOCAL_STATIC_LIBRARIES) \ + $(LOCAL_WHOLE_STATIC_LIBRARIES) \ + $(LOCAL_RLIB_LIBRARIES) \ + $(LOCAL_PROC_MACRO_LIBRARIES) \ + $(LOCAL_HEADER_LIBRARIES) \ + $(LOCAL_STATIC_JAVA_LIBRARIES) \ + ,$(subst :,_,$(d)):static \ + )$(foreach d, \ + $(LOCAL_SHARED_LIBRARIES) \ + $(LOCAL_DYLIB_LIBRARIES) \ + $(LOCAL_JAVA_LIBRARIES) \ + $(LOCAL_JNI_SHARED_LIBRARIES) \ + ,$(subst :,_,$(d)):dynamic \ + ) \ + ) +endif +ifeq ($(LOCAL_IS_HOST_MODULE),true) +notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_HOST_REQUIRED_MODULES),$(subst :,_,$(d)):static)) +else +notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_TARGET_REQUIRED_MODULES),$(subst :,_,$(d)):static)) +endif + +local_path := $(LOCAL_PATH) + + +module_license_metadata := + +ifdef my_register_name + module_license_metadata := $(call local-intermediates-dir)/$(my_register_name).meta_lic + + $(foreach target,$(ALL_MODULES.$(my_register_name).BUILT) $(ALL_MODULES.$(my_register_name).INSTALLED) $(my_test_data) $(my_test_config),\ + $(eval ALL_TARGETS.$(target).META_LIC := $(module_license_metadata))) + + ALL_MODULES.$(my_register_name).META_LIC := $(strip $(ALL_MODULES.$(my_register_name).META_LIC) $(module_license_metadata)) + + ifdef LOCAL_SOONG_LICENSE_METADATA + # Soong modules have already produced a license metadata file, copy it to where Make expects it. + $(eval $(call copy-one-file, $(LOCAL_SOONG_LICENSE_METADATA), $(module_license_metadata))) + else + # Make modules don't have enough information to produce a license metadata rule until after fix-notice-deps + # has been called, store the necessary information until later. + ALL_MODULES.$(my_register_name).DELAYED_META_LIC := $(strip $(ALL_MODULES.$(my_register_name).DELAYED_META_LIC) $(module_license_metadata)) + ALL_MODULES.$(my_register_name).LICENSE_PACKAGE_NAME := $(strip $(license_package_name)) + ALL_MODULES.$(my_register_name).MODULE_TYPE := $(strip $(ALL_MODULES.$(my_register_name).MODULE_TYPE) $(LOCAL_MODULE_TYPE)) + ALL_MODULES.$(my_register_name).MODULE_CLASS := $(strip $(ALL_MODULES.$(my_register_name).MODULE_CLASS) $(LOCAL_MODULE_CLASS)) + ALL_MODULES.$(my_register_name).LICENSE_KINDS := $(ALL_MODULES.$(my_register_name).LICENSE_KINDS) $(license_kinds) + ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS := $(ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS) $(license_conditions) + ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP := $(ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP) $(install_map) + ALL_MODULES.$(my_register_name).NOTICE_DEPS := $(ALL_MODULES.$(my_register_name).NOTICE_DEPS) $(notice_deps) + ALL_MODULES.$(my_register_name).IS_CONTAINER := $(strip $(filter-out false,$(ALL_MODULES.$(my_register_name).IS_CONTAINER) $(is_container))) + ALL_MODULES.$(my_register_name).PATH := $(strip $(ALL_MODULES.$(my_register_name).PATH) $(local_path)) + endif +endif + +ifdef notice_file + +ifdef my_register_name +ALL_MODULES.$(my_register_name).NOTICES := $(ALL_MODULES.$(my_register_name).NOTICES) $(notice_file) +endif + +endif # notice_file diff --git a/make/core/os_licensing.mk b/make/core/os_licensing.mk new file mode 100644 index 0000000..416e4b2 --- /dev/null +++ b/make/core/os_licensing.mk @@ -0,0 +1,167 @@ +ifeq ($(TARGET_BUILD_APPS),) + +.PHONY: systemlicense +systemlicense: $(call corresponding-license-metadata, $(SYSTEM_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(SYSTEM_NOTICE_DEPS)) + +SYSTEM_NOTICE_DEPS += $(UNMOUNTED_NOTICE_DEPS) + +ifneq ($(PRODUCT_NOTICE_SPLIT),true) +$(eval $(call html-notice-rule,$(target_notice_file_html_gz),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS))) + +$(installed_notice_html_or_xml_gz): $(target_notice_file_html_gz) + $(copy-file-to-target) +else +$(eval $(call xml-notice-rule,$(target_notice_file_xml_gz),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS))) + +$(eval $(call text-notice-rule,$(target_notice_file_txt),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS))) + +$(installed_notice_html_or_xml_gz): $(target_notice_file_xml_gz) + $(copy-file-to-target) +endif + +$(call declare-0p-target,$(target_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_notice_html_or_xml_gz)) +endif + +.PHONY: vendorlicense +vendorlicense: $(call corresponding-license-metadata, $(VENDOR_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(VENDOR_NOTICE_DEPS)) + +VENDOR_NOTICE_DEPS += $(UNMOUNTED_NOTICE_DEPS) + +$(eval $(call text-notice-rule,$(target_vendor_notice_file_txt),"Vendor image", \ + "Notices for files contained in all filesystem images except system/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:", \ + $(VENDOR_NOTICE_DEPS),$(VENDOR_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_vendor_notice_file_xml_gz),"Vendor image", \ + "Notices for files contained in all filesystem images except system/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:", \ + $(VENDOR_NOTICE_DEPS),$(VENDOR_NOTICE_DEPS))) + +$(installed_vendor_notice_xml_gz): $(target_vendor_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_vendor_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_vendor_notice_xml_gz)) +endif + +.PHONY: odmlicense +odmlicense: $(call corresponding-license-metadata, $(ODM_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(ODM_NOTICE_DEPS)) +$(eval $(call text-notice-rule,$(target_odm_notice_file_txt),"ODM filesystem image", \ + "Notices for files contained in the odm filesystem image in this directory:", \ + $(ODM_NOTICE_DEPS),$(ODM_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_odm_notice_file_xml_gz),"ODM filesystem image", \ + "Notices for files contained in the odm filesystem image in this directory:", \ + $(ODM_NOTICE_DEPS),$(ODM_NOTICE_DEPS))) + +$(installed_odm_notice_xml_gz): $(target_odm_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_odm_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_odm_notice_xml_gz)) +endif + +.PHONY: oemlicense +oemlicense: $(call corresponding-license-metadata, $(OEM_NOTICE_DEPS)) reportmissinglicenses + +.PHONY: productlicense +productlicense: $(call corresponding-license-metadata, $(PRODUCT_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(PRODUCT_NOTICE_DEPS)) +$(eval $(call text-notice-rule,$(target_product_notice_file_txt),"Product image", \ + "Notices for files contained in the product filesystem image in this directory:", \ + $(PRODUCT_NOTICE_DEPS),$(PRODUCT_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_product_notice_file_xml_gz),"Product image", \ + "Notices for files contained in the product filesystem image in this directory:", \ + $(PRODUCT_NOTICE_DEPS),$(PRODUCT_NOTICE_DEPS))) + +$(installed_product_notice_xml_gz): $(target_product_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_product_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_product_notice_xml_gz)) +endif + +.PHONY: systemextlicense +systemextlicense: $(call corresponding-license-metadata, $(SYSTEM_EXT_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(SYSTEM_EXT_NOTICE_DEPS)) +$(eval $(call text-notice-rule,$(target_system_ext_notice_file_txt),"System_ext image", \ + "Notices for files contained in the system_ext filesystem image in this directory:", \ + $(SYSTEM_EXT_NOTICE_DEPS),$(SYSTEM_EXT_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_system_ext_notice_file_xml_gz),"System_ext image", \ + "Notices for files contained in the system_ext filesystem image in this directory:", \ + $(SYSTEM_EXT_NOTICE_DEPS),$(SYSTEM_EXT_NOTICE_DEPS))) + +$(installed_system_ext_notice_xml_gz): $(target_system_ext_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_system_ext_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_system_ext_notice_xml_gz)) +endif + +.PHONY: vendor_dlkmlicense +vendor_dlkmlicense: $(call corresponding-license-metadata, $(VENDOR_DLKM_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(VENDOR_DLKM_NOTICE_DEPS)) +$(eval $(call text-notice-rule,$(target_vendor_dlkm_notice_file_txt),"Vendor_dlkm image", \ + "Notices for files contained in the vendor_dlkm filesystem image in this directory:", \ + $(VENDOR_DLKM_NOTICE_DEPS),$(VENDOR_DLKM_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_vendor_dlkm_notice_file_xml_gz),"Vendor_dlkm image", \ + "Notices for files contained in the vendor_dlkm filesystem image in this directory:", \ + $(VENDOR_DLKM_NOTICE_DEPS),$(VENDOR_DLKM_NOTICE_DEPS))) + +$(installed_vendor_dlkm_notice_xml_gz): $(target_vendor_dlkm_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_vendor_dlkm_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_vendor_dlkm_notice_xml_gz)) +endif + +.PHONY: odm_dlkmlicense +odm_dlkmlicense: $(call corresponding-license-metadata, $(ODM_DLKM_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(ODM_DLKM_NOTICE_DEPS)) +$(eval $(call text-notice-rule,$(target_odm_dlkm_notice_file_txt),"ODM_dlkm filesystem image", \ + "Notices for files contained in the odm_dlkm filesystem image in this directory:", \ + $(ODM_DLKM_NOTICE_DEPS),$(ODM_DLKM_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_odm_dlkm_notice_file_xml_gz),"ODM_dlkm filesystem image", \ + "Notices for files contained in the odm_dlkm filesystem image in this directory:", \ + $(ODM_DLKM_NOTICE_DEPS),$(ODM_DLKM_NOTICE_DEPS))) + +$(installed_odm_dlkm_notice_xml_gz): $(target_odm_dlkm_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_odm_dlkm_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_odm_dlkm_notice_xml_gz)) +endif + +.PHONY: system_dlkmlicense +system_dlkmlicense: $(call corresponding-license-metadata, $(SYSTEM_DLKM_NOTICE_DEPS)) reportmissinglicenses + +ifneq (,$(SYSTEM_DLKM_NOTICE_DEPS)) +$(eval $(call text-notice-rule,$(target_system_dlkm_notice_file_txt),"System_dlkm filesystem image", \ + "Notices for files contained in the system_dlkm filesystem image in this directory:", \ + $(SYSTEM_DLKM_NOTICE_DEPS),$(SYSTEM_DLKM_NOTICE_DEPS))) + +$(eval $(call xml-notice-rule,$(target_system_dlkm_notice_file_xml_gz),"System_dlkm filesystem image", \ + "Notices for files contained in the system_dlkm filesystem image in this directory:", \ + $(SYSTEM_DLKM_NOTICE_DEPS),$(SYSTEM_DLKM_NOTICE_DEPS))) + +$(installed_system_dlkm_notice_xml_gz): $(target_system_dlkm_notice_file_xml_gz) + $(copy-file-to-target) + +$(call declare-0p-target,$(target_system_dlkm_notice_file_xml_gz)) +$(call declare-0p-target,$(installed_sysetm_dlkm_notice_xml_gz)) +endif + +endif # not TARGET_BUILD_APPS diff --git a/make/core/pack_dyn_relocs_setup.mk b/make/core/pack_dyn_relocs_setup.mk new file mode 100644 index 0000000..f86e11e --- /dev/null +++ b/make/core/pack_dyn_relocs_setup.mk @@ -0,0 +1,43 @@ +############################################################# +## Set up my_pack_module_relocations +## Input variables: +## DISABLE_RELOCATION_PACKER, +## LOCAL_PACK_MODULE_RELOCATIONS*, +## *TARGET_PACK_MODULE_RELOCATIONS, +## LOCAL_MODULE_CLASS, HOST_OS +## LOCAL_IS_HOST_MODULE +## Output variables: +## my_pack_module_relocations, if false skip relocation_packer +############################################################# + +my_pack_module_relocations := false +ifneq ($(DISABLE_RELOCATION_PACKER),true) + my_pack_module_relocations := $(firstword \ + $(LOCAL_PACK_MODULE_RELOCATIONS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \ + $(LOCAL_PACK_MODULE_RELOCATIONS)) +endif + +ifeq ($(my_pack_module_relocations),) + my_pack_module_relocations := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PACK_MODULE_RELOCATIONS) +endif + +# Do not pack relocations for executables. Because packing results in +# non-zero p_vaddr which causes kernel to load executables to lower +# address (starting at 0x8000) http://b/20665974 +ifeq ($(filter SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),) + my_pack_module_relocations := false +endif + +ifdef LOCAL_IS_HOST_MODULE + # Do not pack relocations on host modules + my_pack_module_relocations := false +endif + +# Lld relocation packing cannot be enabled for binaries before Android Pie. +ifneq ($(LOCAL_SDK_VERSION),) + ifneq ($(LOCAL_SDK_VERSION),current) + ifeq ($(call math_lt,$(LOCAL_SDK_VERSION),28),true) + my_pack_module_relocations := false + endif + endif +endif diff --git a/make/core/package.mk b/make/core/package.mk new file mode 100644 index 0000000..591f7aa --- /dev/null +++ b/make/core/package.mk @@ -0,0 +1,64 @@ +# We don't automatically set up rules to build packages for both +# TARGET_ARCH and TARGET_2ND_ARCH. +# To build it for TARGET_2ND_ARCH in a 64bit product, use "LOCAL_MULTILIB := 32". + +$(call record-module-type,PACKAGE) + +my_prefix := TARGET_ +include $(BUILD_SYSTEM)/multilib.mk + +ifeq ($(TARGET_SUPPORTS_32_BIT_APPS)|$(TARGET_SUPPORTS_64_BIT_APPS),true|true) + # packages default to building for either architecture, + # the preferred if its supported, otherwise the non-preferred. +else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true) + # only 64-bit apps supported + ifeq ($(filter $(my_module_multilib),64 both first),$(my_module_multilib)) + # if my_module_multilib was 64, both, first, or unset, build for 64-bit + my_module_multilib := 64 + else + # otherwise don't build this app + my_module_multilib := none + endif +else + # only 32-bit apps supported + ifeq ($(filter $(my_module_multilib),32 both),$(my_module_multilib)) + # if my_module_multilib was 32, both, or unset, build for 32-bit + my_module_multilib := 32 + else ifeq ($(my_module_multilib),first) + ifndef TARGET_IS_64_BIT + # if my_module_multilib was first and this is a 32-bit build, build for + # 32-bit + my_module_multilib := 32 + else + # if my_module_multilib was first and this is a 64-bit build, don't build + # this app + my_module_multilib := none + endif + else + # my_module_mulitlib was 64 or none, don't build this app + my_module_multilib := none + endif +endif + +LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true +LOCAL_2ND_ARCH_VAR_PREFIX := + +# check if preferred arch is supported +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# first arch is supported +include $(BUILD_SYSTEM)/package_internal.mk +else ifneq (,$(TARGET_2ND_ARCH)) +LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) +# check if non-preferred arch is supported +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# secondary arch is supported +include $(BUILD_SYSTEM)/package_internal.mk +endif +endif # TARGET_2ND_ARCH + +LOCAL_2ND_ARCH_VAR_PREFIX := +LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := + +my_module_arch_supported := diff --git a/make/core/package_internal.mk b/make/core/package_internal.mk new file mode 100644 index 0000000..c7a173b --- /dev/null +++ b/make/core/package_internal.mk @@ -0,0 +1,731 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +########################################################### +## Standard rules for building an application package. +## +## Additional inputs from base_rules.make: +## LOCAL_PACKAGE_NAME: The name of the package; the directory +## will be called this. +## +## MODULE, MODULE_PATH, and MODULE_SUFFIX will +## be set for you. +########################################################### + +LOCAL_PACKAGE_NAME := $(strip $(LOCAL_PACKAGE_NAME)) +ifeq ($(LOCAL_PACKAGE_NAME),) +$(error $(LOCAL_PATH): Package modules must define LOCAL_PACKAGE_NAME) +endif + +ifneq ($(strip $(LOCAL_MODULE_SUFFIX)),) +$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_SUFFIX) +endif +LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) + +ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) +$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_STEM or LOCAL_BUILT_MODULE_STEM) +endif + +ifneq ($(strip $(LOCAL_MODULE)),) +$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE) +endif +LOCAL_MODULE := $(LOCAL_PACKAGE_NAME) + +ifneq ($(strip $(LOCAL_MODULE_CLASS)),) +$(error $(LOCAL_PATH): Package modules may not set LOCAL_MODULE_CLASS) +endif +LOCAL_MODULE_CLASS := APPS + +intermediates := $(call local-intermediates-dir) +intermediates.COMMON := $(call local-intermediates-dir,COMMON) + +# Package LOCAL_MODULE_TAGS default to optional +LOCAL_MODULE_TAGS := $(strip $(LOCAL_MODULE_TAGS)) +ifeq ($(LOCAL_MODULE_TAGS),) +LOCAL_MODULE_TAGS := optional +endif + +ifeq ($(filter tests, $(LOCAL_MODULE_TAGS)),) +# Force localization check if it's not tagged as tests. +LOCAL_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) -z +endif + +need_compile_asset := +ifeq (,$(LOCAL_ASSET_DIR)) +LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets +else +need_compile_asset := true +endif + +# LOCAL_RESOURCE_DIR may point to resource generated during the build +need_compile_res := +ifeq (,$(LOCAL_RESOURCE_DIR)) + LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +else + need_compile_res := true + LOCAL_RESOURCE_DIR := $(foreach d,$(LOCAL_RESOURCE_DIR),$(call clean-path,$(d))) +endif + +# If LOCAL_MODULE matches a rule in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES, +# override the manifest package name by the (first) rule matched +override_manifest_name := $(strip $(word 1,\ + $(foreach rule,$(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES),\ + $(eval _pkg_name_pat := $(call word-colon,1,$(rule)))\ + $(eval _manifest_name_pat := $(call word-colon,2,$(rule)))\ + $(if $(filter $(_pkg_name_pat),$(LOCAL_MODULE)),\ + $(patsubst $(_pkg_name_pat),$(_manifest_name_pat),$(LOCAL_MODULE))\ + )\ + )\ +)) + +ifneq (,$(override_manifest_name)) +# Note: this can override LOCAL_MANIFEST_PACKAGE_NAME value set in Android.mk +LOCAL_MANIFEST_PACKAGE_NAME := $(override_manifest_name) +endif + +include $(BUILD_SYSTEM)/force_aapt2.mk +# validate that app contains a manifest file for aapt2 +ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE))) + ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml)) + $(call pretty-error,App missing manifest file which is required by aapt2. \ +Provide a manifest file by either setting LOCAL_MANIFEST_FILE in Android.mk \ +or via a AndroidManifest.xml in this directory) + endif +endif + +# Process Support Library dependencies. +include $(BUILD_SYSTEM)/support_libraries.mk + +# Determine whether auto-RRO is enabled for this package. +enforce_rro_enabled := +ifneq (,$(filter *, $(PRODUCT_ENFORCE_RRO_TARGETS))) + # * means all system and system_ext APKs, so enable conditionally based on module path. + + # Note that base_rules.mk has not yet been included, so it's likely that only + # one of LOCAL_MODULE_PATH and the LOCAL_X_MODULE flags has been set. + ifeq (,$(LOCAL_MODULE_PATH)) + non_rro_target_module := $(filter true,\ + $(LOCAL_ODM_MODULE) \ + $(LOCAL_OEM_MODULE) \ + $(LOCAL_PRODUCT_MODULE) \ + $(LOCAL_PROPRIETARY_MODULE) \ + $(LOCAL_VENDOR_MODULE)) + enforce_rro_enabled := $(if $(non_rro_target_module),,true) + else ifneq ($(filter $(TARGET_OUT)/%,$(LOCAL_MODULE_PATH)),) + enforce_rro_enabled := true + endif +else ifneq (,$(filter $(LOCAL_PACKAGE_NAME), $(PRODUCT_ENFORCE_RRO_TARGETS))) + enforce_rro_enabled := true +endif + +product_package_overlays := $(strip \ + $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \ + $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR))))) +device_package_overlays := $(strip \ + $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \ + $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR))))) + +static_resource_overlays := +runtime_resource_overlays_product := +runtime_resource_overlays_vendor := +ifdef enforce_rro_enabled + ifneq ($(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS),) + # The PRODUCT_ exclusion variable applies to both inclusion variables.. + static_resource_overlays += $(filter $(addsuffix %,$(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)),$(product_package_overlays)) + static_resource_overlays += $(filter $(addsuffix %,$(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)),$(device_package_overlays)) + endif + runtime_resource_overlays_product := $(filter-out $(static_resource_overlays),$(product_package_overlays)) + runtime_resource_overlays_vendor := $(filter-out $(static_resource_overlays),$(device_package_overlays)) +else + static_resource_overlays := $(product_package_overlays) $(device_package_overlays) +endif + +# Add the static overlays. Auto-RRO is created later, as it depends on +# other logic in this file. +LOCAL_RESOURCE_DIR := $(static_resource_overlays) $(LOCAL_RESOURCE_DIR) + +all_assets := $(strip \ + $(foreach dir, $(LOCAL_ASSET_DIR), \ + $(addprefix $(dir)/, \ + $(patsubst assets/%,%, \ + $(call find-subdir-assets, $(dir)) \ + ) \ + ) \ + )) + +ifneq ($(all_assets),) +need_compile_asset := true +endif + +my_res_package := +# In aapt2 the last takes precedence. +my_resource_dirs := $(call reverse-list,$(LOCAL_RESOURCE_DIR)) +my_res_dir := +my_overlay_res_dirs := + +ifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),) +# If we are using static android libraries, every source file becomes an overlay. +# This is to emulate old AAPT behavior which simulated library support. +my_res_dir := +my_overlay_res_dirs := $(my_resource_dirs) +else +# Without static libraries, the first directory is our directory, which can then be +# overlaid by the rest. (First directory in my_resource_dirs is last directory in +# $(LOCAL_RESOURCE_DIR) due to it being reversed. +my_res_dir := $(firstword $(my_resource_dirs)) +my_overlay_res_dirs := $(wordlist 2,999,$(my_resource_dirs)) +endif + +my_overlay_resources := $(strip \ + $(foreach d,$(my_overlay_res_dirs),\ + $(addprefix $(d)/, \ + $(call find-subdir-assets,$(d))))) + +my_res_resources := $(if $(my_res_dir),$(strip \ + $(addprefix $(my_res_dir)/, \ + $(call find-subdir-assets,$(my_res_dir))))) + +all_resources := $(strip $(my_res_resources) $(my_overlay_resources)) + +# The linked resource package. +my_res_package := $(intermediates)/package-res.apk +LOCAL_INTERMEDIATE_TARGETS += $(my_res_package) + +my_bundle_module := $(intermediates)/base.zip +LOCAL_INTERMEDIATE_TARGETS += $(my_bundle_module) + +# Always run aapt2, because we need to at least compile the AndroidManifest.xml. +need_compile_res := true + +ifneq ($(all_resources),) + need_compile_res := true +endif + +all_res_assets := $(strip $(all_assets) $(all_resources)) + +# If no assets or resources were found, clear the directory variables so +# we don't try to build them. +ifneq (true,$(need_compile_asset)) +LOCAL_ASSET_DIR:= +endif +ifneq (true,$(need_compile_res)) +LOCAL_RESOURCE_DIR:= +R_file_stamp := +else +# Make sure that R_file_stamp inherits the proper PRIVATE vars. +# If R.stamp moves, be sure to update the framework makefile, +# which has intimate knowledge of its location. +R_file_stamp := $(intermediates.COMMON)/src/R.stamp +LOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp) +endif + +ifdef LOCAL_COMPRESSED_MODULE +ifneq (true,$(LOCAL_COMPRESSED_MODULE)) +$(call pretty-error, Unknown value for LOCAL_COMPRESSED_MODULE $(LOCAL_COMPRESSED_MODULE)) +endif +endif + +ifdef LOCAL_COMPRESSED_MODULE +PACKAGES.$(LOCAL_PACKAGE_NAME).COMPRESSED := gz +LOCAL_BUILT_MODULE_STEM := package.apk.gz +LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk.gz +else # !LOCAL_COMPRESSED_MODULE +LOCAL_BUILT_MODULE_STEM := package.apk +LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk +endif + +LOCAL_PROGUARD_ENABLED:=$(strip $(LOCAL_PROGUARD_ENABLED)) +ifndef LOCAL_PROGUARD_ENABLED +ifneq ($(DISABLE_PROGUARD),true) + LOCAL_PROGUARD_ENABLED :=full +endif +endif +ifeq ($(LOCAL_PROGUARD_ENABLED),disabled) + # the package explicitly request to disable proguard. + LOCAL_PROGUARD_ENABLED := +endif +proguard_options_file := +ifneq ($(LOCAL_PROGUARD_ENABLED),custom) +ifeq ($(need_compile_res),true) + proguard_options_file := $(intermediates.COMMON)/proguard_options +endif # need_compile_res +endif # !custom +LOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS) +LOCAL_PROGUARD_FLAGS_DEPS += $(proguard_options_file) + +ifeq (true,$(EMMA_INSTRUMENT)) +ifndef LOCAL_EMMA_INSTRUMENT +# No jacoco for test apks. +ifeq (,$(LOCAL_INSTRUMENTATION_FOR)) +LOCAL_EMMA_INSTRUMENT := true +endif # No test apk +endif # LOCAL_EMMA_INSTRUMENT is not set +else +LOCAL_EMMA_INSTRUMENT := false +endif # EMMA_INSTRUMENT is true + +ifeq (true,$(LOCAL_EMMA_INSTRUMENT)) +ifeq (true,$(EMMA_INSTRUMENT_STATIC)) +ifneq ($(LOCAL_SRC_FILES)$(LOCAL_SRCJARS)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),) +# Only add jacocoagent if the package contains some java code +LOCAL_STATIC_JAVA_LIBRARIES += jacocoagent +# Exclude jacoco classes from proguard +LOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags +LOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags +endif # Contains java code +else +ifdef LOCAL_SDK_VERSION +ifdef TARGET_BUILD_APPS +# In unbundled build, merge the coverage library into the apk. +ifneq ($(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),) +# Only add jacocoagent if the package contains some java code +LOCAL_STATIC_JAVA_LIBRARIES += jacocoagent +# Exclude jacoco classes from proguard +LOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags +LOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags +endif # Contains java code +endif # TARGET_BUILD_APPS +endif # LOCAL_SDK_VERSION +endif # EMMA_INSTRUMENT_STATIC +endif # LOCAL_EMMA_INSTRUMENT + +rs_compatibility_jni_libs := + +# If the module is a compressed module, we don't pre-opt it because its final +# installation location will be the data partition. +ifdef LOCAL_COMPRESSED_MODULE +LOCAL_DEX_PREOPT := false +endif + +# Default to use uncompressed native libraries in APKs if minSdkVersion >= marshmallow +ifndef LOCAL_USE_EMBEDDED_NATIVE_LIBS + LOCAL_USE_EMBEDDED_NATIVE_LIBS := $(call math_gt_or_eq, \ + $(patsubst $(PLATFORM_VERSION_CODENAME),100,$(call module-min-sdk-version)),23) +endif + +include $(BUILD_SYSTEM)/android_manifest.mk + +resource_export_package := + +include $(BUILD_SYSTEM)/java_renderscript.mk + +include $(BUILD_SYSTEM)/aapt_flags.mk + +ifeq ($(need_compile_res),true) + +############################### +## APK splits +built_apk_splits := +installed_apk_splits := +my_apk_split_configs := + +ifdef LOCAL_PACKAGE_SPLITS +ifdef LOCAL_COMPRESSED_MODULE +$(error $(LOCAL_MODULE): LOCAL_COMPRESSED_MODULE is not currently supported for split installs) +endif # LOCAL_COMPRESSED_MODULE + +my_apk_split_configs := $(LOCAL_PACKAGE_SPLITS) +my_split_suffixes := $(subst $(comma),_,$(my_apk_split_configs)) +built_apk_splits := $(foreach s,$(my_split_suffixes),$(intermediates)/package_$(s).apk) +endif + +$(R_file_stamp) $(my_res_package): PRIVATE_AAPT_FLAGS := $(filter-out --legacy,$(LOCAL_AAPT_FLAGS)) +$(R_file_stamp) $(my_res_package): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(TARGET_AAPT_CHARACTERISTICS) +$(R_file_stamp) $(my_res_package): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_MANIFEST_PACKAGE_NAME) +$(R_file_stamp) $(my_res_package): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := $(LOCAL_MANIFEST_INSTRUMENTATION_FOR) + +############################### +## AAPT2 + +my_compiled_res_base_dir := $(intermediates.COMMON)/flat-res +ifneq (,$(filter-out current,$(renderscript_target_api))) + ifneq ($(call math_gt_or_eq,$(renderscript_target_api),21),true) + my_generated_res_zips := $(rs_generated_res_zip) + endif # renderscript_target_api < 21 +endif # renderscript_target_api is set +my_asset_dirs := $(LOCAL_ASSET_DIR) +my_full_asset_paths := $(all_assets) + +# Add AAPT2 link specific flags. +ifndef LOCAL_AAPT_NAMESPACES + $(my_res_package): PRIVATE_AAPT_FLAGS += --no-static-lib-packages +endif + +include $(BUILD_SYSTEM)/aapt2.mk + +endif # need_compile_res + +my_dex_jar := $(intermediates.COMMON)/dex.jar + +called_from_package_internal := true +################################# +include $(BUILD_SYSTEM)/java.mk +################################# +called_from_package_internal := + +ifeq ($(need_compile_res),true) + +# Other modules should depend on the BUILT module if +# they want to use this module's R.java file. +$(LOCAL_BUILT_MODULE): $(R_file_stamp) + +ifneq ($(full_classes_jar),) +# The R.java file must exist by the time the java source +# list is generated +$(java_source_list_file): $(R_file_stamp) +endif + +endif # need_compile_res + +LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION)) +ifeq ($(LOCAL_SDK_RES_VERSION),) + LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION) +endif + +$(LOCAL_INTERMEDIATE_TARGETS): \ + PRIVATE_ANDROID_MANIFEST := $(full_android_manifest) + +framework_res_package_export := + +ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) +# Most packages should link against the resources defined by framework-res. +# Even if they don't have their own resources, they may use framework +# resources. +ifeq ($(LOCAL_SDK_RES_VERSION),core_current) +# core_current doesn't contain any framework resources. +else ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),) +# for released sdk versions, the platform resources were built into android.jar. +framework_res_package_export := \ + $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION)) +else # LOCAL_SDK_RES_VERSION +framework_res_package_export := \ + $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk +endif # LOCAL_SDK_RES_VERSION +endif # LOCAL_NO_STANDARD_LIBRARIES + +all_library_res_package_exports := \ + $(framework_res_package_export) \ + $(foreach lib,$(LOCAL_RES_LIBRARIES),\ + $(call intermediates-dir-for,APPS,$(lib),,COMMON)/package-export.apk) + +all_library_res_package_export_deps := \ + $(framework_res_package_export) \ + $(foreach lib,$(LOCAL_RES_LIBRARIES),\ + $(call intermediates-dir-for,APPS,$(lib),,COMMON)/src/R.stamp) +$(resource_export_package) $(R_file_stamp) $(LOCAL_BUILT_MODULE): $(all_library_res_package_export_deps) +$(LOCAL_INTERMEDIATE_TARGETS): \ + PRIVATE_AAPT_INCLUDES := $(all_library_res_package_exports) + +$(my_res_package) : $(all_library_res_package_export_deps) + +ifneq ($(full_classes_jar),) +$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(built_dex) +# Use the jarjar processed arhive as the initial package file. +$(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar) +$(LOCAL_BUILT_MODULE): $(built_dex) $(full_classes_pre_proguard_jar) +else +$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := +$(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE := +endif # full_classes_jar + +include $(BUILD_SYSTEM)/install_jni_libs.mk + +# Pick a key to sign the package with. If this package hasn't specified +# an explicit certificate, use the default. +# Secure release builds will have their packages signed after the fact, +# so it's ok for these private keys to be in the clear. +ifeq ($(LOCAL_CERTIFICATE),) + LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) +endif + +ifeq ($(LOCAL_CERTIFICATE),EXTERNAL) + # The special value "EXTERNAL" means that we will sign it with the + # default devkey, apply predexopt, but then expect the final .apk + # (after dexopting) to be signed by an outside tool. + LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) + PACKAGES.$(LOCAL_PACKAGE_NAME).EXTERNAL_KEY := 1 +endif + +# If this is not an absolute certificate, assign it to a generic one. +ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./) + LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE) +endif +include $(BUILD_SYSTEM)/app_certificate_validate.mk +private_key := $(LOCAL_CERTIFICATE).pk8 +certificate := $(LOCAL_CERTIFICATE).x509.pem +additional_certificates := $(foreach c,$(LOCAL_ADDITIONAL_CERTIFICATES), $(c).x509.pem $(c).pk8) + +$(LOCAL_BUILT_MODULE): $(private_key) $(certificate) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH) +$(LOCAL_BUILT_MODULE): PRIVATE_PRIVATE_KEY := $(private_key) +$(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE := $(certificate) + +PACKAGES.$(LOCAL_PACKAGE_NAME).PRIVATE_KEY := $(private_key) +PACKAGES.$(LOCAL_PACKAGE_NAME).CERTIFICATE := $(certificate) + +$(LOCAL_BUILT_MODULE): $(additional_certificates) +$(LOCAL_BUILT_MODULE): PRIVATE_ADDITIONAL_CERTIFICATES := $(additional_certificates) + +$(LOCAL_BUILT_MODULE): $(LOCAL_CERTIFICATE_LINEAGE) +$(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE_LINEAGE := $(LOCAL_CERTIFICATE_LINEAGE) + +$(LOCAL_BUILT_MODULE): PRIVATE_ROTATION_MIN_SDK_VERSION := $(LOCAL_ROTATION_MIN_SDK_VERSION) + +# Set a actual_partition_tag (calculated in base_rules.mk) for the package. +PACKAGES.$(LOCAL_PACKAGE_NAME).PARTITION := $(actual_partition_tag) + +# Define the rule to build the actual package. +# PRIVATE_JNI_SHARED_LIBRARIES is a list of :. +$(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis) +# PRIVATE_JNI_SHARED_LIBRARIES_ABI is a list of ABI names. +$(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis) +ifneq ($(TARGET_BUILD_APPS),) + # Include all resources for unbundled apps. + LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true +endif +ifeq ($(LOCAL_AAPT_INCLUDE_ALL_RESOURCES),true) + $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_CONFIG := + $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := +else + $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_CONFIG := $(PRODUCT_AAPT_CONFIG) +ifdef LOCAL_PACKAGE_SPLITS + $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := +else + $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := $(PRODUCT_AAPT_PREF_CONFIG) +endif +endif + +# Run veridex on product, system_ext and vendor modules. +# We skip it for unbundled app builds where we cannot build veridex. +module_run_appcompat := +ifeq (true,$(non_system_module)) +ifeq (,$(TARGET_BUILD_APPS)) # ! unbundled app build +ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) + module_run_appcompat := true +endif +endif +endif + +ifeq ($(module_run_appcompat),true) +$(LOCAL_BUILT_MODULE) : $(appcompat-files) +$(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE) +endif + +$(LOCAL_BUILT_MODULE): PRIVATE_RESOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/resources +$(LOCAL_BUILT_MODULE) : $(jni_shared_libraries) +$(LOCAL_BUILT_MODULE) : $(JAR_ARGS) $(SOONG_ZIP) $(MERGE_ZIPS) $(ZIP2ZIP) +$(LOCAL_BUILT_MODULE): PRIVATE_RES_PACKAGE := $(my_res_package) +$(LOCAL_BUILT_MODULE) : $(my_res_package) $(AAPT2) +ifdef LOCAL_COMPRESSED_MODULE +$(LOCAL_BUILT_MODULE) : $(MINIGZIP) +endif +ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) +$(LOCAL_BUILT_MODULE) : $(ZIP2ZIP) +endif +ifeq ($(full_classes_jar),) + # We don't build jar, need to add the Java resources here. + $(LOCAL_BUILT_MODULE): $(java_resource_sources) +endif +$(LOCAL_BUILT_MODULE): PRIVATE_USE_EMBEDDED_NATIVE_LIBS := $(LOCAL_USE_EMBEDDED_NATIVE_LIBS) +$(LOCAL_BUILT_MODULE): + @echo "target Package: $(PRIVATE_MODULE) ($@)" + rm -rf $@.parts + mkdir -p $@.parts + cp -f $(PRIVATE_RES_PACKAGE) $@.parts/apk.zip +ifneq ($(jni_shared_libraries),) + $(call create-jni-shared-libs-package,$@.parts/jni.zip,$(PRIVATE_USE_EMBEDDED_NATIVE_LIBS)) +endif +ifeq ($(full_classes_jar),) +# We don't build jar, need to add the Java resources here. + $(if $(PRIVATE_EXTRA_JAR_ARGS),$(call create-java-resources-jar,$@.parts/res.zip)) +else # full_classes_jar + $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) + $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) +endif # full_classes_jar + $(MERGE_ZIPS) $@ $@.parts/*.zip + rm -rf $@.parts +ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) + @# No need to align, sign-package below will do it. + $(uncompress-dexs) +endif +# Run appcompat before signing. +ifeq ($(module_run_appcompat),true) + $(appcompat-header) + $(run-appcompat) +endif # module_run_appcompat + $(sign-package) +ifdef LOCAL_COMPRESSED_MODULE + $(compress-package) +endif # LOCAL_COMPRESSED_MODULE + +my_package_res_pb := $(intermediates)/package-res.pb.apk +$(my_package_res_pb): $(my_res_package) $(AAPT2) + $(AAPT2) convert --output-format proto $< -o $@ + +$(my_bundle_module): $(my_package_res_pb) +$(my_bundle_module): PRIVATE_RES_PACKAGE := $(my_package_res_pb) + +$(my_bundle_module): $(jni_shared_libraries) +$(my_bundle_module): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis) +$(my_bundle_module): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis) + +ifneq ($(full_classes_jar),) + $(my_bundle_module): PRIVATE_DEX_FILE := $(built_dex) + # Use the jarjar processed archive as the initial package file. + $(my_bundle_module): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar) + $(my_bundle_module): $(built_dex) +else + $(my_bundle_module): PRIVATE_DEX_FILE := + $(my_bundle_module): PRIVATE_SOURCE_ARCHIVE := + # We don't build jar, need to add the Java resources here. + $(my_bundle_module): $(java_resource_sources) +endif # full_classes_jar + +$(my_bundle_module): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP) + @echo "target Bundle: $(PRIVATE_MODULE) ($@)" + rm -rf $@.parts + mkdir -p $@.parts + $(ZIP2ZIP) -i $(PRIVATE_RES_PACKAGE) -o $@.parts/apk.zip AndroidManifest.xml:manifest/AndroidManifest.xml resources.pb "res/**/*" "assets/**/*" + ifneq ($(jni_shared_libraries),) + $(call create-jni-shared-libs-package,$@.parts/jni.zip) + endif + ifeq ($(full_classes_jar),) + # We don't build jar, need to add the Java resources here. + $(if $(PRIVATE_EXTRA_JAR_ARGS),\ + $(call create-java-resources-jar,$@.parts/res.zip) && \ + $(ZIP2ZIP) -i $@.parts/res.zip -o $@.parts/res.zip.tmp "**/*:root/" && \ + mv -f $@.parts/res.zip.tmp $@.parts/res.zip) + else # full_classes_jar + $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) + $(ZIP2ZIP) -i $@.parts/dex.zip -o $@.parts/dex.zip.tmp "classes*.dex:dex/" + mv -f $@.parts/dex.zip.tmp $@.parts/dex.zip + $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) + $(ZIP2ZIP) -i $@.parts/res.zip -o $@.parts/res.zip.tmp "**/*:root/" + mv -f $@.parts/res.zip.tmp $@.parts/res.zip + endif # full_classes_jar + $(MERGE_ZIPS) $@ $@.parts/*.zip + rm -rf $@.parts +ALL_MODULES.$(my_register_name).BUNDLE := $(my_bundle_module) + +ifdef TARGET_BUILD_APPS + ifdef LOCAL_DPI_VARIANTS + $(call pretty-error,Building DPI-specific APKs is no longer supported) + endif +endif + +############################### +## Rule to build a jar containing dex files to dexpreopt without waiting for +## the APK +ifdef LOCAL_DEX_PREOPT + $(my_dex_jar): PRIVATE_DEX_FILE := $(built_dex) + $(my_dex_jar): $(built_dex) $(SOONG_ZIP) + $(hide) mkdir -p $(dir $@) && rm -f $@ + $(call create-dex-jar,$@,$(PRIVATE_DEX_FILE)) +endif + +############################### +## APK splits +ifdef LOCAL_PACKAGE_SPLITS +# The splits should have been built in the same command building the base apk. +# This rule just runs signing. +# Note that we explicily check the existence of the split apk and remove the +# built base apk if the split apk isn't there. +# That way the build system will rerun the aapt after the user changes the splitting parameters. +$(built_apk_splits): PRIVATE_PRIVATE_KEY := $(private_key) +$(built_apk_splits): PRIVATE_CERTIFICATE := $(certificate) +$(built_apk_splits) : $(intermediates)/%.apk : $(LOCAL_BUILT_MODULE) + $(hide) if [ ! -f $@ ]; then \ + echo 'No $@ generated, check your apk splitting parameters.' 1>&2; \ + rm $<; exit 1; \ + fi + $(sign-package) + +# Rules to install the splits +installed_apk_splits := $(foreach s,$(my_split_suffixes),$(my_module_path)/$(LOCAL_MODULE)_$(s).apk) +$(installed_apk_splits) : $(my_module_path)/$(LOCAL_MODULE)_%.apk : $(intermediates)/package_%.apk + @echo "Install: $@" + $(copy-file-to-new-target) + +# Register the additional built and installed files. +ALL_MODULES.$(my_register_name).INSTALLED += $(installed_apk_splits) +ALL_MODULES.$(my_register_name).BUILT_INSTALLED += \ + $(foreach s,$(my_split_suffixes),$(intermediates)/package_$(s).apk:$(my_module_path)/$(LOCAL_MODULE)_$(s).apk) + +# Make sure to install the splits when you run "make ". +$(my_all_targets): $(installed_apk_splits) + +ifdef LOCAL_COMPATIBILITY_SUITE + +$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(foreach s,$(my_split_suffixes),\ + $(call compat-copy-pair,$(intermediates)/package_$(s).apk,$(dir)/$(LOCAL_MODULE)_$(s).apk))))) + +$(call create-suite-dependencies) + +endif # LOCAL_COMPATIBILITY_SUITE +endif # LOCAL_PACKAGE_SPLITS + +# Save information about this package +PACKAGES.$(LOCAL_PACKAGE_NAME).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) +PACKAGES.$(LOCAL_PACKAGE_NAME).RESOURCE_FILES := $(all_resources) + +ifneq ($(LOCAL_MODULE_STEM),) + PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM) +else + PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE) +endif + +PACKAGES := $(PACKAGES) $(LOCAL_PACKAGE_NAME) + +# Reset internal variables. +all_res_assets := + +ifneq (,$(runtime_resource_overlays_product)$(runtime_resource_overlays_vendor)) + ifdef LOCAL_EXPORT_PACKAGE_RESOURCES + enforce_rro_use_res_lib := true + else + enforce_rro_use_res_lib := false + endif + + ifdef LOCAL_MANIFEST_PACKAGE_NAME + enforce_rro_is_manifest_package_name := true + enforce_rro_manifest_package_info := $(LOCAL_MANIFEST_PACKAGE_NAME) + else + enforce_rro_is_manifest_package_name := false + enforce_rro_manifest_package_info := $(full_android_manifest) + endif + + ifdef runtime_resource_overlays_product + $(call append_enforce_rro_sources, \ + $(my_register_name), \ + $(enforce_rro_is_manifest_package_name), \ + $(enforce_rro_manifest_package_info), \ + $(enforce_rro_use_res_lib), \ + $(runtime_resource_overlays_product), \ + product \ + ) + endif + ifdef runtime_resource_overlays_vendor + $(call append_enforce_rro_sources, \ + $(my_register_name), \ + $(enforce_rro_is_manifest_package_name), \ + $(enforce_rro_manifest_package_info), \ + $(enforce_rro_use_res_lib), \ + $(runtime_resource_overlays_vendor), \ + vendor \ + ) + endif +endif diff --git a/make/core/pathmap.mk b/make/core/pathmap.mk new file mode 100644 index 0000000..dacbe21 --- /dev/null +++ b/make/core/pathmap.mk @@ -0,0 +1,94 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# A central place to define mappings to paths, to avoid hard-coding +# them in Android.mk files. Not meant for header file include directories, +# despite the fact that it was historically used for that! +# +# If you want this for a library's header files, use LOCAL_EXPORT_C_INCLUDES +# instead. Then users of the library don't have to do anything --- they'll +# have the correct header files added to their include path automatically. +# + +# +# TODO: Allow each project to define stuff like this before the per-module +# Android.mk files are included, so we don't need to have a big central +# list. +# + +# +# A mapping from shorthand names to include directories. +# +pathmap_INCL := \ + camera:system/media/camera/include \ + frameworks-base:frameworks/base/include \ + frameworks-native:frameworks/native/include \ + libhardware:hardware/libhardware/include \ + libhardware_legacy:hardware/libhardware_legacy/include \ + libril:hardware/ril/include \ + system-core:system/core/include \ + audio:system/media/audio/include \ + audio-effects:system/media/audio_effects/include \ + audio-utils:system/media/audio_utils/include \ + audio-route:system/media/audio_route/include \ + wilhelm:frameworks/wilhelm/include \ + wilhelm-ut:frameworks/wilhelm/src/ut \ + mediandk:frameworks/av/media/ndk/ + +# +# Returns the path to the requested module's include directory, +# relative to the root of the source tree. Does not handle external +# modules. +# +# $(1): a list of modules (or other named entities) to find the includes for +# +define include-path-for +$(foreach n,$(1),$(patsubst $(n):%,%,$(filter $(n):%,$(pathmap_INCL)))) +endef + +# +# A list of all source roots under frameworks/base, which will be +# built into the android.jar. +# +FRAMEWORKS_BASE_SUBDIRS := \ + $(addsuffix /java, \ + core \ + graphics \ + location \ + media \ + media/mca/effect \ + media/mca/filterfw \ + media/mca/filterpacks \ + drm \ + opengl \ + sax \ + telecomm \ + telephony \ + wifi \ + lowpan \ + keystore \ + rs \ + ) + +# +# A version of FRAMEWORKS_BASE_SUBDIRS that is expanded to full paths from +# the root of the tree. This currently needs to be here so that other libraries +# and apps can find the .aidl files in the framework, though we should really +# figure out a better way to do this. +# +FRAMEWORKS_BASE_JAVA_SRC_DIRS := \ + $(addprefix frameworks/base/,$(FRAMEWORKS_BASE_SUBDIRS)) diff --git a/make/core/phony_package.mk b/make/core/phony_package.mk new file mode 100644 index 0000000..578d629 --- /dev/null +++ b/make/core/phony_package.mk @@ -0,0 +1,14 @@ +$(call record-module-type,PHONY_PACKAGE) +ifneq ($(strip $(LOCAL_SRC_FILES)),) +$(error LOCAL_SRC_FILES are not allowed for phony packages) +endif + +LOCAL_MODULE_CLASS := FAKE +LOCAL_MODULE_SUFFIX := -timestamp + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) + $(hide) echo "Fake: $@" + $(hide) mkdir -p $(dir $@) + $(hide) touch $@ diff --git a/make/core/prebuilt.mk b/make/core/prebuilt.mk new file mode 100644 index 0000000..4512cd9 --- /dev/null +++ b/make/core/prebuilt.mk @@ -0,0 +1,53 @@ +########################################################### +## Standard rules for copying files that are prebuilt +## +## Additional inputs from base_rules.make: +## None. +## +########################################################### +$(call record-module-type,PREBUILT) + +ifdef LOCAL_IS_HOST_MODULE + my_prefix := HOST_ + LOCAL_HOST_PREFIX := +else + my_prefix := TARGET_ +endif + +include $(BUILD_SYSTEM)/multilib.mk + +my_skip_non_preferred_arch := + +# check if first arch is supported +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# first arch is supported +include $(BUILD_SYSTEM)/prebuilt_internal.mk +ifneq ($(my_module_multilib),both) +my_skip_non_preferred_arch := true +endif # $(my_module_multilib) +# For apps, we don't want to set up the prebuilt apk rule twice even if "LOCAL_MULTILIB := both". +ifeq (APPS,$(LOCAL_MODULE_CLASS)) +my_skip_non_preferred_arch := true +endif +endif # $(my_module_arch_supported) + +ifndef my_skip_non_preferred_arch +ifneq (,$($(my_prefix)2ND_ARCH)) +# check if secondary arch is supported +LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/module_arch_supported.mk +ifeq ($(my_module_arch_supported),true) +# secondary arch is supported +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := +include $(BUILD_SYSTEM)/prebuilt_internal.mk +endif # $(my_module_arch_supported) +endif # $($(my_prefix)2ND_ARCH) +endif # $(my_skip_non_preferred_arch) not true + +LOCAL_2ND_ARCH_VAR_PREFIX := + +my_module_arch_supported := diff --git a/make/core/prebuilt_internal.mk b/make/core/prebuilt_internal.mk new file mode 100644 index 0000000..ef1471d --- /dev/null +++ b/make/core/prebuilt_internal.mk @@ -0,0 +1,63 @@ +########################################################### +## Standard rules for copying files that are prebuilt +## +## Additional inputs from base_rules.make: +## None. +## +########################################################### + +include $(BUILD_SYSTEM)/use_lld_setup.mk + +ifneq ($(LOCAL_PREBUILT_LIBS),) +$(call pretty-error,dont use LOCAL_PREBUILT_LIBS anymore) +endif +ifneq ($(LOCAL_PREBUILT_EXECUTABLES),) +$(call pretty-error,dont use LOCAL_PREBUILT_EXECUTABLES anymore) +endif +ifneq ($(LOCAL_PREBUILT_JAVA_LIBRARIES),) +$(call pretty-error,dont use LOCAL_PREBUILT_JAVA_LIBRARIES anymore) +endif + +my_32_64_bit_suffix := $(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32) + +ifdef LOCAL_PREBUILT_MODULE_FILE + my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE) +else ifdef LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) + my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))) + LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) := +else ifdef LOCAL_SRC_FILES_$(my_32_64_bit_suffix) + my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES_$(my_32_64_bit_suffix))) + LOCAL_SRC_FILES_$(my_32_64_bit_suffix) := +else ifdef LOCAL_SRC_FILES + my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES)) + LOCAL_SRC_FILES := +else ifdef LOCAL_REPLACE_PREBUILT_APK_INSTALLED + # This is handled specially in app_prebuilt_internal.mk +else + $(call pretty-error,No source files specified) +endif + +LOCAL_CHECKED_MODULE := $(my_prebuilt_src_file) + +ifneq (APPS,$(LOCAL_MODULE_CLASS)) +ifdef LOCAL_COMPRESSED_MODULE +$(error $(LOCAL_MODULE) : LOCAL_COMPRESSED_MODULE can only be defined for module class APPS) +endif # LOCAL_COMPRESSED_MODULE +endif # APPS + +ifeq (APPS,$(LOCAL_MODULE_CLASS)) + include $(BUILD_SYSTEM)/app_prebuilt_internal.mk +else ifeq (JAVA_LIBRARIES,$(LOCAL_MODULE_CLASS)) + include $(BUILD_SYSTEM)/java_prebuilt_internal.mk +else ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + include $(BUILD_SYSTEM)/cc_prebuilt_internal.mk +else ifneq ($(filter SCRIPT ETC DATA RENDERSCRIPT_BITCODE,$(LOCAL_MODULE_CLASS)),) + include $(BUILD_SYSTEM)/misc_prebuilt_internal.mk +else + $(error $(LOCAL_MODULE) : unexpected LOCAL_MODULE_CLASS for prebuilts: $(LOCAL_MODULE_CLASS)) +endif + +$(built_module) : $(LOCAL_ADDITIONAL_DEPENDENCIES) + +my_prebuilt_src_file := +my_preopt_for_extracted_apk := diff --git a/make/core/process_wrapper.sh b/make/core/process_wrapper.sh new file mode 100755 index 0000000..9c3104e --- /dev/null +++ b/make/core/process_wrapper.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# When using a process wrapper, this is the top-level +# command that is executed instead of the server +# command. It starts a new xterm in which the user can +# interact with the new process. +# +# Inside of the xterm is a gdb session, through which +# the user can debug the new process. + +# Save away these variables, since we may loose them +# when starting in the xterm. +export PREV_LD_LIBRARY_PATH=$LD_LIBRARY_PATH +export PREV_PATH=$PATH + +gnome-terminal -t "Wrapper: $1" --disable-factory -x $2/process_wrapper_gdb.sh "$@" + diff --git a/make/core/process_wrapper_gdb.cmds b/make/core/process_wrapper_gdb.cmds new file mode 100644 index 0000000..f5bdd21 --- /dev/null +++ b/make/core/process_wrapper_gdb.cmds @@ -0,0 +1 @@ +run diff --git a/make/core/process_wrapper_gdb.sh b/make/core/process_wrapper_gdb.sh new file mode 100755 index 0000000..38b948a --- /dev/null +++ b/make/core/process_wrapper_gdb.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# This is the command running inside the xterm of our +# debug wrapper. It needs to take care of starting +# the server command, so it can attach to the parent +# process. In addition, here we run the command inside +# of a gdb session to allow for debugging. + +# On some systems, running xterm will cause LD_LIBRARY_PATH +# to be cleared, so restore it and PATH to be safe. +export PATH=$PREV_PATH +export LD_LIBRARY_PATH=$PREV_LD_LIBRARY_PATH + +# Start binderproc (or whatever sub-command is being run) +# inside of gdb, giving gdb an initial command script to +# automatically run the process without user intervention. +gdb -q -x $2/process_wrapper_gdb.cmds --args "$@" diff --git a/make/core/product-graph.mk b/make/core/product-graph.mk new file mode 100644 index 0000000..4a44837 --- /dev/null +++ b/make/core/product-graph.mk @@ -0,0 +1,79 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# the sort also acts as a strip to remove the single space entries that creep in because of the evals +define gather-all-makefiles-for-current-product +$(eval _all_products_visited := )\ +$(sort $(call gather-all-makefiles-for-current-product-inner,$(INTERNAL_PRODUCT))) +endef + +define gather-all-makefiles-for-current-product-inner + $(foreach p,$(1),\ + $(if $(filter $(p),$(_all_products_visited)),, \ + $(p) \ + $(eval _all_products_visited += $(p)) \ + $(call gather-all-makefiles-for-current-product-inner, $(PRODUCTS.$(strip $(p)).INHERITS_FROM)) + ) \ + ) +endef + +node_color_target := orange +node_color_common := beige +node_color_vendor := lavenderblush +node_color_default := white +define node-color +$(if $(filter $(1),$(PRIVATE_TOP_LEVEL_MAKEFILE)),\ + $(node_color_target),\ + $(if $(filter build/make/target/product/%,$(1)),\ + $(node_color_common),\ + $(if $(filter vendor/%,$(1)),$(node_color_vendor),$(node_color_default))\ + )\ +) +endef + +open_parethesis := ( +close_parenthesis := ) + +# Emit properties of a product node to a file. +# $(1) the product +# $(2) the output file +define emit-product-node-props +$(hide) echo \"$(1)\" [ \ +label=\"$(dir $(1))\\n$(notdir $(1))$(if $(filter $(1),$(PRIVATE_TOP_LEVEL_MAKEFILE)),$(subst $(open_parethesis),,$(subst $(close_parenthesis),,\\n\\n$(PRODUCT_MODEL)\\n$(PRODUCT_DEVICE))))\" \ +style=\"filled\" fillcolor=\"$(strip $(call node-color,$(1)))\" \ +colorscheme=\"svg\" fontcolor=\"darkblue\" \ +] >> $(2) + +endef + +products_graph := $(OUT_DIR)/products.dot + +$(products_graph): PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT := $(call gather-all-makefiles-for-current-product) +$(products_graph): PRIVATE_TOP_LEVEL_MAKEFILE := $(INTERNAL_PRODUCT) +$(products_graph): + @echo Product graph DOT: $@ for $(PRIVATE_TOP_LEVEL_MAKEFILE) + $(hide) echo 'digraph {' > $@ + $(hide) echo 'graph [ ratio=.5 ];' >> $@ + $(hide) $(foreach p,$(PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT), \ + $(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), echo \"$(d)\" -\> \"$(p)\" >> $@;)) + $(foreach p,$(PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT),$(call emit-product-node-props,$(p),$@)) + $(hide) echo '}' >> $@ + +.PHONY: product-graph +product-graph: $(products_graph) + @echo Product graph .dot file: $(products_graph) + @echo Command to convert to pdf: dot -Tpdf -Nshape=box -o $(OUT_DIR)/products.pdf $(products_graph) + @echo Command to convert to svg: dot -Tsvg -Nshape=box -o $(OUT_DIR)/products.svg $(products_graph) diff --git a/make/core/product.mk b/make/core/product.mk new file mode 100644 index 0000000..e57ca13 --- /dev/null +++ b/make/core/product.mk @@ -0,0 +1,549 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Variables that are meant to hold only a single value. +# - The value set in the current makefile takes precedence over inherited values +# - If multiple inherited makefiles set the var, the first-inherited value wins +_product_single_value_vars := + +# Variables that are lists of values. +_product_list_vars := + +_product_single_value_vars += PRODUCT_NAME +_product_single_value_vars += PRODUCT_MODEL + +# The resoure configuration options to use for this product. +_product_list_vars += PRODUCT_LOCALES +_product_list_vars += PRODUCT_AAPT_CONFIG +_product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG +_product_list_vars += PRODUCT_AAPT_PREBUILT_DPI +_product_list_vars += PRODUCT_HOST_PACKAGES +_product_list_vars += PRODUCT_PACKAGES +_product_list_vars += PRODUCT_PACKAGES_DEBUG +_product_list_vars += PRODUCT_PACKAGES_DEBUG_ASAN +# Packages included only for eng/userdebug builds, when building with EMMA_INSTRUMENT=true +_product_list_vars += PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE +_product_list_vars += PRODUCT_PACKAGES_ENG +_product_list_vars += PRODUCT_PACKAGES_TESTS + +# The device that this product maps to. +_product_single_value_vars += PRODUCT_DEVICE +_product_single_value_vars += PRODUCT_MANUFACTURER +_product_single_value_vars += PRODUCT_BRAND + +# These PRODUCT_SYSTEM_* flags, if defined, are used in place of the +# corresponding PRODUCT_* flags for the sysprops on /system. +_product_single_value_vars += \ + PRODUCT_SYSTEM_NAME \ + PRODUCT_SYSTEM_MODEL \ + PRODUCT_SYSTEM_DEVICE \ + PRODUCT_SYSTEM_BRAND \ + PRODUCT_SYSTEM_MANUFACTURER \ + +# PRODUCT__PROPERTIES are lists of property assignments +# that go to /build.prop. Each property assignment is +# "key = value" with zero or more whitespace characters on either +# side of the '='. +_product_list_vars += \ + PRODUCT_SYSTEM_PROPERTIES \ + PRODUCT_SYSTEM_EXT_PROPERTIES \ + PRODUCT_VENDOR_PROPERTIES \ + PRODUCT_ODM_PROPERTIES \ + PRODUCT_PRODUCT_PROPERTIES + +# TODO(b/117892318) deprecate these: +# ... in favor or PRODUCT_SYSTEM_PROPERTIES +_product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES +# ... in favor of PRODUCT_VENDOR_PROPERTIES +_product_list_vars += PRODUCT_PROPERTY_OVERRIDES +_product_list_vars += PRODUCT_DEFAULT_PROPERTY_OVERRIDES + +# TODO(b/117892318) consider deprecating these too +_product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST +_product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST + +# The characteristics of the product, which among other things is passed to aapt +_product_single_value_vars += PRODUCT_CHARACTERISTICS + +# A list of words like :[:]. +# The file at the source path should be copied to the destination path +# when building this product. is relative to +# $(PRODUCT_OUT), so it should look like, e.g., "system/etc/file.xml". +# The rules for these copy steps are defined in build/make/core/Makefile. +# The optional : is used to indicate the owner of a vendor file. +_product_list_vars += PRODUCT_COPY_FILES + +# The OTA key(s) specified by the product config, if any. The names +# of these keys are stored in the target-files zip so that post-build +# signing tools can substitute them for the test key embedded by +# default. +_product_list_vars += PRODUCT_OTA_PUBLIC_KEYS +_product_list_vars += PRODUCT_EXTRA_OTA_KEYS +_product_list_vars += PRODUCT_EXTRA_RECOVERY_KEYS + +# Should we use the default resources or add any product specific overlays +_product_list_vars += PRODUCT_PACKAGE_OVERLAYS +_product_list_vars += DEVICE_PACKAGE_OVERLAYS + +# Resource overlay list which must be excluded from enforcing RRO. +_product_list_vars += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS + +# Package list to apply enforcing RRO. +_product_list_vars += PRODUCT_ENFORCE_RRO_TARGETS + +_product_list_vars += PRODUCT_SDK_ATREE_FILES +_product_list_vars += PRODUCT_SDK_ADDON_NAME +_product_list_vars += PRODUCT_SDK_ADDON_COPY_FILES +_product_list_vars += PRODUCT_SDK_ADDON_COPY_MODULES +_product_list_vars += PRODUCT_SDK_ADDON_DOC_MODULES +_product_list_vars += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP + +# which Soong namespaces to export to Make +_product_list_vars += PRODUCT_SOONG_NAMESPACES + +_product_list_vars += PRODUCT_DEFAULT_WIFI_CHANNELS +_product_single_value_vars += PRODUCT_DEFAULT_DEV_CERTIFICATE +_product_list_vars += PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES +_product_list_vars += PRODUCT_RESTRICT_VENDOR_FILES + +# The list of product-specific kernel header dirs +_product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS + +# A list of module names in BOOTCLASSPATH (jar files). Each module may be +# prefixed with ":", which identifies the APEX that provides it. APEXes +# are identified by their "variant" names, i.e. their `apex_name` values in +# Soong, which default to the `name` values. The prefix can also be "platform:" +# or "system_ext:", and defaults to "platform:" if left out. See the long +# comment in build/soong/java/dexprepopt_bootjars.go for details. +_product_list_vars += PRODUCT_BOOT_JARS + +# A list of extra BOOTCLASSPATH jars (to be appended after common jars), +# following the same format as PRODUCT_BOOT_JARS. Products that include +# device-specific makefiles before AOSP makefiles should use this instead of +# PRODUCT_BOOT_JARS, so that device-specific jars go after common jars. +_product_list_vars += PRODUCT_BOOT_JARS_EXTRA + +_product_single_value_vars += PRODUCT_SUPPORTS_BOOT_SIGNER +_product_single_value_vars += PRODUCT_SUPPORTS_VBOOT +_product_single_value_vars += PRODUCT_SUPPORTS_VERITY +_product_single_value_vars += PRODUCT_SUPPORTS_VERITY_FEC +_product_list_vars += PRODUCT_SYSTEM_SERVER_APPS +# List of system_server classpath jars on the platform. +_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS +# List of system_server classpath jars delivered via apex. Format = :. +_product_list_vars += PRODUCT_APEX_SYSTEM_SERVER_JARS +# List of jars on the platform that system_server loads dynamically using separate classloaders. +_product_list_vars += PRODUCT_STANDALONE_SYSTEM_SERVER_JARS +# List of jars delivered via apex that system_server loads dynamically using separate classloaders. +# Format = : +_product_list_vars += PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS +# If true, then suboptimal order of system server jars does not cause an error. +_product_single_value_vars += PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS +# If true, then system server jars defined in Android.mk are supported. +_product_single_value_vars += PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS + +# Additional system server jars to be appended at the end of the common list. +# This is necessary to avoid jars reordering due to makefile inheritance order. +_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS_EXTRA + +# Set to true to disable checks for a product. +_product_list_vars += PRODUCT_BROKEN_VERIFY_USES_LIBRARIES + +# All of the apps that we force preopt, this overrides WITH_DEXPREOPT. +_product_list_vars += PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK +_product_list_vars += PRODUCT_DEXPREOPT_SPEED_APPS +_product_list_vars += PRODUCT_LOADED_BY_PRIVILEGED_MODULES +_product_single_value_vars += PRODUCT_VBOOT_SIGNING_KEY +_product_single_value_vars += PRODUCT_VBOOT_SIGNING_SUBKEY +_product_single_value_vars += PRODUCT_VERITY_SIGNING_KEY +_product_single_value_vars += PRODUCT_SYSTEM_VERITY_PARTITION +_product_single_value_vars += PRODUCT_VENDOR_VERITY_PARTITION +_product_single_value_vars += PRODUCT_PRODUCT_VERITY_PARTITION +_product_single_value_vars += PRODUCT_SYSTEM_EXT_VERITY_PARTITION +_product_single_value_vars += PRODUCT_ODM_VERITY_PARTITION +_product_single_value_vars += PRODUCT_VENDOR_DLKM_VERITY_PARTITION +_product_single_value_vars += PRODUCT_ODM_DLKM_VERITY_PARTITION +_product_single_value_vars += PRODUCT_SYSTEM_DLKM_VERITY_PARTITION +_product_single_value_vars += PRODUCT_SYSTEM_SERVER_DEBUG_INFO +_product_single_value_vars += PRODUCT_OTHER_JAVA_DEBUG_INFO + +# Per-module dex-preopt configs. +_product_list_vars += PRODUCT_DEX_PREOPT_MODULE_CONFIGS +_product_single_value_vars += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER +_product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS +_product_single_value_vars += PRODUCT_DEX_PREOPT_BOOT_FLAGS +_product_single_value_vars += PRODUCT_DEX_PREOPT_PROFILE_DIR +_product_single_value_vars += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES +_product_single_value_vars += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING +_product_single_value_vars += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS + +# Boot image options. +_product_list_vars += PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION +_product_single_value_vars += \ + PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST \ + PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \ + PRODUCT_USES_DEFAULT_ART_CONFIG \ + +_product_single_value_vars += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER +# Per-module sanitizer configs +_product_list_vars += PRODUCT_SANITIZER_MODULE_CONFIGS +_product_single_value_vars += PRODUCT_SYSTEM_BASE_FS_PATH +_product_single_value_vars += PRODUCT_VENDOR_BASE_FS_PATH +_product_single_value_vars += PRODUCT_PRODUCT_BASE_FS_PATH +_product_single_value_vars += PRODUCT_SYSTEM_EXT_BASE_FS_PATH +_product_single_value_vars += PRODUCT_ODM_BASE_FS_PATH +_product_single_value_vars += PRODUCT_VENDOR_DLKM_BASE_FS_PATH +_product_single_value_vars += PRODUCT_ODM_DLKM_BASE_FS_PATH +_product_single_value_vars += PRODUCT_SYSTEM_DLKM_BASE_FS_PATH + +# The first API level this product shipped with +_product_single_value_vars += PRODUCT_SHIPPING_API_LEVEL + +_product_list_vars += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES +_product_list_vars += VENDOR_EXCEPTION_MODULES +_product_list_vars += VENDOR_EXCEPTION_PATHS +# Whether the product wants to ship libartd. For rules and meaning, see art/Android.mk. +_product_single_value_vars += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD + +# Make this art variable visible to soong_config.mk. +_product_single_value_vars += PRODUCT_ART_USE_READ_BARRIER + +# Add reserved headroom to a system image. +_product_single_value_vars += PRODUCT_SYSTEM_HEADROOM + +# Whether to save disk space by minimizing java debug info +_product_single_value_vars += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO + +# Whether any paths are excluded from sanitization when SANITIZE_TARGET=integer_overflow +_product_list_vars += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS + +_product_single_value_vars += PRODUCT_ADB_KEYS + +# Whether any paths should have CFI enabled for components +_product_list_vars += PRODUCT_CFI_INCLUDE_PATHS + +# Whether any paths are excluded from sanitization when SANITIZE_TARGET=cfi +_product_list_vars += PRODUCT_CFI_EXCLUDE_PATHS + +# Whether the Scudo hardened allocator is disabled platform-wide +_product_single_value_vars += PRODUCT_DISABLE_SCUDO + +# List of extra VNDK versions to be included +_product_list_vars += PRODUCT_EXTRA_VNDK_VERSIONS + +# Whether APEX should be compressed or not +_product_single_value_vars += PRODUCT_COMPRESSED_APEX + +# VNDK version of product partition. It can be 'current' if the product +# partitions uses PLATFORM_VNDK_VERSION. +_product_single_value_vars += PRODUCT_PRODUCT_VNDK_VERSION + +_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS +_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT +_product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST +_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT +_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST + +# List of modules that should be forcefully unmarked from being LOCAL_PRODUCT_MODULE, and hence +# installed on /system directory by default. +_product_list_vars += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION + +# When this is true, dynamic partitions is retrofitted on a device that has +# already been launched without dynamic partitions. Otherwise, the device +# is launched with dynamic partitions. +# This flag implies PRODUCT_USE_DYNAMIC_PARTITIONS. +_product_single_value_vars += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS + +# List of tags that will be used to gate blueprint modules from the build graph +_product_list_vars += PRODUCT_INCLUDE_TAGS + +# When this is true, various build time as well as runtime debugfs restrictions are enabled. +_product_single_value_vars += PRODUCT_SET_DEBUGFS_RESTRICTIONS + +# Other dynamic partition feature flags.PRODUCT_USE_DYNAMIC_PARTITION_SIZE and +# PRODUCT_BUILD_SUPER_PARTITION default to the value of PRODUCT_USE_DYNAMIC_PARTITIONS. +_product_single_value_vars += \ + PRODUCT_USE_DYNAMIC_PARTITIONS \ + PRODUCT_USE_DYNAMIC_PARTITION_SIZE \ + PRODUCT_BUILD_SUPER_PARTITION \ + +# If set, kernel configuration requirements are present in OTA package (and will be enforced +# during OTA). Otherwise, kernel configuration requirements are enforced in VTS. +# Devices that checks the running kernel (instead of the kernel in OTA package) should not +# set this variable to prevent OTA failures. +_product_list_vars += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS + +# If set to true, this product builds a generic OTA package, which installs generic system images +# onto matching devices. The product may only build a subset of system images (e.g. only +# system.img), so devices need to install the package in a system-only OTA manner. +_product_single_value_vars += PRODUCT_BUILD_GENERIC_OTA_PACKAGE + +_product_list_vars += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES +_product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES +_product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES + +# Controls for whether different partitions are built for the current product. +_product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE +_product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE +_product_single_value_vars += PRODUCT_BUILD_VENDOR_IMAGE +_product_single_value_vars += PRODUCT_BUILD_PRODUCT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_SYSTEM_EXT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_ODM_IMAGE +_product_single_value_vars += PRODUCT_BUILD_VENDOR_DLKM_IMAGE +_product_single_value_vars += PRODUCT_BUILD_ODM_DLKM_IMAGE +_product_single_value_vars += PRODUCT_BUILD_SYSTEM_DLKM_IMAGE +_product_single_value_vars += PRODUCT_BUILD_CACHE_IMAGE +_product_single_value_vars += PRODUCT_BUILD_RAMDISK_IMAGE +_product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE +_product_single_value_vars += PRODUCT_BUILD_RECOVERY_IMAGE +_product_single_value_vars += PRODUCT_BUILD_BOOT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_INIT_BOOT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_DEBUG_BOOT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_VENDOR_BOOT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE +_product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE +_product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE +_product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE + +# List of boot jars delivered via updatable APEXes, following the same format as +# PRODUCT_BOOT_JARS. +_product_list_vars += PRODUCT_APEX_BOOT_JARS + +# If set, device uses virtual A/B. +_product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA + +# If set, device uses virtual A/B Compression. +_product_single_value_vars += PRODUCT_VIRTUAL_AB_COMPRESSION + +# If set, device retrofits virtual A/B. +_product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA_RETROFIT + +# If set, forcefully generate a non-A/B update package. +# Note: A device configuration should inherit from virtual_ab_ota_plus_non_ab.mk +# instead of setting this variable directly. +# Note: Use TARGET_OTA_ALLOW_NON_AB in the build system because +# TARGET_OTA_ALLOW_NON_AB takes the value of AB_OTA_UPDATER into account. +_product_single_value_vars += PRODUCT_OTA_FORCE_NON_AB_PACKAGE + +# If set, Java module in product partition cannot use hidden APIs. +_product_single_value_vars += PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE + +# If set, only java_sdk_library can be used at inter-partition dependency. +# Note: Build error if BOARD_VNDK_VERSION is not set while +# PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY is true, because +# PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY has no meaning if +# BOARD_VNDK_VERSION is not set. +# Note: When PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is not set, there are +# no restrictions at dependency between system and product partition. +_product_single_value_vars += PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY + +# Allowlist for PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY option. +# Listed modules are allowed at inter-partition dependency even if it isn't +# a java_sdk_library module. +_product_list_vars += PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST + +_product_single_value_vars += PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES + +# Install a copy of the debug policy to the system_ext partition, and allow +# init-second-stage to load debug policy from system_ext. +# This option is only meant to be set by compliance GSI targets. +_product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT + +# If set, metadata files for the following artifacts will be generated. +# - system/framework/*.jar +# - system/framework/oat//*.{oat,vdex,art} +# - system/etc/boot-image.prof +# - system/etc/dirty-image-objects +# One fsverity metadata container file per one input file will be generated in +# system.img, with a suffix ".fsv_meta". e.g. a container file for +# "/system/framework/foo.jar" will be "system/framework/foo.jar.fsv_meta". +_product_single_value_vars += PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA + +# If true, sets the default for MODULE_BUILD_FROM_SOURCE. This overrides +# BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE but not an explicitly set value. +_product_single_value_vars += PRODUCT_MODULE_BUILD_FROM_SOURCE + +.KATI_READONLY := _product_single_value_vars _product_list_vars +_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars) + +# +# Functions for including product makefiles +# + +# +# $(1): product to inherit +# +# To be called from product makefiles, and is later evaluated during the import-nodes +# call below. It does the following: +# 1. Inherits all of the variables from $1. +# 2. Records the inheritance in the .INHERITS_FROM variable +# +# (2) and the PRODUCTS variable can be used together to reconstruct the include hierarchy +# See e.g. product-graph.mk for an example of this. +# +define inherit-product + $(eval _inherit_product_wildcard := $(wildcard $(1)))\ + $(if $(_inherit_product_wildcard),,$(error $(1) does not exist.))\ + $(foreach part,$(_inherit_product_wildcard),\ + $(if $(findstring ../,$(part)),\ + $(eval np := $(call normalize-paths,$(part))),\ + $(eval np := $(strip $(part))))\ + $(foreach v,$(_product_var_list), \ + $(eval $(v) := $($(v)) $(INHERIT_TAG)$(np))) \ + $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \ + $(eval inherit_var := PRODUCTS.$(current_mk).INHERITS_FROM) \ + $(eval $(inherit_var) := $(sort $($(inherit_var)) $(np))) \ + $(call dump-inherit,$(strip $(word 1,$(_include_stack))),$(1)) \ + $(call dump-config-vals,$(current_mk),inherit)) +endef + +# Specifies a number of path prefixes, relative to PRODUCT_OUT, where the +# product makefile hierarchy rooted in the current node places its artifacts. +# Creating artifacts outside the specified paths will cause a build-time error. +define require-artifacts-in-path + $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \ + $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENTS := $(strip $(1))) \ + $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_ALLOWED_LIST := $(strip $(2))) \ + $(eval ARTIFACT_PATH_REQUIREMENT_PRODUCTS := \ + $(sort $(ARTIFACT_PATH_REQUIREMENT_PRODUCTS) $(current_mk))) +endef + +# Like require-artifacts-in-path, but does not require all allow-list entries to +# have an effect. +define require-artifacts-in-path-relaxed + $(require-artifacts-in-path) \ + $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENT_IS_RELAXED := true) +endef + +# Makes including non-existent modules in PRODUCT_PACKAGES an error. +# $(1): list of non-existent modules to allow. +define enforce-product-packages-exist + $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \ + $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST := true) \ + $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST := $(1)) \ + $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST) \ + $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST) +endef + +# +# Do inherit-product only if $(1) exists +# +define inherit-product-if-exists + $(if $(wildcard $(1)),$(call inherit-product,$(1)),) +endef + +# +# $(1): product makefile list +# +#TODO: check to make sure that products have all the necessary vars defined +define import-products +$(call import-nodes,PRODUCTS,$(1),$(_product_var_list),$(_product_single_value_vars)) +endef + + +# +# Does various consistency checks on the current product. +# Takes no parameters, so $(call ) is not necessary. +# +define check-current-product +$(if ,, \ + $(if $(call is-c-identifier,$(PRODUCT_NAME)),, \ + $(error $(INTERNAL_PRODUCT): PRODUCT_NAME must be a valid C identifier, not "$(pn)")) \ + $(if $(PRODUCT_BRAND),, \ + $(error $(INTERNAL_PRODUCT): PRODUCT_BRAND must be defined.)) \ + $(foreach cf,$(strip $(PRODUCT_COPY_FILES)), \ + $(if $(filter 2 3,$(words $(subst :,$(space),$(cf)))),, \ + $(error $(p): malformed COPY_FILE "$(cf)")))) +endef + +# BoardConfig variables that are also inherited in product mks. Should ideally +# be cleaned up to not be product variables. +_readonly_late_variables := \ + DEVICE_PACKAGE_OVERLAYS \ + WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY \ + +# Modified internally in the build system +_readonly_late_variables += \ + PRODUCT_COPY_FILES \ + PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING \ + PRODUCT_DEX_PREOPT_BOOT_FLAGS \ + +_readonly_early_variables := $(filter-out $(_readonly_late_variables),$(_product_var_list)) + +# +# Mark the variables in _product_stash_var_list as readonly +# +define readonly-variables +$(foreach v,$(1), \ + $(eval $(v) ?=) \ + $(eval .KATI_READONLY := $(v)) \ + ) +endef +define readonly-product-vars +$(call readonly-variables,$(_readonly_early_variables)) +endef + +define readonly-final-product-vars +$(call readonly-variables,$(_readonly_late_variables)) +endef + +# Macro re-defined inside strip-product-vars. +get-product-var = $(PRODUCTS.$(strip $(1)).$(2)) +# +# Strip the variables in _product_var_list and a few build-system +# internal variables, and assign the ones for the current product +# to a shorthand that is more convenient to read from elsewhere. +# +define strip-product-vars +$(call dump-phase-start,PRODUCT-EXPAND,,$(_product_var_list),$(_product_single_value_vars), \ + build/make/core/product.mk) \ +$(foreach v,\ + $(_product_var_list) \ + PRODUCT_ENFORCE_PACKAGES_EXIST \ + PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST, \ + $(eval $(v) := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).$(v)))) \ + $(eval get-product-var = $$(if $$(filter $$(1),$$(INTERNAL_PRODUCT)),$$($$(2)),$$(PRODUCTS.$$(strip $$(1)).$$(2)))) \ + $(KATI_obsolete_var PRODUCTS.$(INTERNAL_PRODUCT).$(v),Use $(v) instead) \ +) \ +$(call dump-phase-end,build/make/core/product.mk) +endef + +define add-to-product-copy-files-if-exists +$(if $(wildcard $(word 1,$(subst :, ,$(1)))),$(1)) +endef + +# whitespace placeholder when we record module's dex-preopt config. +_PDPMC_SP_PLACE_HOLDER := |@SP@| +# Set up dex-preopt config for a module. +# $(1) list of module names +# $(2) the modules' dex-preopt config +define add-product-dex-preopt-module-config +$(eval _c := $(subst $(space),$(_PDPMC_SP_PLACE_HOLDER),$(strip $(2))))\ +$(eval PRODUCT_DEX_PREOPT_MODULE_CONFIGS += \ + $(foreach m,$(1),$(m)=$(_c))) +endef + +# whitespace placeholder when we record module's sanitizer config. +_PSMC_SP_PLACE_HOLDER := |@SP@| +# Set up sanitizer config for a module. +# $(1) list of module names +# $(2) the modules' sanitizer config +define add-product-sanitizer-module-config +$(eval _c := $(subst $(space),$(_PSMC_SP_PLACE_HOLDER),$(strip $(2))))\ +$(eval PRODUCT_SANITIZER_MODULE_CONFIGS += \ + $(foreach m,$(1),$(m)=$(_c))) +endef diff --git a/make/core/product_config.mk b/make/core/product_config.mk new file mode 100644 index 0000000..b93f5be --- /dev/null +++ b/make/core/product_config.mk @@ -0,0 +1,598 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# --------------------------------------------------------------- +# Generic functions +# TODO: Move these to definitions.make once we're able to include +# definitions.make before config.make. + +########################################################### +## Return non-empty if $(1) is a C identifier; i.e., if it +## matches /^[a-zA-Z_][a-zA-Z0-9_]*$/. We do this by first +## making sure that it isn't empty and doesn't start with +## a digit, then by removing each valid character. If the +## final result is empty, then it was a valid C identifier. +## +## $(1): word to check +########################################################### + +_ici_digits := 0 1 2 3 4 5 6 7 8 9 +_ici_alphaunderscore := \ + a b c d e f g h i j k l m n o p q r s t u v w x y z \ + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ +define is-c-identifier +$(strip \ + $(if $(1), \ + $(if $(filter $(addsuffix %,$(_ici_digits)),$(1)), \ + , \ + $(eval w := $(1)) \ + $(foreach c,$(_ici_digits) $(_ici_alphaunderscore), \ + $(eval w := $(subst $(c),,$(w))) \ + ) \ + $(if $(w),,TRUE) \ + $(eval w :=) \ + ) \ + ) \ + ) +endef + +# TODO: push this into the combo files; unfortunately, we don't even +# know HOST_OS at this point. +trysed := $(shell echo a | sed -E -e 's/a/b/' 2>/dev/null) +ifeq ($(trysed),b) + SED_EXTENDED := sed -E +else + trysed := $(shell echo c | sed -r -e 's/c/d/' 2>/dev/null) + ifeq ($(trysed),d) + SED_EXTENDED := sed -r + else + $(error Unknown sed version) + endif +endif + +########################################################### +## List all of the files in a subdirectory in a format +## suitable for PRODUCT_COPY_FILES and +## PRODUCT_SDK_ADDON_COPY_FILES +## +## $(1): Glob to match file name +## $(2): Source directory +## $(3): Target base directory +########################################################### + +define find-copy-subdir-files +$(sort $(shell find $(2) -name "$(1)" -type f | $(SED_EXTENDED) "s:($(2)/?(.*)):\\1\\:$(3)/\\2:" | sed "s://:/:g")) +endef + +# +# Convert file file to the PRODUCT_COPY_FILES/PRODUCT_SDK_ADDON_COPY_FILES +# format: for each file F return $(F):$(PREFIX)/$(notdir $(F)) +# $(1): files list +# $(2): prefix + +define copy-files +$(foreach f,$(1),$(f):$(2)/$(notdir $(f))) +endef + +# +# Convert the list of file names to the list of PRODUCT_COPY_FILES items +# $(1): from pattern +# $(2): to pattern +# $(3): file names +# E.g., calling product-copy-files-by-pattern with +# (from/%, to/%, a b) +# returns +# from/a:to/a from/b:to/b +define product-copy-files-by-pattern +$(join $(patsubst %,$(1),$(3)),$(patsubst %,:$(2),$(3))) +endef + +# Return empty unless the board matches +define is-board-platform2 +$(filter $(1), $(TARGET_BOARD_PLATFORM)) +endef + +# Return empty unless the board is in the list +define is-board-platform-in-list2 +$(filter $(1),$(TARGET_BOARD_PLATFORM)) +endef + +# Return empty unless the board is QCOM +define is-vendor-board-qcom +$(if $(strip $(TARGET_BOARD_PLATFORM) $(QCOM_BOARD_PLATFORMS)),$(filter $(TARGET_BOARD_PLATFORM),$(QCOM_BOARD_PLATFORMS)),\ + $(error both TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) and QCOM_BOARD_PLATFORMS=$(QCOM_BOARD_PLATFORMS))) +endef + +# --------------------------------------------------------------- +# Check for obsolete PRODUCT- and APP- goals +ifeq ($(CALLED_FROM_SETUP),true) +product_goals := $(strip $(filter PRODUCT-%,$(MAKECMDGOALS))) +ifdef product_goals + $(error The PRODUCT-* goal is no longer supported. Use `TARGET_PRODUCT= m droid` instead) +endif +unbundled_goals := $(strip $(filter APP-%,$(MAKECMDGOALS))) +ifdef unbundled_goals + $(error The APP-* goal is no longer supported. Use `TARGET_BUILD_APPS="" m droid` instead) +endif # unbundled_goals +endif + +# Default to building dalvikvm on hosts that support it... +ifeq ($(HOST_OS),linux) +# ... or if the if the option is already set +ifeq ($(WITH_HOST_DALVIK),) + WITH_HOST_DALVIK := true +endif +endif + +# --------------------------------------------------------------- +# Include the product definitions. +# We need to do this to translate TARGET_PRODUCT into its +# underlying TARGET_DEVICE before we start defining any rules. +# +include $(BUILD_SYSTEM)/node_fns.mk +include $(BUILD_SYSTEM)/product.mk +include $(BUILD_SYSTEM)/device.mk + +# Read all product definitions. +# +# Products are defined in AndroidProducts.mk files: +android_products_makefiles := $(file <$(OUT_DIR)/.module_paths/AndroidProducts.mk.list) \ + $(SRC_TARGET_DIR)/product/AndroidProducts.mk + +# An AndroidProduct.mk file sets the following variables: +# PRODUCT_MAKEFILES specifies product makefiles. Each item in this list +# is either a :path/to/file.mk, or just path/to/ +# COMMON_LUNCH_CHOICES specifies - values to be shown +# in the `lunch` menu +# STARLARK_OPT_IN_PRODUCTS specifies products to use Starlark-based +# product configuration by default + +# Builds a list of first/second elements of each pair: +# $(call _first,a:A b:B,:) returns 'a b' +# $(call _second,a-A b-B,-) returns 'A B' +_first=$(filter-out $(2)%,$(subst $(2),$(space)$(2),$(1))) +_second=$(filter-out %$(2),$(subst $(2),$(2)$(space),$(1))) + +# Returns : pair from a PRODUCT_MAKEFILE item. +# If an item is :path/to/file.mk, return it as is, +# otherwise assume that an item is path/to/.mk and +# return :path/to/.mk +_product-spec=$(strip $(if $(findstring :,$(1)),$(1),$(basename $(notdir $(1))):$(1))) + +# Reads given AndroidProduct.mk file and sets the following variables: +# ap_product_paths -- the list of : pairs +# ap_common_lunch_choices -- the list of - items +# ap_products_using_starlark_config -- the list of products using starlark config +# In addition, validates COMMON_LUNCH_CHOICES and STARLARK_OPT_IN_PRODUCTS values +define _read-ap-file + $(eval PRODUCT_MAKEFILES :=) \ + $(eval COMMON_LUNCH_CHOICES :=) \ + $(eval STARLARK_OPT_IN_PRODUCTS := ) \ + $(eval ap_product_paths :=) \ + $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \ + $(eval include $(f)) \ + $(foreach p, $(PRODUCT_MAKEFILES),$(eval ap_product_paths += $(call _product-spec,$(p)))) \ + $(eval ap_common_lunch_choices := $(COMMON_LUNCH_CHOICES)) \ + $(eval ap_products_using_starlark_config := $(STARLARK_OPT_IN_PRODUCTS)) \ + $(eval _products := $(call _first,$(ap_product_paths),:)) \ + $(eval _bad := $(filter-out $(_products),$(call _first,$(ap_common_lunch_choices),-))) \ + $(if $(_bad),$(error COMMON_LUNCH_CHOICES contains products(s) not defined in this file: $(_bad))) \ + $(eval _bad := $(filter-out %-eng %-userdebug %-user,$(ap_common_lunch_choices))) \ + $(if $(_bad),$(error invalid variant in COMMON_LUNCH_CHOICES: $(_bad))) + $(eval _bad := $(filter-out $(_products),$(ap_products_using_starlark_config))) \ + $(if $(_bad),$(error STARLARK_OPT_IN_PRODUCTS contains product(s) not defined in this file: $(_bad))) +endef + +# Build cumulative lists of all product specs/lunch choices/Starlark-based products. +product_paths := +common_lunch_choices := +products_using_starlark_config := +$(foreach f,$(android_products_makefiles), \ + $(call _read-ap-file,$(f)) \ + $(eval product_paths += $(ap_product_paths)) \ + $(eval common_lunch_choices += $(ap_common_lunch_choices)) \ + $(eval products_using_starlark_config += $(ap_products_using_starlark_config)) \ +) + +# Dedup, extract product names, etc. +product_paths := $(sort $(product_paths)) +all_named_products := $(sort $(call _first,$(product_paths),:)) +all_product_makefiles := $(sort $(call _second,$(product_paths),:)) +current_product_makefile := $(call _second,$(filter $(TARGET_PRODUCT):%,$(product_paths)),:) +COMMON_LUNCH_CHOICES := $(sort $(common_lunch_choices)) + +# Check that there are no duplicate product names +$(foreach p,$(all_named_products), \ + $(if $(filter 1,$(words $(filter $(p):%,$(product_paths)))),, \ + $(error Product name must be unique, "$(p)" used by $(call _second,$(filter $(p):%,$(product_paths)),:)))) + +ifneq ($(ALLOW_RULES_IN_PRODUCT_CONFIG),) +_product_config_saved_KATI_ALLOW_RULES := $(.KATI_ALLOW_RULES) +.KATI_ALLOW_RULES := $(ALLOW_RULES_IN_PRODUCT_CONFIG) +endif + +ifeq (,$(current_product_makefile)) + $(error Can not locate config makefile for product "$(TARGET_PRODUCT)") +endif + +ifneq (,$(filter $(TARGET_PRODUCT),$(products_using_starlark_config))) + RBC_PRODUCT_CONFIG := true + RBC_BOARD_CONFIG := true +endif + +ifndef RBC_PRODUCT_CONFIG +$(call import-products, $(current_product_makefile)) +else + $(shell mkdir -p $(OUT_DIR)/rbc) + $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_product_config.mk) + + $(shell build/soong/scripts/update_out \ + $(OUT_DIR)/rbc/rbc_product_config_results.mk \ + build/soong/scripts/rbc-run \ + $(current_product_makefile) \ + $(OUT_DIR)/rbc/make_vars_pre_product_config.mk) + ifneq ($(.SHELLSTATUS),0) + $(error product configuration converter failed: $(.SHELLSTATUS)) + endif + include $(OUT_DIR)/rbc/rbc_product_config_results.mk +endif + +# This step was already handled in the RBC product configuration. +ifeq ($(RBC_PRODUCT_CONFIG)$(SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK),) +# Import all the products that have made artifact path requirements, so that we can verify +# the artifacts they produce. They might be intermediate makefiles instead of real products. +$(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\ + $(if $(filter-out $(makefile),$(PRODUCTS)),$(eval $(call import-products,$(makefile))))\ +) +endif + +INTERNAL_PRODUCT := $(current_product_makefile) +# Strip and assign the PRODUCT_ variables. +$(call strip-product-vars) + +# Quick check +$(check-current-product) + +ifneq ($(ALLOW_RULES_IN_PRODUCT_CONFIG),) +.KATI_ALLOW_RULES := $(_saved_KATI_ALLOW_RULES) +_product_config_saved_KATI_ALLOW_RULES := +endif + +############################################################################ + +current_product_makefile := +all_product_makefiles := +all_product_configs := + +############################################################################# +# Check product include tag allowlist +BLUEPRINT_INCLUDE_TAGS_ALLOWLIST := com.android.mainline_go com.android.mainline +.KATI_READONLY := BLUEPRINT_INCLUDE_TAGS_ALLOWLIST +$(foreach include_tag,$(PRODUCT_INCLUDE_TAGS), \ + $(if $(filter $(include_tag),$(BLUEPRINT_INCLUDE_TAGS_ALLOWLIST)),,\ + $(call pretty-error, $(include_tag) is not in BLUEPRINT_INCLUDE_TAGS_ALLOWLIST: $(BLUEPRINT_INCLUDE_TAGS_ALLOWLIST)))) +############################################################################# + +# Quick check and assign default values + +TARGET_DEVICE := $(PRODUCT_DEVICE) + +# TODO: also keep track of things like "port", "land" in product files. + +# Figure out which resoure configuration options to use for this +# product. +# If CUSTOM_LOCALES contains any locales not already included +# in PRODUCT_LOCALES, add them to PRODUCT_LOCALES. +extra_locales := $(filter-out $(PRODUCT_LOCALES),$(CUSTOM_LOCALES)) +ifneq (,$(extra_locales)) + ifneq ($(CALLED_FROM_SETUP),true) + # Don't spam stdout, because envsetup.sh may be scraping values from it. + $(info Adding CUSTOM_LOCALES [$(extra_locales)] to PRODUCT_LOCALES [$(PRODUCT_LOCALES)]) + endif + PRODUCT_LOCALES += $(extra_locales) + extra_locales := +endif + +# Add PRODUCT_LOCALES to PRODUCT_AAPT_CONFIG +PRODUCT_AAPT_CONFIG := $(PRODUCT_LOCALES) $(PRODUCT_AAPT_CONFIG) + +# Keep a copy of the space-separated config +PRODUCT_AAPT_CONFIG_SP := $(PRODUCT_AAPT_CONFIG) +PRODUCT_AAPT_CONFIG := $(subst $(space),$(comma),$(PRODUCT_AAPT_CONFIG)) + +########################################################### +## Add 'platform:' prefix to jars not in : format. +## +## This makes sure that a jar corresponds to ConfigureJarList format of and pairs +## where needed. +## +## $(1): a list of jars either in or : format +########################################################### + +define qualify-platform-jars + $(foreach jar,$(1),$(if $(findstring :,$(jar)),,platform:)$(jar)) +endef + +# Extra boot jars must be appended at the end after common boot jars. +PRODUCT_BOOT_JARS += $(PRODUCT_BOOT_JARS_EXTRA) + +PRODUCT_BOOT_JARS := $(call qualify-platform-jars,$(PRODUCT_BOOT_JARS)) + +# b/191127295: force core-icu4j onto boot image. It comes from a non-updatable APEX jar, but has +# historically been part of the boot image; even though APEX jars are not meant to be part of the +# boot image. +# TODO(b/191686720): remove PRODUCT_APEX_BOOT_JARS to avoid a special handling of core-icu4j +# in make rules. +PRODUCT_APEX_BOOT_JARS := $(filter-out com.android.i18n:core-icu4j,$(PRODUCT_APEX_BOOT_JARS)) +# All APEX jars come after /system and /system_ext jars, so adding core-icu4j at the end of the list +PRODUCT_BOOT_JARS += com.android.i18n:core-icu4j + +# The extra system server jars must be appended at the end after common system server jars. +PRODUCT_SYSTEM_SERVER_JARS += $(PRODUCT_SYSTEM_SERVER_JARS_EXTRA) + +PRODUCT_SYSTEM_SERVER_JARS := $(call qualify-platform-jars,$(PRODUCT_SYSTEM_SERVER_JARS)) + +# Sort APEX boot and system server jars. We use deterministic alphabetical order +# when constructing BOOTCLASSPATH and SYSTEMSERVERCLASSPATH definition on device +# after an update. Enforce it in the build system as well to avoid recompiling +# everything after an update due a change in the order. +PRODUCT_APEX_BOOT_JARS := $(sort $(PRODUCT_APEX_BOOT_JARS)) +PRODUCT_APEX_SYSTEM_SERVER_JARS := $(sort $(PRODUCT_APEX_SYSTEM_SERVER_JARS)) + +PRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \ + $(call qualify-platform-jars,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS)) + +ifndef PRODUCT_SYSTEM_NAME + PRODUCT_SYSTEM_NAME := $(PRODUCT_NAME) +endif +ifndef PRODUCT_SYSTEM_DEVICE + PRODUCT_SYSTEM_DEVICE := $(PRODUCT_DEVICE) +endif +ifndef PRODUCT_SYSTEM_BRAND + PRODUCT_SYSTEM_BRAND := $(PRODUCT_BRAND) +endif +ifndef PRODUCT_MODEL + PRODUCT_MODEL := $(PRODUCT_NAME) +endif +ifndef PRODUCT_SYSTEM_MODEL + PRODUCT_SYSTEM_MODEL := $(PRODUCT_MODEL) +endif + +ifndef PRODUCT_MANUFACTURER + PRODUCT_MANUFACTURER := unknown +endif +ifndef PRODUCT_SYSTEM_MANUFACTURER + PRODUCT_SYSTEM_MANUFACTURER := $(PRODUCT_MANUFACTURER) +endif + +ifndef PRODUCT_CHARACTERISTICS + TARGET_AAPT_CHARACTERISTICS := default +else + TARGET_AAPT_CHARACTERISTICS := $(PRODUCT_CHARACTERISTICS) +endif + +ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE + ifneq (1,$(words $(PRODUCT_DEFAULT_DEV_CERTIFICATE))) + $(error PRODUCT_DEFAULT_DEV_CERTIFICATE='$(PRODUCT_DEFAULT_DEV_CERTIFICATE)', \ + only 1 certificate is allowed.) + endif +endif + +$(foreach pair,$(PRODUCT_APEX_BOOT_JARS), \ + $(eval jar := $(call word-colon,2,$(pair))) \ + $(if $(findstring $(jar), $(PRODUCT_BOOT_JARS)), \ + $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is))) + +ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT) +ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST) + +PRODUCT_OTA_PUBLIC_KEYS := $(sort $(PRODUCT_OTA_PUBLIC_KEYS)) +PRODUCT_EXTRA_OTA_KEYS := $(sort $(PRODUCT_EXTRA_OTA_KEYS)) +PRODUCT_EXTRA_RECOVERY_KEYS := $(sort $(PRODUCT_EXTRA_RECOVERY_KEYS)) + +# Resolve and setup per-module dex-preopt configs. +DEXPREOPT_DISABLED_MODULES := +# If a module has multiple setups, the first takes precedence. +_pdpmc_modules := +$(foreach c,$(PRODUCT_DEX_PREOPT_MODULE_CONFIGS),\ + $(eval m := $(firstword $(subst =,$(space),$(c))))\ + $(if $(filter $(_pdpmc_modules),$(m)),,\ + $(eval _pdpmc_modules += $(m))\ + $(eval cf := $(patsubst $(m)=%,%,$(c)))\ + $(eval cf := $(subst $(_PDPMC_SP_PLACE_HOLDER),$(space),$(cf)))\ + $(if $(filter disable,$(cf)),\ + $(eval DEXPREOPT_DISABLED_MODULES += $(m)),\ + $(eval DEXPREOPT.$(TARGET_PRODUCT).$(m).CONFIG := $(cf))))) +_pdpmc_modules := + + +# Resolve and setup per-module sanitizer configs. +# If a module has multiple setups, the first takes precedence. +_psmc_modules := +$(foreach c,$(PRODUCT_SANITIZER_MODULE_CONFIGS),\ + $(eval m := $(firstword $(subst =,$(space),$(c))))\ + $(if $(filter $(_psmc_modules),$(m)),,\ + $(eval _psmc_modules += $(m))\ + $(eval cf := $(patsubst $(m)=%,%,$(c)))\ + $(eval cf := $(subst $(_PSMC_SP_PLACE_HOLDER),$(space),$(cf)))\ + $(eval SANITIZER.$(TARGET_PRODUCT).$(m).CONFIG := $(cf)))) +_psmc_modules := + +# Reset ADB keys for non-debuggable builds +ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT))) + PRODUCT_ADB_KEYS := +endif +ifneq ($(filter-out 0 1,$(words $(PRODUCT_ADB_KEYS))),) + $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS)) +endif + +# Show a warning wall of text if non-compliance-GSI products set this option. +ifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT + ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64 gsi_car_arm64 gsi_car_x86_64 gsi_tv_arm gsi_tv_arm64,$(PRODUCT_NAME))) + $(warning PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT is set but \ + PRODUCT_NAME ($(PRODUCT_NAME)) doesn't look like a GSI for compliance \ + testing. This is a special configuration for compliance GSI, so do make \ + sure you understand the security implications before setting this \ + option. If you don't know what this option does, then you probably \ + shouldn't set this.) + endif +endif + +ifndef PRODUCT_USE_DYNAMIC_PARTITIONS + PRODUCT_USE_DYNAMIC_PARTITIONS := $(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS) +endif + +# All requirements of PRODUCT_USE_DYNAMIC_PARTITIONS falls back to +# PRODUCT_USE_DYNAMIC_PARTITIONS if not defined. +ifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE + PRODUCT_USE_DYNAMIC_PARTITION_SIZE := $(PRODUCT_USE_DYNAMIC_PARTITIONS) +endif + +ifndef PRODUCT_BUILD_SUPER_PARTITION + PRODUCT_BUILD_SUPER_PARTITION := $(PRODUCT_USE_DYNAMIC_PARTITIONS) +endif + +ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),) + ifdef PRODUCT_SHIPPING_API_LEVEL + ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29)) + PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := true + endif + endif +endif + +ifeq ($(PRODUCT_SET_DEBUGFS_RESTRICTIONS),) + ifdef PRODUCT_SHIPPING_API_LEVEL + ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),31)) + PRODUCT_SET_DEBUGFS_RESTRICTIONS := true + endif + endif +endif + +ifdef PRODUCT_SHIPPING_API_LEVEL + ifneq (,$(call math_gt_or_eq,29,$(PRODUCT_SHIPPING_API_LEVEL))) + PRODUCT_PACKAGES += $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29) + endif +endif + +# If build command defines OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS, +# override PRODUCT_EXTRA_VNDK_VERSIONS with it. +ifdef OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS + PRODUCT_EXTRA_VNDK_VERSIONS := $(OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS) +endif + +########################################### +# APEXes are by default not compressed +# +# APEX compression can be forcibly enabled (resp. disabled) by +# setting OVERRIDE_PRODUCT_COMPRESSED_APEX to true (resp. false), e.g. by +# setting the OVERRIDE_PRODUCT_COMPRESSED_APEX environment variable. +ifdef OVERRIDE_PRODUCT_COMPRESSED_APEX + PRODUCT_COMPRESSED_APEX := $(OVERRIDE_PRODUCT_COMPRESSED_APEX) +endif + +$(KATI_obsolete_var OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS \ + ,Use PRODUCT_EXTRA_VNDK_VERSIONS instead) + +# If build command defines OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE, +# override PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE with it unless it is +# defined as `false`. If the value is `false` clear +# PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE +# OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE can be used for +# testing only. +ifdef OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE + ifeq (false,$(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE)) + PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := + else + PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := $(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE) + endif +else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),) + # No shipping level defined +else ifeq ($(call math_gt,$(PRODUCT_SHIPPING_API_LEVEL),29),true) + # Enforce product interface if PRODUCT_SHIPPING_API_LEVEL is greater than 29. + PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true +endif + +$(KATI_obsolete_var OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE,Use PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE instead) + +# If build command defines PRODUCT_USE_PRODUCT_VNDK_OVERRIDE as `false`, +# PRODUCT_PRODUCT_VNDK_VERSION will not be defined automatically. +# PRODUCT_USE_PRODUCT_VNDK_OVERRIDE can be used for testing only. +PRODUCT_USE_PRODUCT_VNDK := false +ifneq ($(PRODUCT_USE_PRODUCT_VNDK_OVERRIDE),) + PRODUCT_USE_PRODUCT_VNDK := $(PRODUCT_USE_PRODUCT_VNDK_OVERRIDE) +else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),) + # No shipping level defined +else ifeq ($(call math_gt,$(PRODUCT_SHIPPING_API_LEVEL),29),true) + # Enforce product interface for VNDK if PRODUCT_SHIPPING_API_LEVEL is greater + # than 29. + PRODUCT_USE_PRODUCT_VNDK := true +endif + +ifeq ($(PRODUCT_USE_PRODUCT_VNDK),true) + ifndef PRODUCT_PRODUCT_VNDK_VERSION + PRODUCT_PRODUCT_VNDK_VERSION := current + endif +endif + +$(KATI_obsolete_var PRODUCT_USE_PRODUCT_VNDK,Use PRODUCT_PRODUCT_VNDK_VERSION instead) +$(KATI_obsolete_var PRODUCT_USE_PRODUCT_VNDK_OVERRIDE,Use PRODUCT_PRODUCT_VNDK_VERSION instead) + +ifdef PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS + $(error PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS is deprecated, consider using RRO for \ + $(PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS)) +endif + +define product-overrides-config +$$(foreach rule,$$(PRODUCT_$(1)_OVERRIDES),\ + $$(if $$(filter 2,$$(words $$(subst :,$$(space),$$(rule)))),,\ + $$(error Rule "$$(rule)" in PRODUCT_$(1)_OVERRIDE is not :))) +endef + +$(foreach var, \ + MANIFEST_PACKAGE_NAME \ + PACKAGE_NAME \ + CERTIFICATE, \ + $(eval $(call product-overrides-config,$(var)))) + +# Macro to use below. $(1) is the name of the partition +define product-build-image-config +ifneq ($$(filter-out true false,$$(PRODUCT_BUILD_$(1)_IMAGE)),) + $$(error Invalid PRODUCT_BUILD_$(1)_IMAGE: $$(PRODUCT_BUILD_$(1)_IMAGE) -- true false and empty are supported) +endif +endef + +# Copy and check the value of each PRODUCT_BUILD_*_IMAGE variable +$(foreach image, \ + PVMFW \ + SYSTEM \ + SYSTEM_OTHER \ + VENDOR \ + PRODUCT \ + SYSTEM_EXT \ + ODM \ + VENDOR_DLKM \ + ODM_DLKM \ + SYSTEM_DLKM \ + CACHE \ + RAMDISK \ + USERDATA \ + BOOT \ + RECOVERY, \ + $(eval $(call product-build-image-config,$(image)))) + +product-build-image-config := + +$(call readonly-product-vars) diff --git a/make/core/product_config.rbc b/make/core/product_config.rbc new file mode 100644 index 0000000..0189323 --- /dev/null +++ b/make/core/product_config.rbc @@ -0,0 +1,887 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Runtime functions.""" + +_soong_config_namespaces_key = "$SOONG_CONFIG_NAMESPACES" +_dist_for_goals_key = "$dist_for_goals" +def _init_globals(input_variables_init): + """Initializes dictionaries of global variables. + + This function runs the given input_variables_init function, + passing it a globals dictionary and a handle as if it + were a regular product. It then returns 2 copies of + the globals dictionary, so that one can be kept around + to diff changes made to the other later. + """ + globals_base = {"PRODUCT_SOONG_NAMESPACES": []} + input_variables_init(globals_base, __h_new()) + + # Rerun input_variables_init to produce a copy + # of globals_base, because starlark doesn't support + # deep copying objects. + globals = {"PRODUCT_SOONG_NAMESPACES": []} + input_variables_init(globals, __h_new()) + + # Variables that should be defined. + mandatory_vars = [ + "PLATFORM_VERSION_CODENAME", + "PLATFORM_VERSION", + "PRODUCT_SOONG_NAMESPACES", + # TODO(asmundak): do we need TARGET_ARCH? AOSP does not reference it + "TARGET_BUILD_VARIANT", + "TARGET_PRODUCT", + ] + for bv in mandatory_vars: + if not bv in globals: + fail(bv, " is not defined") + + return (globals, globals_base) + +def __print_attr(attr, value): + # Allow using empty strings to clear variables, but not None values + if value == None: + return + if type(value) == "list": + if _options.rearrange: + value = __printvars_rearrange_list(value) + if _options.format == "pretty": + print(attr, "=", repr(value)) + elif _options.format == "make": + print(attr, ":=", " ".join(value)) + elif _options.format == "pretty": + print(attr, "=", repr(value)) + elif _options.format == "make": + # Trim all spacing to a single space + print(attr, ":=", _mkstrip(value)) + else: + fail("bad output format", _options.format) + +def _printvars(state): + """Prints configuration and global variables.""" + (globals, globals_base) = state + for attr, val in sorted(globals.items()): + if attr == _soong_config_namespaces_key: + __print_attr("SOONG_CONFIG_NAMESPACES", val.keys()) + for nsname, nsvars in sorted(val.items()): + # Define SOONG_CONFIG_ for Make, othewise + # it cannot be added to .KATI_READONLY list + if _options.format == "make": + print("SOONG_CONFIG_" + nsname, ":=", " ".join(nsvars.keys())) + for var, val in sorted(nsvars.items()): + if val: + __print_attr("SOONG_CONFIG_%s_%s" % (nsname, var), val) + else: + print("SOONG_CONFIG_%s_%s :=" % (nsname, var)) + elif attr == _dist_for_goals_key: + goals = [] + src_dst_list = [] + goal_dst_list = [] + for goal_name, goal_src_dst_list in sorted(val.items()): + goals.append(goal_name) + for sd in sorted(goal_src_dst_list): + src_dst_list.append(":".join(sd)) + goal_dst_list.append(":".join((goal_name, sd[1]))) + print("_all_dist_goal_output_pairs:=", " ".join(goal_dst_list)) + print("_all_dist_goals:=", " ".join(goals)) + print("_all_dist_src_dst_pairs:=", " ".join(src_dst_list)) + elif attr not in globals_base or globals_base[attr] != val: + __print_attr(attr, val) + +def __printvars_rearrange_list(value_list): + """Rearrange value list: return only distinct elements, maybe sorted.""" + seen = {item: 0 for item in value_list} + return sorted(seen.keys()) if _options.rearrange == "sort" else seen.keys() + +def __sort_pcm_names(pcm_names): + # We have to add an extension back onto the pcm names when sorting, + # or else the sort order could be wrong when one is a prefix of another. + return [x[:-3] for x in sorted([y + ".mk" for y in pcm_names], reverse=True)] + +def _product_configuration(top_pcm_name, top_pcm, input_variables_init): + """Creates configuration.""" + + # Product configuration is created by traversing product's inheritance + # tree. It is traversed twice. + # First, beginning with top-level module we execute a module and find + # its ancestors, repeating this recursively. At the end of this phase + # we get the full inheritance tree. + # Second, we traverse the tree in the postfix order (i.e., visiting a + # node after its ancestors) to calculate the product configuration. + # + # PCM means "Product Configuration Module", i.e., a Starlark file + # whose body consists of a single init function. + + globals, globals_base = _init_globals(input_variables_init) + + # Each PCM is represented by a quadruple of function, config, children names + # and readyness (that is, the configurations from inherited PCMs have been + # substituted). + configs = {top_pcm_name: (top_pcm, None, [], False)} # All known PCMs + + # Stack containing PCMs to be processed + pcm_stack = [top_pcm_name] + + # Run it until pcm_stack is exhausted, but no more than N times + for n in range(1000): + if not pcm_stack: + break + name = pcm_stack.pop() + pcm, cfg, c, _ = configs[name] + + # cfg is set only after PCM has been called, leverage this + # to prevent calling the same PCM twice + if cfg != None: + continue + + # Run this one, obtaining its configuration and child PCMs. + if _options.trace_modules: + print("#%d: %s" % (n, name)) + + # Run PCM. + handle = __h_new() + pcm(globals, handle) + + if handle.artifact_path_requirements: + globals["PRODUCTS."+name+".mk.ARTIFACT_PATH_REQUIREMENTS"] = handle.artifact_path_requirements + globals["PRODUCTS."+name+".mk.ARTIFACT_PATH_ALLOWED_LIST"] = handle.artifact_path_allowed_list + globals["PRODUCTS."+name+".mk.ARTIFACT_PATH_REQUIREMENT_IS_RELAXED"] = "true" if handle.artifact_path_requirement_is_relaxed[0] else "" + globals.setdefault("ARTIFACT_PATH_REQUIREMENT_PRODUCTS", []) + globals["ARTIFACT_PATH_REQUIREMENT_PRODUCTS"] = sorted(globals["ARTIFACT_PATH_REQUIREMENT_PRODUCTS"] + [name+".mk"]) + + if handle.product_enforce_packages_exist[0]: + globals["PRODUCTS."+name+".mk.PRODUCT_ENFORCE_PACKAGES_EXIST"] = "true" + globals["PRODUCTS."+name+".mk.PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST"] = handle.product_enforce_packages_exist_allow_list + + # Now we know everything about this PCM, record it in 'configs'. + children = handle.inherited_modules + if _options.trace_modules: + print("# ", " ".join(children.keys())) + # Starlark dictionaries are guaranteed to iterate through in insertion order, + # so children.keys() will be ordered by the inherit() calls + configs[name] = (pcm, handle.cfg, children.keys(), False) + + for child_name in __sort_pcm_names(children.keys()): + if child_name not in configs: + configs[child_name] = (children[child_name], None, [], False) + pcm_stack.append(child_name) + if pcm_stack: + fail("Inheritance processing took too many iterations") + + for pcm_name in globals.get("ARTIFACT_PATH_REQUIREMENT_PRODUCTS", []): + for var, val in evaluate_finalized_product_variables(configs, pcm_name[:-3]).items(): + globals["PRODUCTS."+pcm_name+"."+var] = val + + # Copy product config variables from the cfg dictionary to the + # PRODUCTS.. global variables. + for var, val in evaluate_finalized_product_variables(configs, top_pcm_name, _options.trace_modules).items(): + globals["PRODUCTS."+top_pcm_name+".mk."+var] = val + + # Record inheritance hierarchy in PRODUCTS..INHERITS_FROM variables. + # This is required for m product-graph. + for config in configs: + if len(configs[config][2]) > 0: + globals["PRODUCTS."+config+".mk.INHERITS_FROM"] = sorted([x + ".mk" for x in configs[config][2]]) + globals["PRODUCTS"] = __words(globals.get("PRODUCTS", [])) + [top_pcm_name + ".mk"] + + return (globals, globals_base) + +def evaluate_finalized_product_variables(configs, top_level_pcm_name, trace=False): + configs_postfix = [] + pcm_stack = [(top_level_pcm_name, True)] + for i in range(1000): + if not pcm_stack: + break + + pcm_name, before = pcm_stack.pop() + if before: + pcm_stack.append((pcm_name, False)) + for child in __sort_pcm_names(configs[pcm_name][2]): + pcm_stack.append((child, True)) + else: + configs_postfix.append(pcm_name) + if pcm_stack: + fail("Inheritance processing took too many iterations") + + # clone the configs, because in the process of evaluating the + # final cfg dictionary we will remove values from the intermediate + # cfg dictionaries. We need to be able to call evaluate_finalized_product_variables() + # multiple times, so we can't change the origional configs object. + cloned_configs = {} + for pcm_name in configs: + # skip unneeded pcms + if pcm_name not in configs_postfix: + continue + pcm, cfg, children_names, ready = configs[pcm_name] + cloned_cfg = {} + for var, val in cfg.items(): + if type(val) == 'list': + cloned_cfg[var] = list(val) + else: + cloned_cfg[var] = val + cloned_configs[pcm_name] = (pcm, cloned_cfg, children_names, ready) + configs = cloned_configs + + if trace: + print("\n#---Postfix---") + for x in configs_postfix: + print("# ", x) + + # Traverse the tree from the bottom, evaluating inherited values + for pcm_name in configs_postfix: + pcm, cfg, children_names, ready = configs[pcm_name] + + # Should run + if cfg == None: + fail("%s: has not been run" % pcm_name) + + # Ready once + if ready: + continue + + # Children should be ready + for child_name in children_names: + if not configs[child_name][3]: + fail("%s: child is not ready" % child_name) + + _substitute_inherited(configs, pcm_name, cfg) + _percolate_inherited(configs, pcm_name, cfg, children_names) + configs[pcm_name] = pcm, cfg, children_names, True + return configs[top_level_pcm_name][1] + +def _dictionary_difference(a, b): + result = {} + for attr, val in a.items(): + if attr not in b or b[attr] != val: + result[attr] = val + return result + +def _board_configuration(board_config_init, input_variables_init): + globals_base = {} + h_base = __h_new() + globals = {} + h = __h_new() + + input_variables_init(globals_base, h_base) + input_variables_init(globals, h) + board_config_init(globals, h) + + # Board configuration files aren't really supposed to change + # product configuration variables, but some do. You lose the + # inheritance features of the product config variables if you do. + for var, value in _dictionary_difference(h.cfg, h_base.cfg).items(): + globals[var] = value + + return (globals, globals_base) + + +def _substitute_inherited(configs, pcm_name, cfg): + """Substitutes inherited values in all the attributes. + + When a value of an attribute is a list, some of its items may be + references to a value of a same attribute in an inherited product, + e.g., for a given module PRODUCT_PACKAGES can be + ["foo", (submodule), "bar"] + and for 'submodule' PRODUCT_PACKAGES may be ["baz"] + (we use a tuple to distinguish submodule references). + After the substitution the value of PRODUCT_PACKAGES for the module + will become ["foo", "baz", "bar"] + """ + for attr, val in cfg.items(): + # TODO(asmundak): should we handle single vars? + if type(val) != "list": + continue + + if attr not in _options.trace_variables: + cfg[attr] = _value_expand(configs, attr, val) + else: + old_val = val + new_val = _value_expand(configs, attr, val) + if new_val != old_val: + print("%s(i): %s=%s (was %s)" % (pcm_name, attr, new_val, old_val)) + cfg[attr] = new_val + +def _value_expand(configs, attr, values_list): + """Expands references to inherited values in a given list.""" + result = [] + expanded = {} + for item in values_list: + # Inherited values are 1-tuples + if type(item) != "tuple": + result.append(item) + continue + child_name = item[0] + if child_name in expanded: + continue + expanded[child_name] = True + child = configs[child_name] + if not child[3]: + fail("%s should be ready" % child_name) + __move_items(result, child[1], attr) + + return result + +def _percolate_inherited(configs, cfg_name, cfg, children_names): + """Percolates the settings that are present only in children.""" + percolated_attrs = {} + for child_name in children_names: + child_cfg = configs[child_name][1] + for attr, value in child_cfg.items(): + if type(value) != "list": + continue + if attr in percolated_attrs: + # We already are percolating this one, just add this list + __move_items(cfg[attr], child_cfg, attr) + elif not attr in cfg: + percolated_attrs[attr] = True + cfg[attr] = [] + __move_items(cfg[attr], child_cfg, attr) + + # single value variables need to be inherited in alphabetical order, + # not in the order of inherit() calls. + for child_name in sorted(children_names): + child_cfg = configs[child_name][1] + for attr, value in child_cfg.items(): + if type(value) != "list": + # Single value variables take the first value available from the leftmost + # branch of the tree. If we also had "or attr in percolated_attrs" in this + # if statement, it would take the value from the rightmost branch. + if cfg.get(attr, "") == "": + cfg[attr] = value + percolated_attrs[attr] = True + + for attr in _options.trace_variables: + if attr in percolated_attrs: + print("%s: %s^=%s" % (cfg_name, attr, cfg[attr])) + +def __move_items(to_list, from_cfg, attr): + value = from_cfg.get(attr, []) + if value: + to_list.extend(value) + from_cfg[attr] = [] + +def _indirect(pcm_name): + """Returns configuration item for the inherited module.""" + return (pcm_name,) + +def _soong_config_namespace(g, nsname): + """Adds given namespace if it does not exist.""" + + old = g.get(_soong_config_namespaces_key, {}) + if old.get(nsname): + return + + # A value cannot be updated, so we need to create a new dictionary + g[_soong_config_namespaces_key] = dict([(k,v) for k,v in old.items()] + [(nsname, {})]) + +def _soong_config_set(g, nsname, var, value): + """Assigns the value to the variable in the namespace.""" + _soong_config_namespace(g, nsname) + g[_soong_config_namespaces_key][nsname][var]=value + +def _soong_config_append(g, nsname, var, value): + """Appends to the value of the variable in the namespace.""" + _soong_config_namespace(g, nsname) + ns = g[_soong_config_namespaces_key][nsname] + oldv = ns.get(var) + if oldv == None: + ns[var] = value + else: + ns[var] += " " + value + + +def _soong_config_get(g, nsname, var): + """Gets to the value of the variable in the namespace.""" + return g.get(_soong_config_namespaces_key, {}).get(nsname, {}).get(var, None) + +def _abspath(paths): + """Provided for compatibility, to be removed later.""" + cwd = rblf_shell('pwd') + results = [] + for path in __words(paths): + if path[0] != "/": + path = cwd + "/" + path + + resultparts = [] + for part in path.split('/'): + if part == "." or part == "": + continue + elif part == "..": + if resultparts: + resultparts.pop() + else: + resultparts.append(part) + results.append("/" + "/".join(resultparts)) + + return " ".join(results) + + +def _addprefix(prefix, string_or_list): + """Adds prefix and returns a list. + + If string_or_list is a list, prepends prefix to each element. + Otherwise, string_or_list is considered to be a string which + is split into words and then prefix is prepended to each one. + + Args: + prefix + string_or_list + + """ + return [prefix + x for x in __words(string_or_list)] + +def _addsuffix(suffix, string_or_list): + """Adds suffix and returns a list. + + If string_or_list is a list, appends suffix to each element. + Otherwise, string_or_list is considered to be a string which + is split into words and then suffix is appended to each one. + + Args: + suffix + string_or_list + """ + return [x + suffix for x in __words(string_or_list)] + +def __words(string_or_list): + if type(string_or_list) == "list": + string_or_list = " ".join(string_or_list) + return _mkstrip(string_or_list).split() + +# Handle manipulation functions. +# A handle passed to a PCM consists of: +# product attributes dict ("cfg") +# inherited modules dict (maps module name to PCM) +# default value list (initially empty, modified by inheriting) +def __h_new(): + """Constructs a handle which is passed to PCM.""" + return struct( + cfg = dict(), + inherited_modules = dict(), + default_list_value = list(), + artifact_path_requirements = list(), + artifact_path_allowed_list = list(), + artifact_path_requirement_is_relaxed = [False], # as a list so that we can reassign it + product_enforce_packages_exist = [False], + product_enforce_packages_exist_allow_list = [], + ) + +def __h_cfg(handle): + """Returns PCM's product configuration attributes dict. + + This function is also exported as rblf.cfg, and every PCM + calls it at the beginning. + """ + return handle.cfg + +def _setdefault(handle, attr): + """If attribute has not been set, assigns default value to it. + + This function is exported as rblf.setdefault(). + Only list attributes are initialized this way. The default + value is kept in the PCM's handle. Calling inherit() updates it. + """ + cfg = handle.cfg + if cfg.get(attr) == None: + cfg[attr] = list(handle.default_list_value) + return cfg[attr] + +def _inherit(handle, pcm_name, pcm): + """Records inheritance. + + This function is exported as rblf.inherit, PCM calls it when + a module is inherited. + """ + handle.inherited_modules[pcm_name] = pcm + handle.default_list_value.append(_indirect(pcm_name)) + + # Add inherited module reference to all configuration values + for attr, val in handle.cfg.items(): + if type(val) == "list": + val.append(_indirect(pcm_name)) + +def __base(path): + """Returns basename.""" + return path.rsplit("/",1)[-1] + +def _board_platform_in(g, string_or_list): + """Returns true if board is in the list.""" + board = g.get("TARGET_BOARD_PLATFORM","") + if not board: + return False + return board in __words(string_or_list) + + +def _board_platform_is(g, s): + """True if board is the same as argument.""" + return g.get("TARGET_BOARD_PLATFORM","") == s + + +def _copy_files(l, outdir): + """Generate :/item for each item.""" + return ["%s:%s/%s" % (path, outdir, __base(path)) for path in __words(l)] + +def _copy_if_exists(path_pair): + """If from file exists, returns [from:to] pair.""" + value = path_pair.split(":", 2) + + # Check that l[0] exists + return [":".join(value)] if rblf_file_exists(value[0]) else [] + +def _enforce_product_packages_exist(handle, pkg_string_or_list=[]): + """Makes including non-existent modules in PRODUCT_PACKAGES an error.""" + handle.product_enforce_packages_exist[0] = True + handle.product_enforce_packages_exist_allow_list.clear() + handle.product_enforce_packages_exist_allow_list.extend(__words(pkg_string_or_list)) + +def _add_product_dex_preopt_module_config(handle, modules, config): + """Equivalent to add-product-dex-preopt-module-config from build/make/core/product.mk.""" + modules = __words(modules) + config = _mkstrip(config).replace(" ", "|@SP@|") + _setdefault(handle, "PRODUCT_DEX_PREOPT_MODULE_CONFIGS") + handle.cfg["PRODUCT_DEX_PREOPT_MODULE_CONFIGS"] += [m + "=" + config for m in modules] + +def _file_wildcard_exists(file_pattern): + """Return True if there are files matching given bash pattern.""" + return len(rblf_wildcard(file_pattern)) > 0 + +def _find_and_copy(pattern, from_dir, to_dir): + """Return a copy list for the files matching the pattern.""" + return sorted([("%s/%s:%s/%s" % (from_dir, f, to_dir, f)) + .replace("//", "/") for f in rblf_find_files(from_dir, pattern, only_files=1)]) + +def _findstring(needle, haystack): + """Equivalent to GNU make's $(findstring).""" + if haystack.find(needle) < 0: + return "" + return needle + +def _filter_out(pattern, text): + """Return all the words from `text' that do not match any word in `pattern'. + + Args: + pattern: string or list of words. '%' stands for wildcard (in regex terms, '.*') + text: string or list of words + Return: + list of words + """ + patterns = [__mkparse_pattern(x) for x in __words(pattern)] + res = [] + for w in __words(text): + match = False + for p in patterns: + if __mkpattern_matches(p, w): + match = True + break + if not match: + res.append(w) + return res + +def _filter(pattern, text): + """Return all the words in `text` that match `pattern`. + + Args: + pattern: strings of words or a list. A word can contain '%', + which stands for any sequence of characters. + text: string or list of words. + """ + patterns = [__mkparse_pattern(x) for x in __words(pattern)] + res = [] + for w in __words(text): + for p in patterns: + if __mkpattern_matches(p, w): + res.append(w) + break + return res + +def _dir(paths): + """Equivalent to the GNU make function $(dir). + + Returns the folder of the file for each path in paths. + """ + return " ".join([w.rsplit("/",1)[0] for w in __words(paths)]) + +def _notdir(paths): + """Equivalent to the GNU make function $(notdir). + + Returns the name of the file at the end of each path in paths. + """ + return " ".join([__base(w) for w in __words(paths)]) + +def _require_artifacts_in_path(handle, paths, allowed_paths): + """Equivalent to require-artifacts-in-path in Make.""" + handle.artifact_path_requirements.clear() + handle.artifact_path_requirements.extend(__words(paths)) + handle.artifact_path_allowed_list.clear() + handle.artifact_path_allowed_list.extend(__words(allowed_paths)) + +def _require_artifacts_in_path_relaxed(handle, paths, allowed_paths): + """Equivalent to require-artifacts-in-path-relaxed in Make.""" + _require_artifacts_in_path(handle, paths, allowed_paths) + handle.artifact_path_requirement_is_relaxed[0] = True + +def _expand_wildcard(pattern): + """Expands shell wildcard pattern.""" + result = [] + for word in __words(pattern): + result.extend(rblf_wildcard(word)) + return result + +def _mkdist_for_goals(g, goal, src_dst_list): + """Implements dist-for-goals macro.""" + goals_map = g.get(_dist_for_goals_key, {}) + pairs = goals_map.get(goal) + if pairs == None: + pairs = [] + g[_dist_for_goals_key] = dict([(k,v) for k,v in goals_map.items()] + [(goal, pairs)]) + for src_dst in __words(src_dst_list): + pair=src_dst.split(":") + if len(pair) > 2: + fail(src_dst + " should be a :-separated pair") + pairs.append((pair[0],pair[1] if len(pair) == 2 and pair[1] else __base(pair[0]))) + g[_dist_for_goals_key][goal] = pairs + + +def _mkerror(file, message = ""): + """Prints error and stops.""" + fail("%s: %s. Stop" % (file, message)) + +def _mkwarning(file, message = ""): + """Prints warning.""" + rblf_log(file, "warning", message, sep = ':') + +def _mk2rbc_error(loc, message): + """Prints a message about conversion error and stops. + + If RBC_MK2RBC_CONTINUE environment variable is set, + the execution will continue after the message is printed. + """ + if _options.mk2rbc_continue: + rblf_log(loc, message, sep = ':') + else: + _mkerror(loc, message) + + +def _mkinfo(file, message = ""): + """Prints info.""" + rblf_log(message) + + +def __mkparse_pattern(pattern): + """Parses Make's patsubst pattern. + + This is equivalent to pattern.split('%', 1), except it + also takes into account escaping the % symbols. + """ + in_escape = False + res = [] + acc = "" + for c in pattern.elems(): + if in_escape: + in_escape = False + acc += c + elif c == '\\': + in_escape = True + elif c == '%' and not res: + res.append(acc) + acc = '' + else: + acc += c + if in_escape: + acc += '\\' + res.append(acc) + return res + +def __mkpattern_matches(pattern, word): + """Returns if a pattern matches a given word. + + The pattern must be a list of strings of length at most 2. + This checks if word is either equal to the pattern or + starts/ends with the two parts of the pattern. + """ + if len(pattern) > 2: + fail("Pattern can have at most 2 components") + elif len(pattern) == 1: + return pattern[0]==word + else: + return ((len(word) >= len(pattern[0])+len(pattern[1])) + and word.startswith(pattern[0]) + and word.endswith(pattern[1])) + +def __mkpatsubst_word(parsed_pattern,parsed_subst, word): + (before, after) = parsed_pattern + if not word.startswith(before): + return word + if not word.endswith(after): + return word + if len(parsed_subst) < 2: + return parsed_subst[0] + return parsed_subst[0] + word[len(before):len(word) - len(after)] + parsed_subst[1] + + +def _mkpatsubst(pattern, replacement, s): + """Emulates Make's patsubst. + + Tokenizes `s` (unless it is already a list), and then performs a simple + wildcard substitution (in other words, `foo%bar` pattern is equivalent to + the regular expression `^foo(.*)bar$, and the first `%` in replacement is + $1 in regex terms). + """ + parsed_pattern = __mkparse_pattern(pattern) + if len(parsed_pattern) == 1: + out_words = [ replacement if x == pattern else x for x in __words(s)] + else: + parsed_replacement = __mkparse_pattern(replacement) + out_words = [__mkpatsubst_word(parsed_pattern, parsed_replacement, x) for x in __words(s)] + return out_words if type(s) == "list" else " ".join(out_words) + + +def _mksort(input): + """Emulate Make's sort. + + This is unique from a regular sort in that it also strips + the input, and removes duplicate words from the input. + """ + input = sorted(__words(input)) + result = [] + for w in input: + if len(result) == 0 or result[-1] != w: + result.append(w) + return result + + +def _mkstrip(s): + """Emulates Make's strip. + + That is, removes string's leading and trailing whitespace characters and + replaces any sequence of whitespace characters with with a single space. + """ + if type(s) != "string": + return s + result = "" + was_space = False + for ch in s.strip().elems(): + is_space = ch.isspace() + if not is_space: + if was_space: + result += " " + result += ch + was_space = is_space + return result + +def _mksubst(old, new, s): + """Emulates Make's subst. + + Replaces each occurence of 'old' with 'new'. + If 's' is a list, applies substitution to each item. + """ + if type(s) == "list": + return [e.replace(old, new) for e in s] + return s.replace(old, new) + + +def _product_copy_files_by_pattern(src, dest, s): + """Creates a copy list. + + For each item in a given list, create : pair, where and + are the results of applying Make-style patsubst of and + respectively. E.g. the result of calling this function with + ("foo/%", "bar/%", ["a", "b"]) will be + ["foo/a:bar/a", "foo/b:bar/b"]. + """ + parsed_src = __mkparse_pattern(src) + parsed_dest = __mkparse_pattern(dest) + parsed_percent = ["", ""] + words = s if type(s) == "list" else _mkstrip(s).split(" ") + return [ __mkpatsubst_word(parsed_percent, parsed_src, x) + ":" + __mkpatsubst_word(parsed_percent, parsed_dest, x) for x in words] + + +def __get_options(): + """Returns struct containing runtime global settings.""" + settings = dict( + format = "pretty", + rearrange = "", + trace_modules = False, + trace_variables = [], + mk2rbc_continue = False, + ) + for x in getattr(rblf_cli, "RBC_OUT", "").split(","): + if x == "sort" or x == "unique": + if settings["rearrange"]: + fail("RBC_OUT: either sort or unique is allowed (and sort implies unique)") + settings["rearrange"] = x + elif x == "pretty" or x == "make": + settings["format"] = x + elif x == "global": + # TODO: Remove this, kept for backwards compatibility + pass + elif x != "": + fail("RBC_OUT: got %s, should be one of: [pretty|make] [sort|unique]" % x) + for x in getattr(rblf_cli, "RBC_DEBUG", "").split(","): + if x == "!trace": + settings["trace_modules"] = True + elif x != "": + settings["trace_variables"].append(x) + if getattr(rblf_cli, "RBC_MK2RBC_CONTINUE", ""): + settings["mk2rbc_continue"] = True + return struct(**settings) + +# Settings used during debugging. +_options = __get_options() +rblf = struct( + soong_config_namespace = _soong_config_namespace, + soong_config_append = _soong_config_append, + soong_config_set = _soong_config_set, + soong_config_get = _soong_config_get, + abspath = _abspath, + add_product_dex_preopt_module_config = _add_product_dex_preopt_module_config, + addprefix = _addprefix, + addsuffix = _addsuffix, + board_platform_in = _board_platform_in, + board_platform_is = _board_platform_is, + copy_files = _copy_files, + copy_if_exists = _copy_if_exists, + cfg = __h_cfg, + dir = _dir, + enforce_product_packages_exist = _enforce_product_packages_exist, + expand_wildcard = _expand_wildcard, + file_exists = rblf_file_exists, + file_wildcard_exists = _file_wildcard_exists, + filter = _filter, + filter_out = _filter_out, + find_and_copy = _find_and_copy, + findstring = _findstring, + inherit = _inherit, + indirect = _indirect, + mk2rbc_error = _mk2rbc_error, + mkdist_for_goals = _mkdist_for_goals, + mkinfo = _mkinfo, + mkerror = _mkerror, + mkpatsubst = _mkpatsubst, + mkwarning = _mkwarning, + mksort = _mksort, + mkstrip = _mkstrip, + mksubst = _mksubst, + notdir = _notdir, + printvars = _printvars, + product_configuration = _product_configuration, + board_configuration = _board_configuration, + product_copy_files_by_pattern = _product_copy_files_by_pattern, + require_artifacts_in_path = _require_artifacts_in_path, + require_artifacts_in_path_relaxed = _require_artifacts_in_path_relaxed, + setdefault = _setdefault, + shell = rblf_shell, + warning = _mkwarning, + words = __words, +) diff --git a/make/core/proguard.flags b/make/core/proguard.flags new file mode 100644 index 0000000..185275e --- /dev/null +++ b/make/core/proguard.flags @@ -0,0 +1,38 @@ +# We have moved -dontobfuscate and -dontoptimize to the makefiles. +# dex does not like code run through proguard optimize and preverify steps. +# -dontoptimize +-dontpreverify + +# Don't obfuscate. We only need dead code striping. +# -dontobfuscate + +# Add this flag in your package's own configuration if it's needed. +#-flattenpackagehierarchy + +# Keep classes and methods that have the guava @VisibleForTesting annotation +-keep @**.VisibleForTesting class * +-keepclassmembers class * { +@**.VisibleForTesting *; +} + +# Understand the common @Keep annotation from various Android packages: +# * android.support.annotation +# * androidx.annotation +# * com.android.internal.annotations +-keep class **android**.annotation*.Keep + +-keep @**android**.annotation*.Keep class * { *; } + +-keepclasseswithmembers class * { + @**android**.annotation*.Keep ; +} + +-keepclasseswithmembers class * { + @**android**.annotation*.Keep ; +} + +-keepclasseswithmembers class * { + @**android**.annotation*.Keep (...); +} + +-include proguard_basic_keeps.flags diff --git a/make/core/proguard.jacoco.flags b/make/core/proguard.jacoco.flags new file mode 100644 index 0000000..c3bed94 --- /dev/null +++ b/make/core/proguard.jacoco.flags @@ -0,0 +1,8 @@ +# Keep everything for the emma classes +-keep class com.vladium.** { + *; +} +# Keep everything for the jacoco classes +-keep class org.jacoco.** { + *; +} diff --git a/make/core/proguard_basic_keeps.flags b/make/core/proguard_basic_keeps.flags new file mode 100644 index 0000000..30c2341 --- /dev/null +++ b/make/core/proguard_basic_keeps.flags @@ -0,0 +1,78 @@ +# Some classes in the libraries extend package private classes to chare common functionality +# that isn't explicitly part of the API +-dontskipnonpubliclibraryclasses -dontskipnonpubliclibraryclassmembers + +# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +} + +# class$ methods are inserted by some compilers to implement .class construct, +# see http://proguard.sourceforge.net/manual/examples.html#library +-keepclassmembernames class * { + java.lang.Class class$(java.lang.String); + java.lang.Class class$(java.lang.String, boolean); +} + +# Keep serializable classes and necessary members for serializable classes +# Copied from the ProGuard manual at http://proguard.sourceforge.net. +-keepnames class * implements java.io.Serializable +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + !static !transient ; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# Keep Throwable's constructor that takes a String argument. +-keepclassmembers class * extends java.lang.Throwable { + (java.lang.String); +} + +# Please specify classes to be kept explicitly in your package's configuration. +# -keep class * extends android.app.Activity +# -keep class * extends android.view.View +# -keep class * extends android.app.Service +# -keep class * extends android.content.BroadcastReceiver +# -keep class * extends android.content.ContentProvider +# -keep class * extends android.preference.Preference +# -keep class * extends android.app.BackupAgent + +# Parcelable CREATORs must be kept for Parcelable functionality +-keep class * implements android.os.Parcelable { + public static final ** CREATOR; +} + +# The support library contains references to newer platform versions. +# Don't warn about those in case this app is linking against an older +# platform version. We know about them, and they are safe. +# See proguard-android.txt in the SDK package. +# +# DO NOT USE THIS: We figured it's dangerous to blindly ignore all support library warnings. +# ProGuard may strip members of subclass of unknown super classes, in case an app is linking against +# LOCAL_SDK_VERSION lower than the support library's LOCAL_SDK_VERSION. +# See bug/20658265. +# -dontwarn android.support.** + +# From https://github.com/google/guava/wiki/UsingProGuardWithGuava +# Striped64, LittleEndianByteArray, UnsignedBytes, AbstractFuture +-dontwarn sun.misc.Unsafe +# Futures.getChecked (which often won't work with Proguard anyway) uses this. It +# has a fallback, but again, don't use Futures.getChecked on Android regardless. +-dontwarn java.lang.ClassValue + +# Less spammy. +-dontnote + +# The lite proto runtime uses reflection to access fields based on the names in +# the schema, keep all the fields. +-keepclassmembers class * extends com.google.protobuf.MessageLite { ; } diff --git a/make/core/project_definitions.mk b/make/core/project_definitions.mk new file mode 100644 index 0000000..5728b67 --- /dev/null +++ b/make/core/project_definitions.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Allow projects to define their own globally-available variables. +# + +# +# Include definitions for prebuilt SDK, if present. +# +-include prebuilts/sdk/current/definitions.mk diff --git a/make/core/python_binary_host_test_config_template.xml b/make/core/python_binary_host_test_config_template.xml new file mode 100644 index 0000000..0f63953 --- /dev/null +++ b/make/core/python_binary_host_test_config_template.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/make/core/rbe.mk b/make/core/rbe.mk new file mode 100644 index 0000000..fd3427a --- /dev/null +++ b/make/core/rbe.mk @@ -0,0 +1,93 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Notice: this works only with Google's RBE service. +ifneq ($(filter-out false,$(USE_RBE)),) + ifdef RBE_DIR + rbe_dir := $(RBE_DIR) + else + rbe_dir := prebuilts/remoteexecution-client/live/ + endif + + ifdef RBE_CXX_POOL + cxx_pool := $(RBE_CXX_POOL) + else + cxx_pool := default + endif + + ifdef RBE_JAVA_POOL + java_pool := $(RBE_JAVA_POOL) + else + java_pool := java16 + endif + + ifdef RBE_CXX_EXEC_STRATEGY + cxx_rbe_exec_strategy := $(RBE_CXX_EXEC_STRATEGY) + else + cxx_rbe_exec_strategy := local + endif + + ifdef RBE_CXX_COMPARE + cxx_compare := $(RBE_CXX_COMPARE) + else + cxx_compare := false + endif + + ifdef RBE_JAVAC_EXEC_STRATEGY + javac_exec_strategy := $(RBE_JAVAC_EXEC_STRATEGY) + else + javac_exec_strategy := remote_local_fallback + endif + + ifdef RBE_R8_EXEC_STRATEGY + r8_exec_strategy := $(RBE_R8_EXEC_STRATEGY) + else + r8_exec_strategy := remote_local_fallback + endif + + ifdef RBE_D8_EXEC_STRATEGY + d8_exec_strategy := $(RBE_D8_EXEC_STRATEGY) + else + d8_exec_strategy := remote_local_fallback + endif + + platform := container-image=docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62 + cxx_platform := $(platform),Pool=$(cxx_pool) + java_r8_d8_platform := $(platform),Pool=$(java_pool) + + RBE_WRAPPER := $(rbe_dir)/rewrapper + RBE_CXX := --labels=type=compile,lang=cpp,compiler=clang --env_var_allowlist=PWD --exec_strategy=$(cxx_rbe_exec_strategy) --platform=$(cxx_platform) --compare=$(cxx_compare) + + # Append rewrapper to existing *_WRAPPER variables so it's possible to + # use both ccache and rewrapper. + CC_WRAPPER := $(strip $(CC_WRAPPER) $(RBE_WRAPPER) $(RBE_CXX)) + CXX_WRAPPER := $(strip $(CXX_WRAPPER) $(RBE_WRAPPER) $(RBE_CXX)) + + ifdef RBE_JAVAC + JAVAC_WRAPPER := $(strip $(JAVAC_WRAPPER) $(RBE_WRAPPER) --labels=type=compile,lang=java,compiler=javac --exec_strategy=$(javac_exec_strategy) --platform=$(java_r8_d8_platform)) + endif + + ifdef RBE_R8 + R8_WRAPPER := $(strip $(RBE_WRAPPER) --labels=type=compile,compiler=r8 --exec_strategy=$(r8_exec_strategy) --platform=$(java_r8_d8_platform) --inputs=out/soong/host/linux-x86/framework/r8-compat-proguard.jar,build/make/core/proguard_basic_keeps.flags --toolchain_inputs=prebuilts/jdk/jdk11/linux-x86/bin/java) + endif + + ifdef RBE_D8 + D8_WRAPPER := $(strip $(RBE_WRAPPER) --labels=type=compile,compiler=d8 --exec_strategy=$(d8_exec_strategy) --platform=$(java_r8_d8_platform) --inputs=out/soong/host/linux-x86/framework/d8.jar --toolchain_inputs=prebuilts/jdk/jdk11/linux-x86/bin/java) + endif + + rbe_dir := +endif + diff --git a/make/core/robolectric_test_config_template.xml b/make/core/robolectric_test_config_template.xml new file mode 100644 index 0000000..483b957 --- /dev/null +++ b/make/core/robolectric_test_config_template.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/make/core/root.mk b/make/core/root.mk new file mode 100644 index 0000000..1ef9aca --- /dev/null +++ b/make/core/root.mk @@ -0,0 +1,3 @@ +### DO NOT EDIT THIS FILE ### +include build/make/core/main.mk +### DO NOT EDIT THIS FILE ### diff --git a/make/core/rust_device_benchmark_config_template.xml b/make/core/rust_device_benchmark_config_template.xml new file mode 100644 index 0000000..2055df2 --- /dev/null +++ b/make/core/rust_device_benchmark_config_template.xml @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/make/core/rust_device_test_config_template.xml b/make/core/rust_device_test_config_template.xml new file mode 100644 index 0000000..bfd2f47 --- /dev/null +++ b/make/core/rust_device_test_config_template.xml @@ -0,0 +1,30 @@ + + + + + + {EXTRA_CONFIGS} + + + + + + + diff --git a/make/core/rust_host_benchmark_config_template.xml b/make/core/rust_host_benchmark_config_template.xml new file mode 100644 index 0000000..bb7c1b5 --- /dev/null +++ b/make/core/rust_host_benchmark_config_template.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/make/core/rust_host_test_config_template.xml b/make/core/rust_host_test_config_template.xml new file mode 100644 index 0000000..fc23fbe --- /dev/null +++ b/make/core/rust_host_test_config_template.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/make/core/sdk_check.mk b/make/core/sdk_check.mk new file mode 100644 index 0000000..09fd0eb --- /dev/null +++ b/make/core/sdk_check.mk @@ -0,0 +1,37 @@ + +# Enforcement checks that LOCAL_SDK_VERSION and LOCAL_PRIVATE_PLATFORM_APIS are +# set correctly. +# Should be included by java targets that allow specifying LOCAL_SDK_VERSION. +# The JAVA_SDK_ENFORCEMENT_WARNING and JAVA_SDK_ENFORCEMENT_ERROR variables may +# be set to a particular module class to enable warnings and errors for that +# subtype. + +allowed_modules := framework-res__auto_generated_rro + + +ifeq (,$(JAVA_SDK_ENFORCEMENT_ERROR)) + JAVA_SDK_ENFORCEMENT_ERROR := APPS +endif + +ifeq ($(LOCAL_SDK_VERSION)$(LOCAL_PRIVATE_PLATFORM_APIS),) + ifeq (,$(filter $(LOCAL_MODULE),$(allowed_modules))) + ifneq ($(JAVA_SDK_ENFORCEMENT_WARNING)$(JAVA_SDK_ENFORCEMENT_ERROR),) + my_message := Must specify LOCAL_SDK_VERSION or LOCAL_PRIVATE_PLATFORM_APIS, + ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_ERROR)) + $(call pretty-error,$(my_message)) + endif + ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_WARNING)) + $(call pretty-warning,$(my_message)) + endif + my_message := + endif + endif +else ifneq ($(LOCAL_SDK_VERSION),) + ifneq ($(LOCAL_PRIVATE_PLATFORM_APIS),) + my_message := Specifies both LOCAL_SDK_VERSION ($(LOCAL_SDK_VERSION)) and + my_message += LOCAL_PRIVATE_PLATFORM_APIS ($(LOCAL_PRIVATE_PLATFORM_APIS)) + my_message += but should specify only one + $(call pretty-error,$(my_message)) + my_message := + endif +endif diff --git a/make/core/shared_library.mk b/make/core/shared_library.mk new file mode 100644 index 0000000..29d8276 --- /dev/null +++ b/make/core/shared_library.mk @@ -0,0 +1,64 @@ +$(call record-module-type,SHARED_LIBRARY) +ifdef LOCAL_IS_HOST_MODULE + $(call pretty-error,BUILD_SHARED_LIBRARY is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_SHARED_LIBRARY instead.) +endif +my_prefix := TARGET_ +include $(BUILD_SYSTEM)/multilib.mk + +ifndef my_module_multilib +# libraries default to building for both architecturess +my_module_multilib := both +endif + +ifeq ($(my_module_multilib),both) +ifneq ($(LOCAL_MODULE_PATH),) +ifneq ($(TARGET_2ND_ARCH),) +$(error $(LOCAL_MODULE): LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds, use LOCAL_MODULE_RELATIVE_PATH instead) +endif +endif + +ifneq ($(LOCAL_UNSTRIPPED_PATH),) +ifneq ($(TARGET_2ND_ARCH),) +$(error $(LOCAL_MODULE): LOCAL_UNSTRIPPED_PATH for shared libraries is unsupported in multiarch builds) +endif +endif +endif # my_module_multilib == both + + +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +include $(BUILD_SYSTEM)/shared_library_internal.mk +endif + +ifdef TARGET_2ND_ARCH + +LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +# Build for TARGET_2ND_ARCH +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := + +include $(BUILD_SYSTEM)/shared_library_internal.mk + +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := + +endif # TARGET_2ND_ARCH + +my_module_arch_supported := + +########################################################### +## Copy headers to the install tree +########################################################### +ifdef LOCAL_COPY_HEADERS +$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ + $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ + $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) +include $(BUILD_SYSTEM)/copy_headers.mk +endif diff --git a/make/core/shared_library_internal.mk b/make/core/shared_library_internal.mk new file mode 100644 index 0000000..139de10 --- /dev/null +++ b/make/core/shared_library_internal.mk @@ -0,0 +1,104 @@ +########################################################### +## Standard rules for building a normal shared library. +## +## Additional inputs from base_rules.make: +## None. +## +## LOCAL_MODULE_SUFFIX will be set for you. +########################################################### + +ifeq ($(strip $(LOCAL_MODULE_CLASS)),) +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +endif +ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) +LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX) +endif +ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)$(LOCAL_MODULE_STEM_32)$(LOCAL_MODULE_STEM_64)),) +$(error $(LOCAL_PATH): Cannot set module stem for a library) +endif + +ifdef target-shared-library-hook +$(call target-shared-library-hook) +endif + +skip_build_from_source := +ifdef LOCAL_PREBUILT_MODULE_FILE +ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) +include $(BUILD_SYSTEM)/prebuilt_internal.mk +skip_build_from_source := true +endif +endif + +ifndef skip_build_from_source + +include $(BUILD_SYSTEM)/dynamic_binary.mk + +# Define PRIVATE_ variables from global vars +ifeq ($(LOCAL_NO_LIBCRT_BUILTINS),true) +my_target_libcrt_builtins := +else +my_target_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS) +endif +ifeq ($(LOCAL_NO_CRT),true) +my_target_crtbegin_so_o := +my_target_crtend_so_o := +else ifdef LOCAL_USE_VNDK +my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.vendor) +my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.vendor) +else +my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so) +my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so) +endif +ifneq ($(LOCAL_SDK_VERSION),) +my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.sdk.$(my_ndk_crt_version)) +my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.sdk.$(my_ndk_crt_version)) +endif +$(linked_module): PRIVATE_TARGET_LIBCRT_BUILTINS := $(my_target_libcrt_builtins) +$(linked_module): PRIVATE_TARGET_CRTBEGIN_SO_O := $(my_target_crtbegin_so_o) +$(linked_module): PRIVATE_TARGET_CRTEND_SO_O := $(my_target_crtend_so_o) + +$(linked_module): \ + $(all_objects) \ + $(all_libraries) \ + $(my_target_crtbegin_so_o) \ + $(my_target_crtend_so_o) \ + $(my_target_libcrt_builtins) \ + $(LOCAL_ADDITIONAL_DEPENDENCIES) $(CLANG_CXX) + $(transform-o-to-shared-lib) + +ifeq ($(my_native_coverage),true) +gcno_suffix := .zip + +built_whole_gcno_libraries := \ + $(foreach lib,$(my_whole_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ + $(my_host_cross))/$(lib)$(gcno_suffix)) + +built_static_gcno_libraries := \ + $(foreach lib,$(my_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ + $(my_host_cross))/$(lib)$(gcno_suffix)) + +ifdef LOCAL_IS_HOST_MODULE +my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) +else +my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) +endif + +GCNO_ARCHIVE := $(basename $(my_installed_module_stem))$(gcno_suffix) + +$(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES)) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(strip $(built_static_gcno_libraries)) +$(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(built_static_gcno_libraries) + $(package-coverage-files) + +$(my_coverage_path)/$(GCNO_ARCHIVE) : $(intermediates)/$(GCNO_ARCHIVE) + $(copy-file-to-target) + +$(LOCAL_BUILT_MODULE): $(my_coverage_path)/$(GCNO_ARCHIVE) +endif + +endif # skip_build_from_source diff --git a/make/core/shell_test_config_template.xml b/make/core/shell_test_config_template.xml new file mode 100644 index 0000000..5e1c8ee --- /dev/null +++ b/make/core/shell_test_config_template.xml @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/make/core/soong_android_app_set.mk b/make/core/soong_android_app_set.mk new file mode 100644 index 0000000..ec3d8c8 --- /dev/null +++ b/make/core/soong_android_app_set.mk @@ -0,0 +1,29 @@ +# App prebuilt coming from Soong. +# Extra inputs: +# LOCAL_APK_SET_INSTALL_FILE + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,soong_apk_set.mk may only be used from Soong) +endif + +LOCAL_BUILT_MODULE_STEM := package.apk +LOCAL_INSTALLED_MODULE_STEM := $(notdir $(LOCAL_PREBUILT_MODULE_FILE)) + +# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE +# to avoid checkbuilds making an extra copy of every module. +LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE) + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE))) + +PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) + +PACKAGES := $(PACKAGES) $(LOCAL_MODULE) +# We can't know exactly what apk files would be outputted yet. +# Let extract_apks generate apkcerts.txt and merge it later. +PACKAGES.$(LOCAL_MODULE).APKCERTS_FILE := $(LOCAL_APKCERTS_FILE) + +SOONG_ALREADY_CONV += $(LOCAL_MODULE) diff --git a/make/core/soong_app_prebuilt.mk b/make/core/soong_app_prebuilt.mk new file mode 100644 index 0000000..d771d22 --- /dev/null +++ b/make/core/soong_app_prebuilt.mk @@ -0,0 +1,266 @@ +# App prebuilt coming from Soong. +# Extra inputs: +# LOCAL_SOONG_BUILT_INSTALLED +# LOCAL_SOONG_BUNDLE +# LOCAL_SOONG_CLASSES_JAR +# LOCAL_SOONG_DEX_JAR +# LOCAL_SOONG_HEADER_JAR +# LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR +# LOCAL_SOONG_PROGUARD_DICT +# LOCAL_SOONG_PROGUARD_USAGE_ZIP +# LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE +# LOCAL_SOONG_RRO_DIRS +# LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH) +# LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH) +# LOCAL_SOONG_JNI_LIBS_SYMBOLS +# LOCAL_SOONG_DEXPREOPT_CONFIG + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,soong_app_prebuilt.mk may only be used from Soong) +endif + +LOCAL_MODULE_SUFFIX := .apk +LOCAL_BUILT_MODULE_STEM := package.apk + +intermediates.COMMON := $(call local-intermediates-dir,COMMON) + +full_classes_jar := $(intermediates.COMMON)/classes.jar +full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar +full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar + + +# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE +# to avoid checkbuilds making an extra copy of every module. +LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_CLASSES_JAR) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_HEADER_JAR) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_FULL_MANIFEST_FILE) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEXPREOPT_CONFIG) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEX_JAR) + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +ifdef LOCAL_SOONG_CLASSES_JAR + $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar))) + $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar))) + $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar))) + + ifneq ($(TURBINE_ENABLED),false) + ifdef LOCAL_SOONG_HEADER_JAR + $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar))) + else + $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar))) + endif + endif # TURBINE_ENABLED != false + + javac-check : $(full_classes_jar) + javac-check-$(LOCAL_MODULE) : $(full_classes_jar) + .PHONY: javac-check-$(LOCAL_MODULE) +endif + +ifdef LOCAL_SOONG_DEXPREOPT_CONFIG + my_dexpreopt_config := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config + $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(my_dexpreopt_config))) + $(LOCAL_BUILT_MODULE): $(my_dexpreopt_config) +endif + + + +# Run veridex on product, system_ext and vendor modules. +# We skip it for unbundled app builds where we cannot build veridex. +module_run_appcompat := +ifeq (true,$(non_system_module)) +ifeq (,$(TARGET_BUILD_APPS)) # ! unbundled app build +ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) + module_run_appcompat := true +endif +endif +endif + +ifeq ($(module_run_appcompat),true) + $(LOCAL_BUILT_MODULE): $(appcompat-files) + $(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE) + $(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE) + @echo "Copy: $@" + $(copy-file-to-target) + $(appcompat-header) + $(run-appcompat) +else + $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE))) +endif + +ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR + $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\ + $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar) +endif + +ifdef LOCAL_SOONG_PROGUARD_DICT + my_proguard_dictionary_directory := $(local-proguard-dictionary-directory) + my_proguard_dictionary_mapping_directory := $(local-proguard-dictionary-mapping-directory) + $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_DICT),\ + $(intermediates.COMMON)/proguard_dictionary)) + $(eval $(call copy-r8-dictionary-file-with-mapping,\ + $(LOCAL_SOONG_PROGUARD_DICT),\ + $(my_proguard_dictionary_directory)/proguard_dictionary,\ + $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto)) + $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),\ + $(my_proguard_dictionary_directory)/classes.jar)) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(intermediates.COMMON)/proguard_dictionary) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(my_proguard_dictionary_directory)/proguard_dictionary) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(my_proguard_dictionary_directory)/classes.jar) +endif + +ifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP + $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_USAGE_ZIP),\ + $(call local-packaging-dir,proguard_usage)/proguard_usage.zip)) + $(call add-dependency,$(LOCAL_BUILT_MODULE),\ + $(call local-packaging-dir,proguard_usage)/proguard_usage.zip) +endif + +ifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE +resource_export_package := $(intermediates.COMMON)/package-export.apk +resource_export_stamp := $(intermediates.COMMON)/src/R.stamp + +$(resource_export_package): PRIVATE_STAMP := $(resource_export_stamp) +$(resource_export_package): .KATI_IMPLICIT_OUTPUTS := $(resource_export_stamp) +$(resource_export_package): $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE) + @echo "Copy: $@" + $(copy-file-to-target) + touch $(PRIVATE_STAMP) +$(call add-dependency,$(LOCAL_BUILT_MODULE),$(resource_export_package)) + +endif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE + +java-dex: $(LOCAL_SOONG_DEX_JAR) + + +# Copy test suite files. +ifdef LOCAL_COMPATIBILITY_SUITE +my_apks_to_install := $(foreach f,$(filter %.apk %.idsig,$(LOCAL_SOONG_BUILT_INSTALLED)),$(call word-colon,1,$(f))) +$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ + $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ + $(foreach a,$(my_apks_to_install),\ + $(call compat-copy-pair,$(a),$(dir)/$(notdir $(a))))))) +$(call create-suite-dependencies) +endif + +# install symbol files of JNI libraries +my_jni_lib_symbols_copy_files := $(foreach f,$(LOCAL_SOONG_JNI_LIBS_SYMBOLS),\ + $(call word-colon,1,$(f)):$(patsubst $(PRODUCT_OUT)/%,$(TARGET_OUT_UNSTRIPPED)/%,$(call word-colon,2,$(f)))) +$(LOCAL_BUILT_MODULE): | $(call copy-many-files, $(my_jni_lib_symbols_copy_files)) + +# embedded JNI will already have been handled by soong +my_embed_jni := +my_prebuilt_jni_libs := +ifdef LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH) + my_2nd_arch_prefix := + LOCAL_JNI_SHARED_LIBRARIES := $(LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH)) + include $(BUILD_SYSTEM)/install_jni_libs_internal.mk +endif +ifdef TARGET_2ND_ARCH + ifdef LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH) + my_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) + LOCAL_JNI_SHARED_LIBRARIES := $(LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH)) + include $(BUILD_SYSTEM)/install_jni_libs_internal.mk + endif +endif +LOCAL_SHARED_JNI_LIBRARIES := +my_embed_jni := +my_prebuilt_jni_libs := +my_2nd_arch_prefix := + +PACKAGES := $(PACKAGES) $(LOCAL_MODULE) +ifndef LOCAL_CERTIFICATE + $(call pretty-error,LOCAL_CERTIFICATE must be set for soong_app_prebuilt.mk) +endif +ifeq ($(LOCAL_CERTIFICATE),PRESIGNED) + # The magic string "PRESIGNED" means this package is already checked + # signed with its release key. + # + # By setting .CERTIFICATE but not .PRIVATE_KEY, this package will be + # mentioned in apkcerts.txt (with certificate set to "PRESIGNED") + # but the dexpreopt process will not try to re-sign the app. + PACKAGES.$(LOCAL_MODULE).CERTIFICATE := PRESIGNED +else ifneq ($(LOCAL_CERTIFICATE),) + PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE) + PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(patsubst %.x509.pem,%.pk8,$(LOCAL_CERTIFICATE)) +endif +include $(BUILD_SYSTEM)/app_certificate_validate.mk +PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) + +ifneq ($(LOCAL_MODULE_STEM),) + PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM) +else + PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE) +endif + +# Set a actual_partition_tag (calculated in base_rules.mk) for the package. +PACKAGES.$(LOCAL_MODULE).PARTITION := $(actual_partition_tag) + +ifdef LOCAL_SOONG_BUNDLE + ALL_MODULES.$(my_register_name).BUNDLE := $(LOCAL_SOONG_BUNDLE) +endif + +ifdef LOCAL_SOONG_LINT_REPORTS + ALL_MODULES.$(my_register_name).LINT_REPORTS := $(LOCAL_SOONG_LINT_REPORTS) +endif + +ifndef LOCAL_IS_HOST_MODULE +ifeq ($(LOCAL_SDK_VERSION),system_current) +my_link_type := java:system +else ifneq ($(LOCAL_SDK_VERSION),) +my_link_type := java:sdk +else +my_link_type := java:platform +endif +# warn/allowed types are both empty because Soong modules can't depend on +# make-defined modules. +my_warn_types := +my_allowed_types := + +my_link_deps := +my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) +my_common := COMMON +include $(BUILD_SYSTEM)/link_type.mk +endif # !LOCAL_IS_HOST_MODULE + +ifdef LOCAL_SOONG_DEVICE_RRO_DIRS + $(call append_enforce_rro_sources, \ + $(my_register_name), \ + false, \ + $(LOCAL_FULL_MANIFEST_FILE), \ + $(if $(LOCAL_EXPORT_PACKAGE_RESOURCES),true,false), \ + $(LOCAL_SOONG_DEVICE_RRO_DIRS), \ + vendor \ + ) +endif + +ifdef LOCAL_SOONG_PRODUCT_RRO_DIRS + $(call append_enforce_rro_sources, \ + $(my_register_name), \ + false, \ + $(LOCAL_FULL_MANIFEST_FILE), \ + $(if $(LOCAL_EXPORT_PACKAGE_RESOURCES),true,false), \ + $(LOCAL_SOONG_PRODUCT_RRO_DIRS), \ + product \ + ) +endif + +ifdef LOCAL_PREBUILT_COVERAGE_ARCHIVE + my_coverage_dir := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) + my_coverage_copy_pairs := $(foreach f,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(f):$(my_coverage_dir)/$(notdir $(f))) + my_coverage_files := $(call copy-many-files,$(my_coverage_copy_pairs)) + $(LOCAL_INSTALLED_MODULE): $(my_coverage_files) +endif + +SOONG_ALREADY_CONV += $(LOCAL_MODULE) diff --git a/make/core/soong_cc_rust_prebuilt.mk b/make/core/soong_cc_rust_prebuilt.mk new file mode 100644 index 0000000..07e577a --- /dev/null +++ b/make/core/soong_cc_rust_prebuilt.mk @@ -0,0 +1,274 @@ +# Native prebuilt coming from Soong. +# Extra inputs: +# LOCAL_SOONG_LINK_TYPE +# LOCAL_SOONG_TOC +# LOCAL_SOONG_UNSTRIPPED_BINARY +# LOCAL_SOONG_VNDK_VERSION : means the version of VNDK where this module belongs + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,soong_cc_rust_prebuilt.mk may only be used from Soong) +endif + +ifdef LOCAL_IS_HOST_MODULE + ifneq ($(HOST_OS),$(LOCAL_MODULE_HOST_OS)) + my_prefix := HOST_CROSS_ + LOCAL_HOST_PREFIX := $(my_prefix) + else + my_prefix := HOST_ + LOCAL_HOST_PREFIX := + endif +else + my_prefix := TARGET_ +endif + +ifeq ($($(my_prefix)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH)) + # primary arch + LOCAL_2ND_ARCH_VAR_PREFIX := +else ifeq ($($(my_prefix)2ND_ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH)) + # secondary arch + LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX) +else + $(call pretty-error,Unsupported LOCAL_MODULE_$(my_prefix)ARCH=$(LOCAL_MODULE_$(my_prefix)ARCH)) +endif + +# Don't install static/rlib/proc_macro libraries. +ifndef LOCAL_UNINSTALLABLE_MODULE + ifneq ($(filter STATIC_LIBRARIES RLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS)),) + LOCAL_UNINSTALLABLE_MODULE := true + endif +endif + +# Don't install modules of current VNDK when it is told so +ifeq ($(TARGET_SKIP_CURRENT_VNDK),true) + ifeq ($(LOCAL_SOONG_VNDK_VERSION),$(PLATFORM_VNDK_VERSION)) + LOCAL_UNINSTALLABLE_MODULE := true + endif +endif + + +# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE +# to avoid checkbuilds making an extra copy of every module. +LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE) + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),) + # Soong module is a static or shared library + EXPORTS_LIST += $(intermediates) + EXPORTS.$(intermediates).FLAGS := $(LOCAL_EXPORT_CFLAGS) + EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS) + + ifdef LOCAL_SOONG_TOC + $(eval $(call copy-one-file,$(LOCAL_SOONG_TOC),$(LOCAL_BUILT_MODULE).toc)) + $(call add-dependency,$(LOCAL_BUILT_MODULE).toc,$(LOCAL_BUILT_MODULE)) + $(my_all_targets): $(LOCAL_BUILT_MODULE).toc + endif + + SOONG_ALREADY_CONV += $(LOCAL_MODULE) + + my_link_type := $(LOCAL_SOONG_LINK_TYPE) + my_warn_types := + my_allowed_types := + my_link_deps := + my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) + my_common := + include $(BUILD_SYSTEM)/link_type.mk +endif + +ifdef LOCAL_USE_VNDK + ifneq ($(LOCAL_VNDK_DEPEND_ON_CORE_VARIANT),true) + name_without_suffix := $(patsubst %.vendor,%,$(LOCAL_MODULE)) + ifneq ($(name_without_suffix),$(LOCAL_MODULE)) + SPLIT_VENDOR.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1 + else + name_without_suffix := $(patsubst %.product,%,$(LOCAL_MODULE)) + ifneq ($(name_without_suffix),$(LOCAL_MODULE)) + SPLIT_PRODUCT.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1 + endif + endif + name_without_suffix := + endif +endif + +# Check prebuilt ELF binaries. +ifdef LOCAL_INSTALLED_MODULE + ifneq ($(LOCAL_CHECK_ELF_FILES),) + my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE) + my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) + include $(BUILD_SYSTEM)/check_elf_file.mk + endif +endif + +# The real dependency will be added after all Android.mks are loaded and the install paths +# of the shared libraries are determined. +ifdef LOCAL_INSTALLED_MODULE + ifdef LOCAL_SHARED_LIBRARIES + my_shared_libraries := $(LOCAL_SHARED_LIBRARIES) + ifdef LOCAL_USE_VNDK + my_shared_libraries := $(foreach l,$(my_shared_libraries),\ + $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) + endif + $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ + $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries)) + endif + ifdef LOCAL_DYLIB_LIBRARIES + my_dylibs := $(LOCAL_DYLIB_LIBRARIES) + # Treat these as shared library dependencies for installation purposes. + ifdef LOCAL_USE_VNDK + my_dylibs := $(foreach l,$(my_dylibs),\ + $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) + endif + $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ + $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_dylibs)) + endif +endif + +my_check_same_vndk_variants := +ifeq ($(LOCAL_CHECK_SAME_VNDK_VARIANTS),true) + ifeq ($(filter hwaddress address, $(SANITIZE_TARGET)),) + ifneq ($(CLANG_COVERAGE),true) + # Do not compare VNDK variant for special cases e.g. coverage builds. + ifneq ($(SKIP_VNDK_VARIANTS_CHECK),true) + my_check_same_vndk_variants := true + endif + endif + endif +endif + +ifeq ($(my_check_same_vndk_variants),true) + same_vndk_variants_stamp := $(intermediates)/same_vndk_variants.timestamp + + my_core_register_name := $(subst .vendor,,$(subst .product,,$(my_register_name))) + my_core_variant_files := $(call module-target-built-files,$(my_core_register_name)) + my_core_shared_lib := $(sort $(filter %.so,$(my_core_variant_files))) + + $(same_vndk_variants_stamp): PRIVATE_CORE_VARIANT := $(my_core_shared_lib) + $(same_vndk_variants_stamp): PRIVATE_VENDOR_VARIANT := $(LOCAL_PREBUILT_MODULE_FILE) + $(same_vndk_variants_stamp): PRIVATE_TOOLS_PREFIX := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)TOOLS_PREFIX) + + $(same_vndk_variants_stamp): $(my_core_shared_lib) $(LOCAL_PREBUILT_MODULE_FILE) + $(call verify-vndk-libs-identical,\ + $(PRIVATE_CORE_VARIANT),\ + $(PRIVATE_VENDOR_VARIANT),\ + $(PRIVATE_TOOLS_PREFIX)) + touch $@ + + $(LOCAL_BUILT_MODULE): $(same_vndk_variants_stamp) +endif + +# Use copy-or-link-prebuilt-to-target for host executables and shared libraries, +# to preserve symlinks to the source trees. They can then run directly from the +# prebuilt directories where the linker can load their dependencies using +# relative RUNPATHs. +$(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE) +ifeq ($(LOCAL_IS_HOST_MODULE) $(if $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),true,),true true) + $(copy-or-link-prebuilt-to-target) + ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + [ -x $@ ] || ( $(call echo-error,$@,Target of symlink is not executable); false ) + endif +else + $(transform-prebuilt-to-target) + ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) + $(hide) chmod +x $@ + endif +endif + +ifndef LOCAL_IS_HOST_MODULE + ifdef LOCAL_SOONG_UNSTRIPPED_BINARY + ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true) + my_symbol_path := $(if $(LOCAL_SOONG_SYMBOL_PATH),$(LOCAL_SOONG_SYMBOL_PATH),$(my_module_path)) + # Store a copy with symbols for symbolic debugging + my_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path)) + # drop /root as /root is mounted as / + my_unstripped_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/root/%,$(TARGET_OUT_UNSTRIPPED)/%, $(my_unstripped_path)) + symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem) + $(eval $(call copy-unstripped-elf-file-with-mapping,$(LOCAL_SOONG_UNSTRIPPED_BINARY),$(symbolic_output))) + $(LOCAL_BUILT_MODULE): | $(symbolic_output) + + ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) + my_breakpad_path := $(TARGET_OUT_BREAKPAD)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path)) + breakpad_output := $(my_breakpad_path)/$(my_installed_module_stem).sym + $(breakpad_output) : $(LOCAL_SOONG_UNSTRIPPED_BINARY) | $(BREAKPAD_DUMP_SYMS) $(PRIVATE_READELF) + @echo "target breakpad: $(PRIVATE_MODULE) ($@)" + @mkdir -p $(dir $@) + $(hide) if $(PRIVATE_READELF) -S $< > /dev/null 2>&1 ; then \ + $(BREAKPAD_DUMP_SYMS) -c $< > $@ ; \ + else \ + echo "skipped for non-elf file."; \ + touch $@; \ + fi + $(call add-dependency,$(LOCAL_BUILT_MODULE),$(breakpad_output)) + endif + endif + endif +endif + +ifeq ($(NATIVE_COVERAGE),true) + ifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE))) + $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).zip)) + ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true) + ifdef LOCAL_IS_HOST_MODULE + my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) + else + my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) + endif + my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).zip + $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path))) + $(LOCAL_BUILT_MODULE): $(my_coverage_path) + endif + else + # Coverage information is needed when static lib is a dependency of another + # coverage-enabled module. + ifeq (STATIC_LIBRARIES, $(LOCAL_MODULE_CLASS)) + GCNO_ARCHIVE := $(LOCAL_MODULE).zip + $(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) + $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := + $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := + $(intermediates)/$(GCNO_ARCHIVE) : + $(package-coverage-files) + endif + endif +endif + +# A product may be configured to strip everything in some build variants. +# We do the stripping as a post-install command so that LOCAL_BUILT_MODULE +# is still with the symbols and we don't need to clean it (and relink) when +# you switch build variant. +ifneq ($(filter $(STRIP_EVERYTHING_BUILD_VARIANTS),$(TARGET_BUILD_VARIANT)),) +$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_STRIP) --strip-all $(LOCAL_INSTALLED_MODULE) +endif + +$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) + +# We don't care about installed rlib/static libraries, since the libraries have +# already been linked into the module at that point. We do, however, care +# about the NOTICE files for any rlib/static libraries that we use. +# (see notice_files.mk) +# +# Filter out some NDK libraries that are not being exported. +my_static_libraries := \ + $(filter-out ndk_libc++_static ndk_libc++abi ndk_libandroid_support ndk_libunwind \ + ndk_libc++_static.native_bridge ndk_libc++abi.native_bridge \ + ndk_libandroid_support.native_bridge ndk_libunwind.native_bridge, \ + $(LOCAL_STATIC_LIBRARIES)) +installed_static_library_notice_file_targets := \ + $(foreach lib,$(my_static_libraries) $(LOCAL_WHOLE_STATIC_LIBRARIES), \ + NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-STATIC_LIBRARIES-$(lib)) +installed_static_library_notice_file_targets += \ + $(foreach lib,$(LOCAL_RLIB_LIBRARIES), \ + NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-RLIB_LIBRARIES-$(lib)) +installed_static_library_notice_file_targets += \ + $(foreach lib,$(LOCAL_PROC_MACRO_LIBRARIES), \ + NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST$(if $(my_host_cross),_CROSS,),TARGET)-PROC_MACRO_LIBRARIES-$(lib)) + +$(notice_target): | $(installed_static_library_notice_file_targets) +$(LOCAL_INSTALLED_MODULE): | $(notice_target) + +# Reinstall shared library dependencies of fuzz targets to /data/fuzz/ (for +# target) or /data/ (for host). +ifdef LOCAL_IS_FUZZ_TARGET +$(LOCAL_INSTALLED_MODULE): $(LOCAL_FUZZ_INSTALLED_SHARED_DEPS) +endif diff --git a/make/core/soong_config.mk b/make/core/soong_config.mk new file mode 100644 index 0000000..ca87556 --- /dev/null +++ b/make/core/soong_config.mk @@ -0,0 +1,314 @@ +SOONG_MAKEVARS_MK := $(SOONG_OUT_DIR)/make_vars-$(TARGET_PRODUCT).mk +SOONG_VARIABLES := $(SOONG_OUT_DIR)/soong.variables +SOONG_ANDROID_MK := $(SOONG_OUT_DIR)/Android-$(TARGET_PRODUCT).mk +CAMERA_WITHOUT_GRALLOC4 ?= false +BINDER32BIT := +ifneq ($(TARGET_USES_64_BIT_BINDER),true) +ifneq ($(TARGET_IS_64_BIT),true) +BINDER32BIT := true +endif +endif + +include $(BUILD_SYSTEM)/dex_preopt_config.mk + +ifeq ($(WRITE_SOONG_VARIABLES),true) + +# Create soong.variables with copies of makefile settings. Runs every build, +# but only updates soong.variables if it changes +$(shell mkdir -p $(dir $(SOONG_VARIABLES))) +$(call json_start) + +$(call add_json_str, Make_suffix, -$(TARGET_PRODUCT)) + +$(call add_json_str, BuildId, $(BUILD_ID)) +$(call add_json_str, BuildNumberFile, build_number.txt) + +$(call add_json_str, Platform_version_name, $(PLATFORM_VERSION)) +$(call add_json_val, Platform_sdk_version, $(PLATFORM_SDK_VERSION)) +$(call add_json_str, Platform_sdk_codename, $(PLATFORM_VERSION_CODENAME)) +$(call add_json_bool, Platform_sdk_final, $(filter REL,$(PLATFORM_VERSION_CODENAME))) +$(call add_json_val, Platform_sdk_extension_version, $(PLATFORM_SDK_EXTENSION_VERSION)) +$(call add_json_val, Platform_base_sdk_extension_version, $(PLATFORM_BASE_SDK_EXTENSION_VERSION)) +$(call add_json_csv, Platform_version_active_codenames, $(PLATFORM_VERSION_ALL_CODENAMES)) +$(call add_json_str, Platform_security_patch, $(PLATFORM_SECURITY_PATCH)) +$(call add_json_str, Platform_preview_sdk_version, $(PLATFORM_PREVIEW_SDK_VERSION)) +$(call add_json_str, Platform_base_os, $(PLATFORM_BASE_OS)) +$(call add_json_str, Platform_version_last_stable, $(PLATFORM_VERSION_LAST_STABLE)) + +$(call add_json_str, Platform_min_supported_target_sdk_version, $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION)) + +$(call add_json_bool, Allow_missing_dependencies, $(filter true,$(ALLOW_MISSING_DEPENDENCIES))) +$(call add_json_bool, Unbundled_build, $(TARGET_BUILD_UNBUNDLED)) +$(call add_json_list, Unbundled_build_apps, $(TARGET_BUILD_APPS)) +$(call add_json_bool, Unbundled_build_image, $(TARGET_BUILD_UNBUNDLED_IMAGE)) +$(call add_json_bool, Always_use_prebuilt_sdks, $(TARGET_BUILD_USE_PREBUILT_SDKS)) + +$(call add_json_bool, Debuggable, $(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +$(call add_json_bool, Eng, $(filter eng,$(TARGET_BUILD_VARIANT))) + +$(call add_json_str, DeviceName, $(TARGET_DEVICE)) +$(call add_json_str, DeviceProduct, $(TARGET_PRODUCT)) +$(call add_json_str, DeviceArch, $(TARGET_ARCH)) +$(call add_json_str, DeviceArchVariant, $(TARGET_ARCH_VARIANT)) +$(call add_json_str, DeviceCpuVariant, $(TARGET_CPU_VARIANT)) +$(call add_json_list, DeviceAbi, $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2)) + +$(call add_json_str, DeviceSecondaryArch, $(TARGET_2ND_ARCH)) +$(call add_json_str, DeviceSecondaryArchVariant, $(TARGET_2ND_ARCH_VARIANT)) +$(call add_json_str, DeviceSecondaryCpuVariant, $(TARGET_2ND_CPU_VARIANT)) +$(call add_json_list, DeviceSecondaryAbi, $(TARGET_2ND_CPU_ABI) $(TARGET_2ND_CPU_ABI2)) + +$(call add_json_bool, Aml_abis, $(if $(filter mainline_sdk,$(TARGET_ARCH_SUITE)),true)) +$(call add_json_bool, Ndk_abis, $(if $(filter ndk, $(TARGET_ARCH_SUITE)),true)) + +$(call add_json_str, NativeBridgeArch, $(TARGET_NATIVE_BRIDGE_ARCH)) +$(call add_json_str, NativeBridgeArchVariant, $(TARGET_NATIVE_BRIDGE_ARCH_VARIANT)) +$(call add_json_str, NativeBridgeCpuVariant, $(TARGET_NATIVE_BRIDGE_CPU_VARIANT)) +$(call add_json_list, NativeBridgeAbi, $(TARGET_NATIVE_BRIDGE_ABI)) +$(call add_json_str, NativeBridgeRelativePath, $(TARGET_NATIVE_BRIDGE_RELATIVE_PATH)) + +$(call add_json_str, NativeBridgeSecondaryArch, $(TARGET_NATIVE_BRIDGE_2ND_ARCH)) +$(call add_json_str, NativeBridgeSecondaryArchVariant, $(TARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT)) +$(call add_json_str, NativeBridgeSecondaryCpuVariant, $(TARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT)) +$(call add_json_list, NativeBridgeSecondaryAbi, $(TARGET_NATIVE_BRIDGE_2ND_ABI)) +$(call add_json_str, NativeBridgeSecondaryRelativePath, $(TARGET_NATIVE_BRIDGE_2ND_RELATIVE_PATH)) + +$(call add_json_str, HostArch, $(HOST_ARCH)) +$(call add_json_str, HostSecondaryArch, $(HOST_2ND_ARCH)) +$(call add_json_bool, HostStaticBinaries, $(BUILD_HOST_static)) +$(call add_json_bool, HostMusl, $(USE_HOST_MUSL)) + +$(call add_json_str, CrossHost, $(HOST_CROSS_OS)) +$(call add_json_str, CrossHostArch, $(HOST_CROSS_ARCH)) +$(call add_json_str, CrossHostSecondaryArch, $(HOST_CROSS_2ND_ARCH)) + +$(call add_json_list, DeviceResourceOverlays, $(DEVICE_PACKAGE_OVERLAYS)) +$(call add_json_list, ProductResourceOverlays, $(PRODUCT_PACKAGE_OVERLAYS)) +$(call add_json_list, EnforceRROTargets, $(PRODUCT_ENFORCE_RRO_TARGETS)) +$(call add_json_list, EnforceRROExcludedOverlays, $(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)) + +$(call add_json_str, AAPTCharacteristics, $(TARGET_AAPT_CHARACTERISTICS)) +$(call add_json_list, AAPTConfig, $(PRODUCT_AAPT_CONFIG)) +$(call add_json_str, AAPTPreferredConfig, $(PRODUCT_AAPT_PREF_CONFIG)) +$(call add_json_list, AAPTPrebuiltDPI, $(PRODUCT_AAPT_PREBUILT_DPI)) + +$(call add_json_str, DefaultAppCertificate, $(PRODUCT_DEFAULT_DEV_CERTIFICATE)) + +$(call add_json_str, AppsDefaultVersionName, $(APPS_DEFAULT_VERSION_NAME)) + +$(call add_json_list, SanitizeHost, $(SANITIZE_HOST)) +$(call add_json_list, SanitizeDevice, $(SANITIZE_TARGET)) +$(call add_json_list, SanitizeDeviceDiag, $(SANITIZE_TARGET_DIAG)) +$(call add_json_list, SanitizeDeviceArch, $(SANITIZE_TARGET_ARCH)) + +$(call add_json_bool, Safestack, $(filter true,$(USE_SAFESTACK))) +$(call add_json_bool, EnableCFI, $(call invert_bool,$(filter false,$(ENABLE_CFI)))) +$(call add_json_list, CFIExcludePaths, $(CFI_EXCLUDE_PATHS) $(PRODUCT_CFI_EXCLUDE_PATHS)) +$(call add_json_list, CFIIncludePaths, $(CFI_INCLUDE_PATHS) $(PRODUCT_CFI_INCLUDE_PATHS)) +$(call add_json_list, IntegerOverflowExcludePaths, $(INTEGER_OVERFLOW_EXCLUDE_PATHS) $(PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS)) + +$(call add_json_list, MemtagHeapExcludePaths, $(MEMTAG_HEAP_EXCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS)) +$(call add_json_list, MemtagHeapAsyncIncludePaths, $(MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS)) +$(call add_json_list, MemtagHeapSyncIncludePaths, $(MEMTAG_HEAP_SYNC_INCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS)) + +$(call add_json_bool, DisableScudo, $(filter true,$(PRODUCT_DISABLE_SCUDO))) + +$(call add_json_bool, ClangTidy, $(filter 1 true,$(WITH_TIDY))) +$(call add_json_str, TidyChecks, $(WITH_TIDY_CHECKS)) + +$(call add_json_list, JavaCoveragePaths, $(JAVA_COVERAGE_PATHS)) +$(call add_json_list, JavaCoverageExcludePaths, $(JAVA_COVERAGE_EXCLUDE_PATHS)) + +$(call add_json_bool, GcovCoverage, $(filter true,$(NATIVE_COVERAGE))) +$(call add_json_bool, ClangCoverage, $(filter true,$(CLANG_COVERAGE))) +$(call add_json_bool, ClangCoverageContinuousMode, $(filter true,$(CLANG_COVERAGE_CONTINUOUS_MODE))) +$(call add_json_list, NativeCoveragePaths, $(NATIVE_COVERAGE_PATHS)) +$(call add_json_list, NativeCoverageExcludePaths, $(NATIVE_COVERAGE_EXCLUDE_PATHS)) + +$(call add_json_bool, SamplingPGO, $(filter true,$(SAMPLING_PGO))) + +$(call add_json_bool, ArtUseReadBarrier, $(call invert_bool,$(filter false,$(PRODUCT_ART_USE_READ_BARRIER)))) +$(call add_json_bool, Binder32bit, $(BINDER32BIT)) +$(call add_json_str, BtConfigIncludeDir, $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR)) +$(call add_json_list, DeviceKernelHeaders, $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS)) +$(call add_json_str, DeviceVndkVersion, $(BOARD_VNDK_VERSION)) +$(call add_json_str, Platform_vndk_version, $(PLATFORM_VNDK_VERSION)) +$(call add_json_str, ProductVndkVersion, $(PRODUCT_PRODUCT_VNDK_VERSION)) +$(call add_json_list, ExtraVndkVersions, $(PRODUCT_EXTRA_VNDK_VERSIONS)) +$(call add_json_list, DeviceSystemSdkVersions, $(BOARD_SYSTEMSDK_VERSIONS)) +$(call add_json_str, RecoverySnapshotVersion, $(RECOVERY_SNAPSHOT_VERSION)) +$(call add_json_list, Platform_systemsdk_versions, $(PLATFORM_SYSTEMSDK_VERSIONS)) +$(call add_json_bool, Malloc_not_svelte, $(call invert_bool,$(filter true,$(MALLOC_SVELTE)))) +$(call add_json_bool, Malloc_zero_contents, $(call invert_bool,$(filter false,$(MALLOC_ZERO_CONTENTS)))) +$(call add_json_bool, Malloc_pattern_fill_contents, $(MALLOC_PATTERN_FILL_CONTENTS)) +$(call add_json_str, Override_rs_driver, $(OVERRIDE_RS_DRIVER)) + +$(call add_json_bool, UncompressPrivAppDex, $(call invert_bool,$(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS)))) +$(call add_json_list, ModulesLoadedByPrivilegedModules, $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES)) + +$(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS)) +$(call add_json_list, ApexBootJars, $(PRODUCT_APEX_BOOT_JARS)) + +$(call add_json_bool, VndkUseCoreVariant, $(TARGET_VNDK_USE_CORE_VARIANT)) +$(call add_json_bool, VndkSnapshotBuildArtifacts, $(VNDK_SNAPSHOT_BUILD_ARTIFACTS)) + +$(call add_json_bool, DirectedVendorSnapshot, $(DIRECTED_VENDOR_SNAPSHOT)) +$(call add_json_map, VendorSnapshotModules) +$(foreach module,$(VENDOR_SNAPSHOT_MODULES),\ + $(call add_json_bool,$(module),true)) +$(call end_json_map) + +$(call add_json_bool, DirectedRecoverySnapshot, $(DIRECTED_RECOVERY_SNAPSHOT)) +$(call add_json_map, RecoverySnapshotModules) +$(foreach module,$(RECOVERY_SNAPSHOT_MODULES),\ + $(call add_json_bool,$(module),true)) +$(call end_json_map) + +$(call add_json_list, VendorSnapshotDirsIncluded, $(VENDOR_SNAPSHOT_DIRS_INCLUDED)) +$(call add_json_list, VendorSnapshotDirsExcluded, $(VENDOR_SNAPSHOT_DIRS_EXCLUDED)) +$(call add_json_list, RecoverySnapshotDirsIncluded, $(RECOVERY_SNAPSHOT_DIRS_INCLUDED)) +$(call add_json_list, RecoverySnapshotDirsExcluded, $(RECOVERY_SNAPSHOT_DIRS_EXCLUDED)) +$(call add_json_bool, HostFakeSnapshotEnabled, $(HOST_FAKE_SNAPSHOT_ENABLE)) + +$(call add_json_bool, Treble_linker_namespaces, $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES))) +$(call add_json_bool, Enforce_vintf_manifest, $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST))) + +$(call add_json_bool, Uml, $(filter true,$(TARGET_USER_MODE_LINUX))) +$(call add_json_str, VendorPath, $(TARGET_COPY_OUT_VENDOR)) +$(call add_json_str, OdmPath, $(TARGET_COPY_OUT_ODM)) +$(call add_json_str, VendorDlkmPath, $(TARGET_COPY_OUT_VENDOR_DLKM)) +$(call add_json_str, OdmDlkmPath, $(TARGET_COPY_OUT_ODM_DLKM)) +$(call add_json_str, SystemDlkmPath, $(TARGET_COPY_OUT_SYSTEM_DLKM)) +$(call add_json_str, ProductPath, $(TARGET_COPY_OUT_PRODUCT)) +$(call add_json_str, SystemExtPath, $(TARGET_COPY_OUT_SYSTEM_EXT)) +$(call add_json_bool, MinimizeJavaDebugInfo, $(filter true,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO))) + +$(call add_json_bool, UseGoma, $(filter-out false,$(USE_GOMA))) +$(call add_json_bool, UseRBE, $(filter-out false,$(USE_RBE))) +$(call add_json_bool, UseRBEJAVAC, $(filter-out false,$(RBE_JAVAC))) +$(call add_json_bool, UseRBER8, $(filter-out false,$(RBE_R8))) +$(call add_json_bool, UseRBED8, $(filter-out false,$(RBE_D8))) +$(call add_json_bool, Arc, $(filter true,$(TARGET_ARC))) + +$(call add_json_list, NamespacesToExport, $(PRODUCT_SOONG_NAMESPACES)) + +$(call add_json_list, PgoAdditionalProfileDirs, $(PGO_ADDITIONAL_PROFILE_DIRS)) + +$(call add_json_list, BoardPlatVendorPolicy, $(BOARD_PLAT_VENDOR_POLICY)) +$(call add_json_list, BoardReqdMaskPolicy, $(BOARD_REQD_MASK_POLICY)) +$(call add_json_list, BoardSystemExtPublicPrebuiltDirs, $(BOARD_SYSTEM_EXT_PUBLIC_PREBUILT_DIRS)) +$(call add_json_list, BoardSystemExtPrivatePrebuiltDirs, $(BOARD_SYSTEM_EXT_PRIVATE_PREBUILT_DIRS)) +$(call add_json_list, BoardProductPublicPrebuiltDirs, $(BOARD_PRODUCT_PUBLIC_PREBUILT_DIRS)) +$(call add_json_list, BoardProductPrivatePrebuiltDirs, $(BOARD_PRODUCT_PRIVATE_PREBUILT_DIRS)) +$(call add_json_list, BoardVendorSepolicyDirs, $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS)) +$(call add_json_list, BoardOdmSepolicyDirs, $(BOARD_ODM_SEPOLICY_DIRS)) +$(call add_json_list, BoardVendorDlkmSepolicyDirs, $(BOARD_VENDOR_DLKM_SEPOLICY_DIRS)) +$(call add_json_list, BoardOdmDlkmSepolicyDirs, $(BOARD_ODM_DLKM_SEPOLICY_DIRS)) +$(call add_json_list, BoardSystemDlkmSepolicyDirs, $(BOARD_SYSTEM_DLKM_SEPOLICY_DIRS)) +# TODO: BOARD_PLAT_* dirs only kept for compatibility reasons. Will be a hard error on API level 31 +$(call add_json_list, SystemExtPublicSepolicyDirs, $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS) $(BOARD_PLAT_PUBLIC_SEPOLICY_DIR)) +$(call add_json_list, SystemExtPrivateSepolicyDirs, $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS) $(BOARD_PLAT_PRIVATE_SEPOLICY_DIR)) +$(call add_json_list, BoardSepolicyM4Defs, $(BOARD_SEPOLICY_M4DEFS)) +$(call add_json_str, BoardSepolicyVers, $(BOARD_SEPOLICY_VERS)) +$(call add_json_str, SystemExtSepolicyPrebuiltApiDir, $(BOARD_SYSTEM_EXT_PREBUILT_DIR)) +$(call add_json_str, ProductSepolicyPrebuiltApiDir, $(BOARD_PRODUCT_PREBUILT_DIR)) + +$(call add_json_str, PlatformSepolicyVersion, $(PLATFORM_SEPOLICY_VERSION)) +$(call add_json_str, TotSepolicyVersion, $(TOT_SEPOLICY_VERSION)) +$(call add_json_list, PlatformSepolicyCompatVersions, $(PLATFORM_SEPOLICY_COMPAT_VERSIONS)) + +$(call add_json_bool, Flatten_apex, $(filter true,$(TARGET_FLATTEN_APEX))) +$(call add_json_bool, ForceApexSymlinkOptimization, $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION))) + +$(call add_json_str, DexpreoptGlobalConfig, $(DEX_PREOPT_CONFIG)) + +$(call add_json_bool, WithDexpreopt, $(filter true,$(WITH_DEXPREOPT))) + +$(call add_json_bool, CameraSupportHDMI, $(filter true,$(CAMERA_SUPPORT_HDMI))) +$(call add_json_bool, CameraSupportHDMISubVideo, $(filter true,$(CAMERA_SUPPORT_HDMI_SUBVIDEO))) +$(call add_json_bool, CameraSupportOSD, $(filter true,$(CAMERA_SUPPORT_OSD))) +$(call add_json_bool, CameraSupportSubDevice, $(filter true,$(CAMERA_SUPPORT_SUBDEVICE))) +$(call add_json_bool, CameraWithGralloc4, $(filter-out true,$(CAMERA_WITHOUT_GRALLOC4))) +$(call add_json_bool, CameraWithOutGralloc4, $(filter true,$(CAMERA_WITHOUT_GRALLOC4))) + +$(call add_json_list, ManifestPackageNameOverrides, $(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES)) +$(call add_json_list, PackageNameOverrides, $(PRODUCT_PACKAGE_NAME_OVERRIDES)) +$(call add_json_list, CertificateOverrides, $(PRODUCT_CERTIFICATE_OVERRIDES)) + +$(call add_json_str, ApexGlobalMinSdkVersionOverride, $(APEX_GLOBAL_MIN_SDK_VERSION_OVERRIDE)) + +$(call add_json_bool, EnforceSystemCertificate, $(filter true,$(ENFORCE_SYSTEM_CERTIFICATE))) +$(call add_json_list, EnforceSystemCertificateAllowList, $(ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST)) + +$(call add_json_list, ProductHiddenAPIStubs, $(PRODUCT_HIDDENAPI_STUBS)) +$(call add_json_list, ProductHiddenAPIStubsSystem, $(PRODUCT_HIDDENAPI_STUBS_SYSTEM)) +$(call add_json_list, ProductHiddenAPIStubsTest, $(PRODUCT_HIDDENAPI_STUBS_TEST)) + +$(call add_json_list, ProductPublicSepolicyDirs, $(PRODUCT_PUBLIC_SEPOLICY_DIRS)) +$(call add_json_list, ProductPrivateSepolicyDirs, $(PRODUCT_PRIVATE_SEPOLICY_DIRS)) + +$(call add_json_list, TargetFSConfigGen, $(TARGET_FS_CONFIG_GEN)) + +$(call add_json_list, MissingUsesLibraries, $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)) + +$(call add_json_map, VendorVars) +$(foreach namespace,$(SOONG_CONFIG_NAMESPACES),\ + $(call add_json_map, $(namespace))\ + $(foreach key,$(SOONG_CONFIG_$(namespace)),\ + $(call add_json_str,$(key),$(SOONG_CONFIG_$(namespace)_$(key))))\ + $(call end_json_map)) +$(call end_json_map) + +$(call add_json_bool, EnforceProductPartitionInterface, $(filter true,$(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE))) +$(call add_json_str, DeviceCurrentApiLevelForVendorModules, $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)) + +$(call add_json_bool, EnforceInterPartitionJavaSdkLibrary, $(filter true,$(PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY))) +$(call add_json_list, InterPartitionJavaLibraryAllowList, $(PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST)) + +$(call add_json_bool, InstallExtraFlattenedApexes, $(PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES)) + +$(call add_json_bool, CompressedApex, $(filter true,$(PRODUCT_COMPRESSED_APEX))) + +$(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))) + +$(call add_json_list, BoardKernelBinaries, $(BOARD_KERNEL_BINARIES)) +$(call add_json_list, BoardKernelModuleInterfaceVersions, $(BOARD_KERNEL_MODULE_INTERFACE_VERSIONS)) + +$(call add_json_bool, BoardMoveRecoveryResourcesToVendorBoot, $(filter true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))) +$(call add_json_str, PrebuiltHiddenApiDir, $(BOARD_PREBUILT_HIDDENAPI_DIR)) + +$(call add_json_str, ShippingApiLevel, $(PRODUCT_SHIPPING_API_LEVEL)) + +$(call add_json_bool, BuildBrokenEnforceSyspropOwner, $(filter true,$(BUILD_BROKEN_ENFORCE_SYSPROP_OWNER))) +$(call add_json_bool, BuildBrokenTrebleSyspropNeverallow, $(filter true,$(BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW))) +$(call add_json_bool, BuildBrokenVendorPropertyNamespace, $(filter true,$(BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE))) +$(call add_json_list, BuildBrokenInputDirModules, $(BUILD_BROKEN_INPUT_DIR_MODULES)) + +$(call add_json_bool, BuildDebugfsRestrictionsEnabled, $(filter true,$(PRODUCT_SET_DEBUGFS_RESTRICTIONS))) + +$(call add_json_bool, RequiresInsecureExecmemForSwiftshader, $(filter true,$(PRODUCT_REQUIRES_INSECURE_EXECMEM_FOR_SWIFTSHADER))) + +$(call add_json_bool, SelinuxIgnoreNeverallows, $(filter true,$(SELINUX_IGNORE_NEVERALLOWS))) + +$(call add_json_bool, SepolicySplit, $(filter true,$(PRODUCT_SEPOLICY_SPLIT))) + +$(call add_json_list, SepolicyFreezeTestExtraDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_DIRS)) +$(call add_json_list, SepolicyFreezeTestExtraPrebuiltDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS)) + +$(call add_json_bool, GenerateAidlNdkPlatformBackend, $(filter true,$(NEED_AIDL_NDK_PLATFORM_BACKEND))) + +$(call add_json_bool, ForceMultilibFirstOnDevice, $(filter true,$(FORCE_MULTILIB_FIRST_ON_DEVICE))) + +$(call add_json_list, IncludeTags, $(PRODUCT_INCLUDE_TAGS)) + +$(call json_end) + +$(file >$(SOONG_VARIABLES).tmp,$(json_contents)) + +$(shell if ! cmp -s $(SOONG_VARIABLES).tmp $(SOONG_VARIABLES); then \ + mv $(SOONG_VARIABLES).tmp $(SOONG_VARIABLES); \ + else \ + rm $(SOONG_VARIABLES).tmp; \ + fi) + +endif # CONFIGURE_SOONG diff --git a/make/core/soong_droiddoc_prebuilt.mk b/make/core/soong_droiddoc_prebuilt.mk new file mode 100644 index 0000000..ba597c5 --- /dev/null +++ b/make/core/soong_droiddoc_prebuilt.mk @@ -0,0 +1,39 @@ +# Droiddoc prebuilt coming from Soong. + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,soong_droiddoc_prebuilt.mk may only be used from Soong) +endif + +ifdef LOCAL_DROIDDOC_STUBS_SRCJAR +$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_STUBS_SRCJAR),$(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar)) +$(eval ALL_TARGETS.$(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) +ALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar + +.PHONY: $(LOCAL_MODULE) +$(LOCAL_MODULE) : $(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar +endif + +ifdef LOCAL_DROIDDOC_DOC_ZIP +$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_DOC_ZIP),$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip)) +$(eval ALL_TARGETS.$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) +$(call dist-for-goals,docs,$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip) + +.PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip +$(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip : $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip +ALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip +endif + +ifdef LOCAL_DROIDDOC_ANNOTATIONS_ZIP +$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_ANNOTATIONS_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip)) +$(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) +endif + +ifdef LOCAL_DROIDDOC_API_VERSIONS_XML +$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_API_VERSIONS_XML),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_generated-api-versions.xml)) +$(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_generated-api-versions.xml.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) +endif + +ifdef LOCAL_DROIDDOC_METADATA_ZIP +$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_METADATA_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)-metadata.zip)) +$(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)-metadata.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) +endif diff --git a/make/core/soong_java_prebuilt.mk b/make/core/soong_java_prebuilt.mk new file mode 100644 index 0000000..a8f475f --- /dev/null +++ b/make/core/soong_java_prebuilt.mk @@ -0,0 +1,217 @@ +# Java prebuilt coming from Soong. +# Extra inputs: +# LOCAL_SOONG_BUILT_INSTALLED +# LOCAL_SOONG_CLASSES_JAR +# LOCAL_SOONG_HEADER_JAR +# LOCAL_SOONG_DEX_JAR +# LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR +# LOCAL_SOONG_DEXPREOPT_CONFIG + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,soong_java_prebuilt.mk may only be used from Soong) +endif + +LOCAL_MODULE_SUFFIX := .jar +LOCAL_BUILT_MODULE_STEM := javalib.jar + +intermediates.COMMON := $(call local-intermediates-dir,COMMON) + +full_classes_jar := $(intermediates.COMMON)/classes.jar +full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar +full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar +common_javalib.jar := $(intermediates.COMMON)/javalib.jar + +ifdef LOCAL_SOONG_AAR + LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_AAR) +endif + +# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE +# to avoid checkbuilds making an extra copy of every module. +LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_HEADER_JAR) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_FULL_MANIFEST_FILE) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEXPREOPT_CONFIG) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE) +LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEX_JAR) + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +ifdef LOCAL_SOONG_CLASSES_JAR + $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar))) + $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar))) + $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar))) + + ifneq ($(TURBINE_ENABLED),false) + ifdef LOCAL_SOONG_HEADER_JAR + $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar))) + else + $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar))) + endif + endif # TURBINE_ENABLED != false +endif + +$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE))) + +ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR + $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\ + $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)) + $(call add-dependency,$(common_javalib.jar),\ + $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar) +endif + +ifdef LOCAL_SOONG_PROGUARD_DICT + my_proguard_dictionary_directory := $(local-proguard-dictionary-directory) + my_proguard_dictionary_mapping_directory := $(local-proguard-dictionary-mapping-directory) + $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_DICT),\ + $(intermediates.COMMON)/proguard_dictionary)) + $(eval $(call copy-r8-dictionary-file-with-mapping,\ + $(LOCAL_SOONG_PROGUARD_DICT),\ + $(my_proguard_dictionary_directory)/proguard_dictionary,\ + $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto)) + $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),\ + $(my_proguard_dictionary_directory)/classes.jar)) + $(call add-dependency,$(common_javalib.jar),\ + $(intermediates.COMMON)/proguard_dictionary) + $(call add-dependency,$(common_javalib.jar),\ + $(my_proguard_dictionary_directory)/proguard_dictionary) + $(call add-dependency,$(common_javalib.jar),\ + $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto) + $(call add-dependency,$(common_javalib.jar),\ + $(my_proguard_dictionary_directory)/classes.jar) +endif + +ifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP + $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_USAGE_ZIP),\ + $(call local-packaging-dir,proguard_usage)/proguard_usage.zip)) + $(call add-dependency,$(common_javalib.jar),\ + $(call local-packaging-dir,proguard_usage)/proguard_usage.zip) +endif + + +ifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE + my_res_package := $(intermediates.COMMON)/package-res.apk + + $(my_res_package): $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE) + @echo "Copy: $@" + $(copy-file-to-target) + + $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_res_package)) + + my_proguard_flags := $(intermediates.COMMON)/export_proguard_flags + $(my_proguard_flags): $(LOCAL_SOONG_EXPORT_PROGUARD_FLAGS) + @echo "Export proguard flags: $@" + rm -f $@ + touch $@ + for f in $+; do \ + echo -e "\n# including $$f" >>$@; \ + cat $$f >>$@; \ + done + + $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_proguard_flags)) + + my_static_library_extra_packages := $(intermediates.COMMON)/extra_packages + $(eval $(call copy-one-file,$(LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES),$(my_static_library_extra_packages))) + $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_extra_packages)) + + my_static_library_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml + $(eval $(call copy-one-file,$(LOCAL_FULL_MANIFEST_FILE),$(my_static_library_android_manifest))) + $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_android_manifest)) +endif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE + + +ifdef LOCAL_SOONG_DEX_JAR + ifndef LOCAL_IS_HOST_MODULE + boot_jars := $(foreach pair,$(PRODUCT_BOOT_JARS), $(call word-colon,2,$(pair))) + ifneq ($(filter $(LOCAL_MODULE),$(boot_jars)),) # is_boot_jar + ifeq (true,$(WITH_DEXPREOPT)) + # $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE) contains modules that installs + # all of bootjars' dexpreopt files (.art, .oat, .vdex, ...) + # Add them to the required list so they are installed alongside this module. + ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += \ + $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE) \ + $(2ND_DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE) + # Copy $(LOCAL_BUILT_MODULE) and its dependencies when installing boot.art + # so that dependencies of $(LOCAL_BUILT_MODULE) (which may include + # jacoco-report-classes.jar) are copied for every build. + $(foreach m,$(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE) $(2ND_DEFAULT_DEX_PREOPT_INSTALLED_IMAGE_MODULE), \ + $(eval $(call add-dependency,$(firstword $(call module-installed-files,$(m))),$(LOCAL_BUILT_MODULE))) \ + ) + endif + endif # is_boot_jar + + $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar))) + $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(common_javalib.jar))) + ifdef LOCAL_SOONG_CLASSES_JAR + $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_jar))) + ifneq ($(TURBINE_ENABLED),false) + $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_header_jar))) + endif + endif + endif + + java-dex : $(LOCAL_BUILT_MODULE) +else # LOCAL_SOONG_DEX_JAR + ifndef LOCAL_UNINSTALLABLE_MODULE + ifndef LOCAL_IS_HOST_MODULE + $(call pretty-error,Installable device module must have LOCAL_SOONG_DEX_JAR set) + endif + endif +endif # LOCAL_SOONG_DEX_JAR + +ALL_MODULES.$(my_register_name).CLASSES_JAR := $(full_classes_jar) + +ifdef LOCAL_SOONG_AAR + ALL_MODULES.$(my_register_name).AAR := $(LOCAL_SOONG_AAR) +endif + +# Copy dexpreopt.config files from Soong libraries to the location where Make +# modules can find them. +ifdef LOCAL_SOONG_DEXPREOPT_CONFIG + $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(call local-intermediates-dir,)/dexpreopt.config)) + my_dexpreopt_config := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config + $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(my_dexpreopt_config))) + $(LOCAL_BUILT_MODULE): $(my_dexpreopt_config) +endif + +ifdef LOCAL_SOONG_CLASSES_JAR +javac-check : $(full_classes_jar) +javac-check-$(LOCAL_MODULE) : $(full_classes_jar) +endif +.PHONY: javac-check-$(LOCAL_MODULE) + +ifndef LOCAL_IS_HOST_MODULE +ifeq ($(LOCAL_SDK_VERSION),system_current) +my_link_type := java:system +else ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) +my_link_type := java:system +else ifeq ($(LOCAL_SDK_VERSION),core_current) +my_link_type := java:core +else ifneq ($(LOCAL_SDK_VERSION),) +my_link_type := java:sdk +else +my_link_type := java:platform +endif +# warn/allowed types are both empty because Soong modules can't depend on +# make-defined modules. +my_warn_types := +my_allowed_types := + +my_link_deps := +my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) +my_common := COMMON +include $(BUILD_SYSTEM)/link_type.mk +endif # !LOCAL_IS_HOST_MODULE + +# LOCAL_EXPORT_SDK_LIBRARIES set by soong is written to exported-sdk-libs file +my_exported_sdk_libs_file := $(intermediates.COMMON)/exported-sdk-libs +$(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS := $(LOCAL_EXPORT_SDK_LIBRARIES) +$(my_exported_sdk_libs_file): + @echo "Export SDK libs $@" + $(hide) mkdir -p $(dir $@) && rm -f $@ + $(if $(PRIVATE_EXPORTED_SDK_LIBS),\ + $(hide) echo $(PRIVATE_EXPORTED_SDK_LIBS) | tr ' ' '\n' > $@,\ + $(hide) touch $@) + +SOONG_ALREADY_CONV += $(LOCAL_MODULE) diff --git a/make/core/static_java_library.mk b/make/core/static_java_library.mk new file mode 100644 index 0000000..4053985 --- /dev/null +++ b/make/core/static_java_library.mk @@ -0,0 +1,225 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Standard rules for building a "static" java library. +# Static java libraries are not installed, nor listed on any +# classpaths. They can, however, be included wholesale in +# other java modules. + +$(call record-module-type,STATIC_JAVA_LIBRARY) +LOCAL_UNINSTALLABLE_MODULE := true +LOCAL_IS_STATIC_JAVA_LIBRARY := true +LOCAL_MODULE_CLASS := JAVA_LIBRARIES + +intermediates.COMMON := $(call local-intermediates-dir,COMMON) + +my_res_package := + +# Process Support Library dependencies. +include $(BUILD_SYSTEM)/support_libraries.mk + +include $(BUILD_SYSTEM)/force_aapt2.mk + +# Hack to build static Java library with Android resource +# See bug 5714516 +all_resources := +need_compile_res := +# A static Java library needs to explicily set LOCAL_RESOURCE_DIR. +ifdef LOCAL_RESOURCE_DIR +need_compile_res := true +LOCAL_RESOURCE_DIR := $(foreach d,$(LOCAL_RESOURCE_DIR),$(call clean-path,$(d))) +endif +ifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),) +need_compile_res := true +endif + +ifeq ($(need_compile_res),true) +all_resources := $(strip \ + $(foreach dir, $(LOCAL_RESOURCE_DIR), \ + $(addprefix $(dir)/, \ + $(patsubst res/%,%, \ + $(call find-subdir-assets,$(dir)) \ + ) \ + ) \ + )) + +# By default we should remove the R/Manifest classes from a static Java library, +# because they will be regenerated in the app that uses it. +# But if the static Java library will be used by a library, then we may need to +# keep the generated classes with "LOCAL_JAR_EXCLUDE_FILES := none". +ifndef LOCAL_JAR_EXCLUDE_FILES +LOCAL_JAR_EXCLUDE_FILES := $(ANDROID_RESOURCE_GENERATED_CLASSES) +endif +ifeq (none,$(LOCAL_JAR_EXCLUDE_FILES)) +LOCAL_JAR_EXCLUDE_FILES := +endif + +proguard_options_file := + +ifneq ($(filter custom,$(LOCAL_PROGUARD_ENABLED)),custom) + proguard_options_file := $(intermediates.COMMON)/proguard_options +endif + +LOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS) +LOCAL_PROGUARD_FLAGS_DEPS += $(proguard_options_file) + +R_file_stamp := $(intermediates.COMMON)/src/R.stamp +LOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp) + +ifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),) + # If we are using static android libraries, every source file becomes an overlay. + # This is to emulate old AAPT behavior which simulated library support. + my_res_resources := + my_overlay_resources := $(all_resources) +else + # Otherwise, for a library we treat all the resource equal with no overlay. + my_res_resources := $(all_resources) + my_overlay_resources := +endif +# For libraries put everything in the COMMON intermediate directory. +my_res_package := $(intermediates.COMMON)/package-res.apk + +LOCAL_INTERMEDIATE_TARGETS += $(my_res_package) + +endif # need_compile_res + +all_res_assets := $(all_resources) + +include $(BUILD_SYSTEM)/java_renderscript.mk + +ifeq (true,$(need_compile_res)) +# work around missing manifests by creating a default one +ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE))) + ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml)) + LOCAL_FULL_MANIFEST_FILE := $(call local-intermediates-dir,COMMON)/DefaultManifest.xml + $(call create-default-manifest-file,$(LOCAL_FULL_MANIFEST_FILE),$(call module-min-sdk-version)) + endif +endif +include $(BUILD_SYSTEM)/android_manifest.mk + +LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION)) +ifeq ($(LOCAL_SDK_RES_VERSION),) + LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION) +endif + +framework_res_package_export := +# Please refer to package.mk +ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) +ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),) +framework_res_package_export := \ + $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION)) +else +framework_res_package_export := \ + $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk +endif +endif + +import_proguard_flag_files := $(strip $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\ + $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags)) +$(intermediates.COMMON)/export_proguard_flags: $(import_proguard_flag_files) $(addprefix $(LOCAL_PATH)/,$(LOCAL_EXPORT_PROGUARD_FLAG_FILES)) + @echo "Export proguard flags: $@" + rm -f $@ + touch $@ + for f in $+; do \ + echo -e "\n# including $$f" >>$@; \ + cat $$f >>$@; \ + done +import_proguard_flag_files := + +include $(BUILD_SYSTEM)/aapt_flags.mk + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(TARGET_AAPT_CHARACTERISTICS) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_MANIFEST_PACKAGE_NAME) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := $(LOCAL_MANIFEST_INSTRUMENTATION_FOR) + +# add --non-constant-id to prevent inlining constants. +# AAR needs text symbol file R.txt. +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) --static-lib --output-text-symbols $(intermediates.COMMON)/R.txt +ifndef LOCAL_AAPT_NAMESPACES + $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS += --no-static-lib-packages +endif +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PRODUCT_AAPT_CONFIG := +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_AAPT_CHARACTERISTICS := + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ANDROID_MANIFEST := $(full_android_manifest) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_PUBLICS_OUTPUT := $(intermediates.COMMON)/public_resources.xml +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_INCLUDES := $(framework_res_package_export) + +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file) +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_PACKAGE_NAME := +$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := + +# One more level with name res so we can zip up the flat resources that can be linked by apps. +my_compiled_res_base_dir := $(intermediates.COMMON)/flat-res/res +ifneq (,$(filter-out current,$(renderscript_target_api))) + ifneq ($(call math_gt_or_eq,$(renderscript_target_api),21),true) + my_generated_res_zips := $(rs_generated_res_zip) + endif # renderscript_target_api < 21 +endif # renderscript_target_api is set +include $(BUILD_SYSTEM)/aapt2.mk +$(my_res_package) : $(framework_res_package_export) +$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(intermediates.COMMON)/R.txt + +endif # need_compile_res + +include $(BUILD_SYSTEM)/java_library.mk + +ifeq (true,$(need_compile_res)) + +$(LOCAL_BUILT_MODULE): $(R_file_stamp) +$(java_source_list_file): $(R_file_stamp) +$(full_classes_compiled_jar): $(R_file_stamp) +$(full_classes_turbine_jar): $(R_file_stamp) + + +# if we have custom proguarding done use the proguarded classes jar instead of the normal classes jar +ifeq ($(filter custom,$(LOCAL_PROGUARD_ENABLED)),custom) +aar_classes_jar = $(full_classes_jar) +else +aar_classes_jar = $(full_classes_pre_proguard_jar) +endif + +# Rule to build AAR, archive including classes.jar, resource, etc. +built_aar := $(intermediates.COMMON)/javalib.aar +$(built_aar): PRIVATE_MODULE := $(LOCAL_MODULE) +$(built_aar): PRIVATE_ANDROID_MANIFEST := $(full_android_manifest) +$(built_aar): PRIVATE_CLASSES_JAR := $(aar_classes_jar) +$(built_aar): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) +$(built_aar): PRIVATE_R_TXT := $(intermediates.COMMON)/R.txt +$(built_aar): $(JAR_ARGS) +$(built_aar) : $(aar_classes_jar) $(full_android_manifest) $(intermediates.COMMON)/R.txt + @echo "target AAR: $(PRIVATE_MODULE) ($@)" + $(hide) rm -rf $(dir $@)aar && mkdir -p $(dir $@)aar/res + $(hide) cp $(PRIVATE_ANDROID_MANIFEST) $(dir $@)aar/AndroidManifest.xml + $(hide) cp $(PRIVATE_CLASSES_JAR) $(dir $@)aar/classes.jar + # Note: Use "cp -n" to honor the resource overlay rules, if multiple res dirs exist. + $(hide) $(foreach res,$(PRIVATE_RESOURCE_DIR),cp -Rfn $(res)/* $(dir $@)aar/res;) + $(hide) cp $(PRIVATE_R_TXT) $(dir $@)aar/R.txt + $(hide) $(JAR) -cMf $@ \ + $(call jar-args-sorted-files-in-directory,$(dir $@)aar) + +# Register the aar file. +ALL_MODULES.$(my_register_name).AAR := $(built_aar) +endif # need_compile_res + +# Reset internal variables. +aar_classes_jar := +all_res_assets := +LOCAL_IS_STATIC_JAVA_LIBRARY := diff --git a/make/core/static_library.mk b/make/core/static_library.mk new file mode 100644 index 0000000..a450092 --- /dev/null +++ b/make/core/static_library.mk @@ -0,0 +1,49 @@ +$(call record-module-type,STATIC_LIBRARY) +ifdef LOCAL_IS_HOST_MODULE + $(call pretty-error,BUILD_STATIC_LIBRARY is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_STATIC_LIBRARY instead) +endif +my_prefix := TARGET_ +include $(BUILD_SYSTEM)/multilib.mk + +ifndef my_module_multilib +# libraries default to building for both architecturess +my_module_multilib := both +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +include $(BUILD_SYSTEM)/static_library_internal.mk +endif + +ifdef TARGET_2ND_ARCH + +LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) +include $(BUILD_SYSTEM)/module_arch_supported.mk + +ifeq ($(my_module_arch_supported),true) +# Build for TARGET_2ND_ARCH +LOCAL_BUILT_MODULE := +LOCAL_INSTALLED_MODULE := +LOCAL_INTERMEDIATE_TARGETS := + +include $(BUILD_SYSTEM)/static_library_internal.mk + +endif + +LOCAL_2ND_ARCH_VAR_PREFIX := + +endif # TARGET_2ND_ARCH + +my_module_arch_supported := + +########################################################### +## Copy headers to the install tree +########################################################### +ifdef LOCAL_COPY_HEADERS +$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ + $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ + $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) +include $(BUILD_SYSTEM)/copy_headers.mk +endif diff --git a/make/core/static_library_internal.mk b/make/core/static_library_internal.mk new file mode 100644 index 0000000..0392460 --- /dev/null +++ b/make/core/static_library_internal.mk @@ -0,0 +1,43 @@ +########################################################### +## Standard rules for building a static library. +## +## Additional inputs from base_rules.make: +## None. +## +## LOCAL_MODULE_SUFFIX will be set for you. +########################################################### + +ifeq ($(strip $(LOCAL_MODULE_CLASS)),) +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +endif +ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) +LOCAL_MODULE_SUFFIX := .a +endif +LOCAL_UNINSTALLABLE_MODULE := true +ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) +$(error $(LOCAL_PATH): Cannot set module stem for a library) +endif + +include $(BUILD_SYSTEM)/binary.mk + +$(LOCAL_BUILT_MODULE) : $(built_whole_libraries) +$(LOCAL_BUILT_MODULE) : $(all_objects) $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_AR) + $(transform-o-to-static-lib) + +ifeq ($(NATIVE_COVERAGE),true) +gcno_suffix := .zip + +built_whole_gcno_libraries := \ + $(foreach lib,$(my_whole_static_libraries), \ + $(call intermediates-dir-for, \ + STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ + $(my_host_cross))/$(lib)$(gcno_suffix)) + +GCNO_ARCHIVE := $(LOCAL_MODULE)$(gcno_suffix) + +$(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES)) +$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) +$(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) + $(package-coverage-files) +endif diff --git a/make/core/suite_host_config.mk b/make/core/suite_host_config.mk new file mode 100644 index 0000000..d575c5b --- /dev/null +++ b/make/core/suite_host_config.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_MODULE_CLASS := FAKE +LOCAL_IS_HOST_MODULE := true + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): + @echo "$(LOCAL_COMPATIBILITY_SUITE) host-driven test target: $(PRIVATE_MODULE)" + $(hide) touch $@ diff --git a/make/core/support_libraries.mk b/make/core/support_libraries.mk new file mode 100644 index 0000000..7538ce0 --- /dev/null +++ b/make/core/support_libraries.mk @@ -0,0 +1,53 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +########################################################### +## Rules for resolving Support Library dependencies. +## +## The following variables may be modified: +## - LOCAL_JAVA_LIBRARIES +## - LOCAL_STATIC_JAVA_LIBRARIES +## - LOCAL_SHARED_ANDROID_LIBRARIES +## - LOCAL_STATIC_ANDROID_LIBRARIES +########################################################### + +# Some projects don't work correctly yet. Allow them to skip resolution. +ifndef LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES + +# Aggregate all requested Support Library modules. +requested_support_libs := $(filter $(SUPPORT_LIBRARIES_JARS) $(SUPPORT_LIBRARIES_AARS), \ + $(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES) \ + $(LOCAL_SHARED_ANDROID_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES)) + +# Filter the Support Library modules out of the library variables. We don't +# trust developers to get these right, so they will be added back by the +# build system based on the output of this file and the type of build. +LOCAL_JAVA_LIBRARIES := $(filter-out $(requested_support_libs), \ + $(LOCAL_JAVA_LIBRARIES)) +LOCAL_STATIC_JAVA_LIBRARIES := $(filter-out $(requested_support_libs), \ + $(LOCAL_STATIC_JAVA_LIBRARIES)) +LOCAL_SHARED_ANDROID_LIBRARIES := $(filter-out $(requested_support_libs), \ + $(LOCAL_SHARED_ANDROID_LIBRARIES)) +LOCAL_STATIC_ANDROID_LIBRARIES := $(filter-out $(requested_support_libs), \ + $(LOCAL_STATIC_ANDROID_LIBRARIES)) + +LOCAL_STATIC_ANDROID_LIBRARIES := $(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) \ + $(filter $(SUPPORT_LIBRARIES_AARS),$(requested_support_libs))) +LOCAL_STATIC_JAVA_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_LIBRARIES) \ + $(filter $(SUPPORT_LIBRARIES_JARS),$(requested_support_libs))) + +endif #LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES +LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES := diff --git a/make/core/sysprop.mk b/make/core/sysprop.mk new file mode 100644 index 0000000..b491146 --- /dev/null +++ b/make/core/sysprop.mk @@ -0,0 +1,554 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# sysprop.mk defines rules for generating /[etc/]build.prop files + +# ----------------------------------------------------------------- +# property_overrides_split_enabled +property_overrides_split_enabled := +ifeq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true) + property_overrides_split_enabled := true +endif + +BUILDINFO_SH := build/make/tools/buildinfo.sh +POST_PROCESS_PROPS := $(HOST_OUT_EXECUTABLES)/post_process_props$(HOST_EXECUTABLE_SUFFIX) + +# Emits a set of sysprops common to all partitions to a file. +# $(1): Partition name +# $(2): Output file name +define generate-common-build-props + echo "####################################" >> $(2);\ + echo "# from generate-common-build-props" >> $(2);\ + echo "# These properties identify this partition image." >> $(2);\ + echo "####################################" >> $(2);\ + $(if $(filter system,$(1)),\ + echo "ro.product.$(1).brand=$(PRODUCT_SYSTEM_BRAND)" >> $(2);\ + echo "ro.product.$(1).device=$(PRODUCT_SYSTEM_DEVICE)" >> $(2);\ + echo "ro.product.$(1).manufacturer=$(PRODUCT_SYSTEM_MANUFACTURER)" >> $(2);\ + echo "ro.product.$(1).model=$(PRODUCT_SYSTEM_MODEL)" >> $(2);\ + echo "ro.product.$(1).name=$(PRODUCT_SYSTEM_NAME)" >> $(2);\ + ,\ + echo "ro.product.$(1).brand=$(PRODUCT_BRAND)" >> $(2);\ + echo "ro.product.$(1).device=$(TARGET_DEVICE)" >> $(2);\ + echo "ro.product.$(1).manufacturer=$(PRODUCT_MANUFACTURER)" >> $(2);\ + echo "ro.product.$(1).model=$(PRODUCT_MODEL)" >> $(2);\ + echo "ro.product.$(1).name=$(TARGET_PRODUCT)" >> $(2);\ + )\ + $(if $(filter true,$(ZYGOTE_FORCE_64)),\ + $(if $(filter vendor,$(1)),\ + echo "ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\ + echo "ro.$(1).product.cpu.abilist32=" >> $(2);\ + echo "ro.$(1).product.cpu.abilist64=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\ + )\ + ,\ + $(if $(filter system vendor odm,$(1)),\ + echo "ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST)" >> $(2);\ + echo "ro.$(1).product.cpu.abilist32=$(TARGET_CPU_ABI_LIST_32_BIT)" >> $(2);\ + echo "ro.$(1).product.cpu.abilist64=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\ + )\ + )\ + echo "ro.$(1).build.date=`$(DATE_FROM_FILE)`" >> $(2);\ + echo "ro.$(1).build.date.utc=`$(DATE_FROM_FILE) +%s`" >> $(2);\ + echo "ro.$(1).build.fingerprint=$(BUILD_FINGERPRINT_FROM_FILE)" >> $(2);\ + echo "ro.$(1).build.id=$(BUILD_ID)" >> $(2);\ + echo "ro.$(1).build.tags=$(BUILD_VERSION_TAGS)" >> $(2);\ + echo "ro.$(1).build.type=$(TARGET_BUILD_VARIANT)" >> $(2);\ + echo "ro.$(1).build.version.incremental=$(BUILD_NUMBER_FROM_FILE)" >> $(2);\ + echo "ro.$(1).build.version.release=$(PLATFORM_VERSION_LAST_STABLE)" >> $(2);\ + echo "ro.$(1).build.version.release_or_codename=$(PLATFORM_VERSION)" >> $(2);\ + echo "ro.$(1).build.version.sdk=$(PLATFORM_SDK_VERSION)" >> $(2);\ + +endef + +# Rule for generating /[etc/]build.prop file +# +# $(1): partition name +# $(2): path to the output +# $(3): path to the input *.prop files. The contents of the files are directly +# emitted to the output +# $(4): list of variable names each of which contains name=value pairs +# $(5): optional list of prop names to force remove from the output. Properties from both +# $(3) and (4) are affected +# $(6): optional list of files to append at the end. The content of each file is emitted +# to the output +# $(7): optional flag to skip common properties generation +define build-properties +ALL_DEFAULT_INSTALLED_MODULES += $(2) + +$(eval # Properties can be assigned using `prop ?= value` or `prop = value` syntax.) +$(eval # Eliminate spaces around the ?= and = separators.) +$(foreach name,$(strip $(4)),\ + $(eval _temp := $$(call collapse-pairs,$$($(name)),?=))\ + $(eval _resolved_$(name) := $$(call collapse-pairs,$$(_temp),=))\ +) + +$(eval # Implement the legacy behavior when BUILD_BROKEN_DUP_SYSPROP is on.) +$(eval # Optional assignments are all converted to normal assignments and) +$(eval # when their duplicates the first one wins) +$(if $(filter true,$(BUILD_BROKEN_DUP_SYSPROP)),\ + $(foreach name,$(strip $(4)),\ + $(eval _temp := $$(subst ?=,=,$$(_resolved_$(name))))\ + $(eval _resolved_$(name) := $$(call uniq-pairs-by-first-component,$$(_resolved_$(name)),=))\ + )\ + $(eval _option := --allow-dup)\ +) + +$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6) + $(hide) echo Building $$@ + $(hide) mkdir -p $$(dir $$@) + $(hide) rm -f $$@ && touch $$@ +ifneq ($(strip $(7)), true) + $(hide) $$(call generate-common-build-props,$(call to-lower,$(strip $(1))),$$@) +endif + $(hide) $(foreach file,$(strip $(3)),\ + if [ -f "$(file)" ]; then\ + echo "" >> $$@;\ + echo "####################################" >> $$@;\ + echo "# from $(file)" >> $$@;\ + echo "####################################" >> $$@;\ + cat $(file) >> $$@;\ + fi;) + $(hide) $(foreach name,$(strip $(4)),\ + echo "" >> $$@;\ + echo "####################################" >> $$@;\ + echo "# from variable $(name)" >> $$@;\ + echo "####################################" >> $$@;\ + $$(foreach line,$$(_resolved_$(name)),\ + echo "$$(line)" >> $$@;\ + )\ + ) + $(hide) $(POST_PROCESS_PROPS) $$(_option) --sdk-version $(PLATFORM_SDK_VERSION) $$@ $(5) + $(hide) $(foreach file,$(strip $(6)),\ + if [ -f "$(file)" ]; then\ + cat $(file) >> $$@;\ + fi;) + $(hide) echo "# end of file" >> $$@ + +$(call declare-0p-target,$(2)) +endef + +# ----------------------------------------------------------------- +# Define fingerprint, thumbprint, and version tags for the current build +# +# BUILD_VERSION_TAGS is a comma-separated list of tags chosen by the device +# implementer that further distinguishes the build. It's basically defined +# by the device implementer. Here, we are adding a mandatory tag that +# identifies the signing config of the build. +BUILD_VERSION_TAGS := $(BUILD_VERSION_TAGS) +ifeq ($(TARGET_BUILD_TYPE),debug) + BUILD_VERSION_TAGS += debug +endif +# The "test-keys" tag marks builds signed with the old test keys, +# which are available in the SDK. "dev-keys" marks builds signed with +# non-default dev keys (usually private keys from a vendor directory). +# Both of these tags will be removed and replaced with "release-keys" +# when the target-files is signed in a post-build step. +ifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/make/target/product/security/testkey) +BUILD_KEYS := test-keys +else +BUILD_KEYS := dev-keys +endif + +# For user fingerprint +ifeq ($(TARGET_BUILD_VARIANT),eng) +BUILD_KEYS := test-keys +else +BUILD_KEYS := release-keys +endif + +BUILD_VERSION_TAGS += $(BUILD_KEYS) +BUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS))) + +# BUILD_FINGERPRINT is used used to uniquely identify the combined build and +# product; used by the OTA server. +ifeq (,$(strip $(BUILD_FINGERPRINT))) + ifdef ROCKCHIP_BUILD_NUMBER + BF_BUILD_NUMBER := $(ROCKCHIP_BUILD_NUMBER) + else + ifeq ($(strip $(HAS_BUILD_NUMBER)),false) + BF_BUILD_NUMBER := $(BUILD_USERNAME)$$($(DATE_FROM_FILE) +%m%d%H%M) + else + BF_BUILD_NUMBER := $(file <$(BUILD_NUMBER_FILE)) + endif + endif + BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BF_BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS) +endif +# unset it for safety. +BF_BUILD_NUMBER := + +BUILD_FINGERPRINT_FILE := $(PRODUCT_OUT)/build_fingerprint.txt +ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_FINGERPRINT) >$(BUILD_FINGERPRINT_FILE) && grep " " $(BUILD_FINGERPRINT_FILE))) + $(error BUILD_FINGERPRINT cannot contain spaces: "$(file <$(BUILD_FINGERPRINT_FILE))") +endif +BUILD_FINGERPRINT_FROM_FILE := $$(cat $(BUILD_FINGERPRINT_FILE)) +# unset it for safety. +BUILD_FINGERPRINT := + +# BUILD_THUMBPRINT is used to uniquely identify the system build; used by the +# OTA server. This purposefully excludes any product-specific variables. +ifeq (,$(strip $(BUILD_THUMBPRINT))) + BUILD_THUMBPRINT := $(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS) +endif + +BUILD_THUMBPRINT_FILE := $(PRODUCT_OUT)/build_thumbprint.txt +ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep " " $(BUILD_THUMBPRINT_FILE))) + $(error BUILD_THUMBPRINT cannot contain spaces: "$(file <$(BUILD_THUMBPRINT_FILE))") +endif +BUILD_THUMBPRINT_FROM_FILE := $$(cat $(BUILD_THUMBPRINT_FILE)) +# unset it for safety. +BUILD_THUMBPRINT := + +# ----------------------------------------------------------------- +# Define human readable strings that describe this build +# + +# BUILD_ID: detail info; has the same info as the build fingerprint +BUILD_DESC := $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) $(PLATFORM_VERSION) $(BUILD_ID) $(BUILD_NUMBER_FROM_FILE) $(BUILD_VERSION_TAGS) + +# BUILD_DISPLAY_ID is shown under Settings -> About Phone +ifeq ($(TARGET_BUILD_VARIANT),user) + # User builds should show: + # release build number or branch.buld_number non-release builds + + # Dev. branches should have DISPLAY_BUILD_NUMBER set + ifeq (true,$(DISPLAY_BUILD_NUMBER)) + BUILD_DISPLAY_ID := $(BUILD_ID).$(BUILD_NUMBER_FROM_FILE) $(BUILD_KEYS) + else + BUILD_DISPLAY_ID := $(BUILD_ID) $(BUILD_KEYS) + endif +else + # Non-user builds should show detailed build information + BUILD_DISPLAY_ID := $(BUILD_DESC) +endif + +# TARGET_BUILD_FLAVOR and ro.build.flavor are used only by the test +# harness to distinguish builds. Only add _asan for a sanitized build +# if it isn't already a part of the flavor (via a dedicated lunch +# config for example). +TARGET_BUILD_FLAVOR := $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) +ifneq (, $(filter address, $(SANITIZE_TARGET))) +ifeq (,$(findstring _asan,$(TARGET_BUILD_FLAVOR))) +TARGET_BUILD_FLAVOR := $(TARGET_BUILD_FLAVOR)_asan +endif +endif + +KNOWN_OEM_THUMBPRINT_PROPERTIES := \ + ro.product.brand \ + ro.product.name \ + ro.product.device +OEM_THUMBPRINT_PROPERTIES := $(filter $(KNOWN_OEM_THUMBPRINT_PROPERTIES),\ + $(PRODUCT_OEM_PROPERTIES)) +KNOWN_OEM_THUMBPRINT_PROPERTIES:= + +# ----------------------------------------------------------------- +# system/build.prop +# +# Note: parts of this file that can't be generated by the build-properties +# macro are manually created as separate files and then fed into the macro + +# Accepts a whitespace separated list of product locales such as +# (en_US en_AU en_GB...) and returns the first locale in the list with +# underscores replaced with hyphens. In the example above, this will +# return "en-US". +define get-default-product-locale +$(strip $(subst _,-, $(firstword $(1)))) +endef + +gen_from_buildinfo_sh := $(call intermediates-dir-for,PACKAGING,system_build_prop)/buildinfo.prop +$(gen_from_buildinfo_sh): $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) | $(BUILD_DATETIME_FILE) $(BUILD_NUMBER_FILE) + $(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \ + TARGET_BUILD_FLAVOR="$(TARGET_BUILD_FLAVOR)" \ + TARGET_DEVICE="$(TARGET_DEVICE)" \ + PRODUCT_DEFAULT_LOCALE="$(call get-default-product-locale,$(PRODUCT_LOCALES))" \ + PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \ + PRIVATE_BUILD_DESC="$(BUILD_DESC)" \ + BUILD_ID="$(BUILD_ID)" \ + BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \ + DATE="$(DATE_FROM_FILE)" \ + BUILD_USERNAME="$(BUILD_USERNAME)" \ + BUILD_HOSTNAME="$(BUILD_HOSTNAME)" \ + BUILD_NUMBER="$(BUILD_NUMBER_FROM_FILE)" \ + BOARD_BUILD_SYSTEM_ROOT_IMAGE="$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)" \ + BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT="$(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT)" \ + PLATFORM_VERSION="$(PLATFORM_VERSION)" \ + PLATFORM_DISPLAY_VERSION="$(PLATFORM_DISPLAY_VERSION)" \ + PLATFORM_VERSION_LAST_STABLE="$(PLATFORM_VERSION_LAST_STABLE)" \ + PLATFORM_SECURITY_PATCH="$(PLATFORM_SECURITY_PATCH)" \ + PLATFORM_BASE_OS="$(PLATFORM_BASE_OS)" \ + PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \ + PLATFORM_PREVIEW_SDK_VERSION="$(PLATFORM_PREVIEW_SDK_VERSION)" \ + PLATFORM_PREVIEW_SDK_FINGERPRINT="$$(cat $(API_FINGERPRINT))" \ + PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \ + PLATFORM_VERSION_ALL_CODENAMES="$(PLATFORM_VERSION_ALL_CODENAMES)" \ + PLATFORM_VERSION_KNOWN_CODENAMES="$(PLATFORM_VERSION_KNOWN_CODENAMES)" \ + PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION="$(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION)" \ + BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \ + $(if $(OEM_THUMBPRINT_PROPERTIES),BUILD_THUMBPRINT="$(BUILD_THUMBPRINT_FROM_FILE)") \ + TARGET_CPU_ABI_LIST="$(TARGET_CPU_ABI_LIST)" \ + TARGET_CPU_ABI_LIST_32_BIT="$(TARGET_CPU_ABI_LIST_32_BIT)" \ + TARGET_CPU_ABI_LIST_64_BIT="$(TARGET_CPU_ABI_LIST_64_BIT)" \ + TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \ + TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \ + ZYGOTE_FORCE_64_BIT="$(ZYGOTE_FORCE_64_BIT)" \ + bash $(BUILDINFO_SH) > $@ + +ifdef TARGET_SYSTEM_PROP +system_prop_file := $(TARGET_SYSTEM_PROP) +else +system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop) +endif + +_prop_files_ := \ + $(gen_from_buildinfo_sh) \ + $(system_prop_file) + +# Order matters here. When there are duplicates, the last one wins. +# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter +_prop_vars_ := \ + ADDITIONAL_SYSTEM_PROPERTIES \ + PRODUCT_SYSTEM_PROPERTIES + +# TODO(b/117892318): deprecate this +_prop_vars_ += \ + PRODUCT_SYSTEM_DEFAULT_PROPERTIES + +ifndef property_overrides_split_enabled +_prop_vars_ += \ + ADDITIONAL_VENDOR_PROPERTIES \ + PRODUCT_VENDOR_PROPERTIES +endif + +INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop + +$(eval $(call build-properties,\ + system,\ + $(INSTALLED_BUILD_PROP_TARGET),\ + $(_prop_files_),\ + $(_prop_vars_),\ + $(PRODUCT_SYSTEM_PROPERTY_BLACKLIST),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_BUILD_PROP_TARGET))) + +# ----------------------------------------------------------------- +# vendor/build.prop +# +_prop_files_ := $(if $(TARGET_VENDOR_PROP),\ + $(TARGET_VENDOR_PROP),\ + $(wildcard $(TARGET_DEVICE_DIR)/vendor.prop)) + +android_info_prop := $(call intermediates-dir-for,ETC,android_info_prop)/android_info.prop +$(android_info_prop): $(INSTALLED_ANDROID_INFO_TXT_TARGET) + cat $< | grep 'require version-' | sed -e 's/require version-/ro.build.expect./g' > $@ + +_prop_files_ += $(android_info_prop) + +ifdef property_overrides_split_enabled +# Order matters here. When there are duplicates, the last one wins. +# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter +_prop_vars_ := \ + ADDITIONAL_VENDOR_PROPERTIES \ + PRODUCT_VENDOR_PROPERTIES + +# TODO(b/117892318): deprecate this +_prop_vars_ += \ + PRODUCT_DEFAULT_PROPERTY_OVERRIDES \ + PRODUCT_PROPERTY_OVERRIDES +else +_prop_vars_ := +endif + +INSTALLED_VENDOR_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR)/build.prop +$(eval $(call build-properties,\ + vendor,\ + $(INSTALLED_VENDOR_BUILD_PROP_TARGET),\ + $(_prop_files_),\ + $(_prop_vars_),\ + $(PRODUCT_VENDOR_PROPERTY_BLACKLIST),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_VENDOR_BUILD_PROP_TARGET))) + +# ----------------------------------------------------------------- +# product/etc/build.prop +# + +_prop_files_ := $(if $(TARGET_PRODUCT_PROP),\ + $(TARGET_PRODUCT_PROP),\ + $(wildcard $(TARGET_DEVICE_DIR)/product.prop)) + +# Order matters here. When there are duplicates, the last one wins. +# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter +_prop_vars_ := \ + ADDITIONAL_PRODUCT_PROPERTIES \ + PRODUCT_PRODUCT_PROPERTIES + +INSTALLED_PRODUCT_BUILD_PROP_TARGET := $(TARGET_OUT_PRODUCT)/etc/build.prop + +ifdef PRODUCT_OEM_PROPERTIES +import_oem_prop := $(call intermediates-dir-for,ETC,import_oem_prop)/oem.prop + +$(import_oem_prop): + $(hide) echo "####################################" >> $@; \ + echo "# PRODUCT_OEM_PROPERTIES" >> $@; \ + echo "####################################" >> $@; + $(hide) $(foreach prop,$(PRODUCT_OEM_PROPERTIES), \ + echo "import /oem/oem.prop $(prop)" >> $@;) + +_footers_ := $(import_oem_prop) +else +_footers_ := +endif + +# Skip common /product properties generation if device released before R and +# has no product partition. This is the first part of the check. +ifeq ($(call math_lt,$(if $(PRODUCT_SHIPPING_API_LEVEL),$(PRODUCT_SHIPPING_API_LEVEL),30),30), true) + _skip_common_properties := true +endif + +# The second part of the check - always generate common properties for the +# devices with product partition regardless of shipping level. +ifneq ($(BOARD_USES_PRODUCTIMAGE),) + _skip_common_properties := +endif + +$(eval $(call build-properties,\ + product,\ + $(INSTALLED_PRODUCT_BUILD_PROP_TARGET),\ + $(_prop_files_),\ + $(_prop_vars_),\ + $(empty),\ + $(_footers_),\ + $(_skip_common_properties))) + +$(eval $(call declare-1p-target,$(INSTALLED_PRODUCT_BUILD_PROP_TARGET))) + +_skip_common_properties := + +# ---------------------------------------------------------------- +# odm/etc/build.prop +# +_prop_files_ := $(if $(TARGET_ODM_PROP),\ + $(TARGET_ODM_PROP),\ + $(wildcard $(TARGET_DEVICE_DIR)/odm.prop)) + +# Order matters here. When there are duplicates, the last one wins. +# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter +_prop_vars_ := \ + ADDITIONAL_ODM_PROPERTIES \ + PRODUCT_ODM_PROPERTIES + +INSTALLED_ODM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM)/etc/build.prop +$(eval $(call build-properties,\ + odm,\ + $(INSTALLED_ODM_BUILD_PROP_TARGET),\ + $(_prop_files_),\ + $(_prop_vars_),\ + $(empty),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_ODM_BUILD_PROP_TARGET))) + +# ---------------------------------------------------------------- +# vendor_dlkm/etc/build.prop +# + +INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR_DLKM)/etc/build.prop +$(eval $(call build-properties,\ + vendor_dlkm,\ + $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET))) + +# ---------------------------------------------------------------- +# odm_dlkm/etc/build.prop +# + +INSTALLED_ODM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM_DLKM)/etc/build.prop +$(eval $(call build-properties,\ + odm_dlkm,\ + $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET))) + +# ---------------------------------------------------------------- +# system_dlkm/build.prop +# + +INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_DLKM)/etc/build.prop +$(eval $(call build-properties,\ + system_dlkm,\ + $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET))) + +# ----------------------------------------------------------------- +# system_ext/etc/build.prop +# +_prop_files_ := $(if $(TARGET_SYSTEM_EXT_PROP),\ + $(TARGET_SYSTEM_EXT_PROP),\ + $(wildcard $(TARGET_DEVICE_DIR)/system_ext.prop)) + +# Order matters here. When there are duplicates, the last one wins. +# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter +_prop_vars_ := PRODUCT_SYSTEM_EXT_PROPERTIES + +INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_EXT)/etc/build.prop +$(eval $(call build-properties,\ + system_ext,\ + $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET),\ + $(_prop_files_),\ + $(_prop_vars_),\ + $(empty),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET))) + +# ---------------------------------------------------------------- +# ramdisk/boot/etc/build.prop +# + +RAMDISK_BUILD_PROP_REL_PATH := system/etc/ramdisk/build.prop +INSTALLED_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RAMDISK_OUT)/$(RAMDISK_BUILD_PROP_REL_PATH) +$(eval $(call build-properties,\ + bootimage,\ + $(INSTALLED_RAMDISK_BUILD_PROP_TARGET),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty),\ + $(empty))) + +$(eval $(call declare-1p-target,$(INSTALLED_RAMDISK_BUILD_PROP_TARGET))) diff --git a/make/core/target_test_internal.mk b/make/core/target_test_internal.mk new file mode 100644 index 0000000..5745451 --- /dev/null +++ b/make/core/target_test_internal.mk @@ -0,0 +1,47 @@ +####################################################### +## Shared definitions for all target test compilations. +####################################################### + +ifeq ($(LOCAL_GTEST),true) + LOCAL_CFLAGS += -DGTEST_OS_LINUX_ANDROID -DGTEST_HAS_STD_STRING + + ifndef LOCAL_SDK_VERSION + LOCAL_STATIC_LIBRARIES += libgtest_main libgtest + else + # TODO(danalbert): Remove the suffix from the module since we only need the + # one variant now. + my_ndk_gtest_suffix := _c++ + LOCAL_STATIC_LIBRARIES += \ + libgtest_main_ndk$(my_ndk_gtest_suffix) \ + libgtest_ndk$(my_ndk_gtest_suffix) + endif +endif + +ifdef LOCAL_MODULE_PATH +$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH when building test $(LOCAL_MODULE)) +endif + +ifdef LOCAL_MODULE_PATH_32 +$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_32 when building test $(LOCAL_MODULE)) +endif + +ifdef LOCAL_MODULE_PATH_64 +$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_64 when building test $(LOCAL_MODULE)) +endif + +use_testcase_folder := false +ifneq ($(LOCAL_MODULE),$(filter $(LOCAL_MODULE),$(DEFAULT_DATA_OUT_MODULES))) + use_testcase_folder := true +endif + +ifneq ($(use_testcase_folder),true) +ifndef LOCAL_MODULE_RELATIVE_PATH +LOCAL_MODULE_RELATIVE_PATH := $(LOCAL_MODULE) +endif +endif + +# Implicitly run this test under MTE SYNC for aarch64 binaries. This is a no-op +# on non-MTE hardware. +ifneq (,$(filter arm64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))) + LOCAL_WHOLE_STATIC_LIBRARIES += note_memtag_heap_sync +endif diff --git a/make/core/tasks/OWNERS b/make/core/tasks/OWNERS new file mode 100644 index 0000000..594930d --- /dev/null +++ b/make/core/tasks/OWNERS @@ -0,0 +1 @@ +per-file art-host-tests.mk = dshi@google.com,dsrbecky@google.com,jdesprez@google.com,rpl@google.com diff --git a/make/core/tasks/art-host-tests.mk b/make/core/tasks/art-host-tests.mk new file mode 100644 index 0000000..2af1ded --- /dev/null +++ b/make/core/tasks/art-host-tests.mk @@ -0,0 +1,48 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.PHONY: art-host-tests + +intermediates_dir := $(call intermediates-dir-for,PACKAGING,art-host-tests) +art_host_tests_zip := $(PRODUCT_OUT)/art-host-tests.zip +# Get the hostside libraries to be packaged in the test zip. Unlike +# device-tests.mk or general-tests.mk, the files are not copied to the +# testcases directory. +my_host_shared_lib_for_art_host_tests := $(foreach f,$(COMPATIBILITY.art-host-tests.HOST_SHARED_LIBRARY.FILES),$(strip \ + $(eval _cmf_tuple := $(subst :, ,$(f))) \ + $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ + $(_cmf_src))) + +$(art_host_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_art_host_tests) + +$(art_host_tests_zip) : $(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests) $(SOONG_ZIP) + echo $(sort $(COMPATIBILITY.art-host-tests.FILES)) | tr " " "\n" > $@.list + grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true + $(hide) touch $@-host-libs.list + $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ + echo $$shared_lib >> $@-host-libs.list; \ + done + grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true + $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \ + -P target -C $(PRODUCT_OUT) -l $@-target.list \ + -P host/testcases -C $(HOST_OUT) -l $@-host-libs.list + rm -f $@.list $@-host.list $@-target.list $@-host-libs.list + +art-host-tests: $(art_host_tests_zip) +$(call dist-for-goals, art-host-tests, $(art_host_tests_zip)) + +$(call declare-1p-container,$(art_host_tests_zip),) +$(call declare-container-license-deps,$(art_host_tests_zip),$(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests),$(PRODUCT_OUT)/:/) + +tests: art-host-tests diff --git a/make/core/tasks/build_custom_images.mk b/make/core/tasks/build_custom_images.mk new file mode 100644 index 0000000..c9b07da --- /dev/null +++ b/make/core/tasks/build_custom_images.mk @@ -0,0 +1,81 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Build additional images requested by the product makefile. +# This script gives the ability to build multiple additional images and you can +# configure what modules/files to include in each image. +# 1. Define PRODUCT_CUSTOM_IMAGE_MAKEFILES in your product makefile. +# PRODUCT_CUSTOM_IMAGE_MAKEFILES is a list of makefiles. +# Each makefile configures an image. +# For image configuration makefile foo/bar/xyz.mk, the built image file name +# will be xyz.img. So make sure they won't conflict. +# 2. In each image's configuration makefile, you can define variables: +# - CUSTOM_IMAGE_MOUNT_POINT, the mount point, such as "oem", "odm" etc. +# - CUSTOM_IMAGE_PARTITION_SIZE +# - CUSTOM_IMAGE_FILE_SYSTEM_TYPE +# - CUSTOM_IMAGE_DICT_FILE, a text file defines a dictionary accepted by +# BuildImage() in tools/releasetools/build_image.py. +# - CUSTOM_IMAGE_MODULES, a list of module names you want to include in +# the image; Not only the module itself will be installed to proper path in +# the image, you can also piggyback additional files/directories with the +# module's LOCAL_PICKUP_FILES. +# - CUSTOM_IMAGE_COPY_FILES, a list of ":" to be copied to the +# image. is relativ to the root of the image. +# - CUSTOM_IMAGE_SELINUX, set to "true" if the image supports selinux. +# - CUSTOM_IMAGE_SUPPORT_VERITY, set to "true" if the product supports verity. +# - CUSTOM_IMAGE_SUPPORT_VERITY_FEC, set to "true" if the product supports +# verity FEC (forward error correction). +# - CUSTOM_IMAGE_VERITY_BLOCK_DEVICE +# - CUSTOM_IMAGE_AVB_HASH_ENABLE, set to "true" to add AVB HASH footer. +# - CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS, additional args of AVB HASH footer. +# - CUSTOM_IMAGE_AVB_HASHTREE_ENABLE, set to "true" to add AVB HASHTREE +# footer. +# - CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS, additional args of AVB +# HASHTREE footer. +# - CUSTOM_IMAGE_AVB_KEY_PATH, custom AVB signing key. +# - CUSTOM_IMAGE_AVB_ALGORITHM, custom AVB signing algorithm. +# +# To build all those images, run "make custom_images". + +ifneq ($(filter $(MAKECMDGOALS),custom_images),) + +.PHONY: custom_images + +custom_image_parameter_variables := \ + CUSTOM_IMAGE_MOUNT_POINT \ + CUSTOM_IMAGE_PARTITION_SIZE \ + CUSTOM_IMAGE_FILE_SYSTEM_TYPE \ + CUSTOM_IMAGE_DICT_FILE \ + CUSTOM_IMAGE_MODULES \ + CUSTOM_IMAGE_COPY_FILES \ + CUSTOM_IMAGE_SELINUX \ + CUSTOM_IMAGE_SUPPORT_VERITY \ + CUSTOM_IMAGE_SUPPORT_VERITY_FEC \ + CUSTOM_IMAGE_VERITY_BLOCK_DEVICE \ + CUSTOM_IMAGE_AVB_HASH_ENABLE \ + CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS \ + CUSTOM_IMAGE_AVB_HASHTREE_ENABLE \ + CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS \ + CUSTOM_IMAGE_AVB_KEY_PATH \ + CUSTOM_IMAGE_AVB_ALGORITHM \ + +# We don't expect product makefile to inherit/override PRODUCT_CUSTOM_IMAGE_MAKEFILES, +# so we don't put it in the _product_var_list. +$(foreach mk, $(PRODUCT_CUSTOM_IMAGE_MAKEFILES),\ + $(eval my_custom_imag_makefile := $(mk))\ + $(eval include $(BUILD_SYSTEM)/tasks/tools/build_custom_image.mk)) + +endif diff --git a/make/core/tasks/catbox.mk b/make/core/tasks/catbox.mk new file mode 100644 index 0000000..443f4bb --- /dev/null +++ b/make/core/tasks/catbox.mk @@ -0,0 +1,24 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +test_suite_name := catbox +test_suite_tradefed := catbox-tradefed +test_suite_readme := test/catbox/tools/catbox-tradefed/README +test_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/catbox-report-lib.jar + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: catbox +catbox: $(compatibility_zip) +$(call dist-for-goals, catbox, $(compatibility_zip)) \ No newline at end of file diff --git a/make/core/tasks/collect_gpl_sources.mk b/make/core/tasks/collect_gpl_sources.mk new file mode 100644 index 0000000..9e9ab8e --- /dev/null +++ b/make/core/tasks/collect_gpl_sources.mk @@ -0,0 +1,29 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The rule below doesn't have dependenices on the files that it copies, +# so manually generate into a PACKAGING intermediate dir, which is wiped +# in installclean between incremental builds on build servers. +gpl_source_tgz := $(call intermediates-dir-for,PACKAGING,gpl_source)/gpl_source.tgz + +ALL_GPL_MODULE_LICENSE_FILES := $(sort $(ALL_GPL_MODULE_LICENSE_FILES)) + +# FORCE since we can't know whether any of the sources changed +$(gpl_source_tgz): PRIVATE_PATHS := $(sort $(patsubst %/, %, $(dir $(ALL_GPL_MODULE_LICENSE_FILES)))) +$(gpl_source_tgz) : $(ALL_GPL_MODULE_LICENSE_FILES) + @echo Package GPL sources: $@ + $(hide) tar cfz $@ --exclude ".git*" $(PRIVATE_PATHS) + +# Dist the tgz only if we are doing a full build +$(call dist-for-goals,droidcore-unbundled,$(gpl_source_tgz)) diff --git a/make/core/tasks/csuite.mk b/make/core/tasks/csuite.mk new file mode 100644 index 0000000..a8dba1d --- /dev/null +++ b/make/core/tasks/csuite.mk @@ -0,0 +1,23 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +test_suite_name := csuite +test_suite_tradefed := csuite-tradefed +test_suite_readme := test/app_compat/csuite/README.md + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: csuite +csuite: $(compatibility_zip) +$(call dist-for-goals, csuite, $(compatibility_zip)) diff --git a/make/core/tasks/cts.mk b/make/core/tasks/cts.mk new file mode 100644 index 0000000..c282268 --- /dev/null +++ b/make/core/tasks/cts.mk @@ -0,0 +1,236 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +test_suite_name := cts +test_suite_tradefed := cts-tradefed +test_suite_dynamic_config := cts/tools/cts-tradefed/DynamicConfig.xml +test_suite_readme := cts/tools/cts-tradefed/README + +$(call declare-1p-target,$(test_suite_dynamic_config),cts) +$(call declare-1p-target,$(test_suite_readme),cts) + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: cts +cts: $(compatibility_zip) $(compatibility_tests_list_zip) +$(call dist-for-goals, cts, $(compatibility_zip) $(compatibility_tests_list_zip)) + +.PHONY: cts_v2 +cts_v2: cts + +# platform version check (b/32056228) +# ============================================================ +ifneq (,$(wildcard cts/)) + cts_platform_version_path := cts/tests/tests/os/assets/platform_versions.txt + cts_platform_version_string := $(shell cat $(cts_platform_version_path)) + cts_platform_release_path := cts/tests/tests/os/assets/platform_releases.txt + cts_platform_release_string := $(shell cat $(cts_platform_release_path)) + + ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string))) + define error_msg + ============================================================ + Could not find version "$(PLATFORM_VERSION)" in CTS platform version file: + $(cts_platform_version_path) + Most likely PLATFORM_VERSION in build/core/version_defaults.mk + has changed and a new version must be added to this CTS file. + ============================================================ + endef + $(error $(error_msg)) + endif + ifeq (,$(findstring $(PLATFORM_VERSION_LAST_STABLE),$(cts_platform_release_string))) + define error_msg + ============================================================ + Could not find version "$(PLATFORM_VERSION_LAST_STABLE)" in CTS platform release file: + $(cts_platform_release_path) + Most likely PLATFORM_VERSION_LAST_STABLE in build/core/version_defaults.mk + has changed and a new version must be added to this CTS file. + ============================================================ + endef + $(error $(error_msg)) + endif +endif + +# Creates a "cts-verifier" directory that will contain: +# +# 1. Out directory with a "android-cts-verifier" containing the CTS Verifier +# and other binaries it needs. +# +# 2. Zipped version of the android-cts-verifier directory to be included with +# the build distribution. +## +cts-dir := $(HOST_OUT)/cts-verifier +verifier-dir-name := android-cts-verifier +verifier-dir := $(cts-dir)/$(verifier-dir-name) +verifier-zip-name := $(verifier-dir-name).zip +verifier-zip := $(cts-dir)/$(verifier-zip-name) + +cts : $(verifier-zip) +$(verifier-zip): PRIVATE_DIR := $(cts-dir) +$(verifier-zip): $(SOONG_ANDROID_CTS_VERIFIER_ZIP) + rm -rf $(PRIVATE_DIR) + mkdir -p $(PRIVATE_DIR) + unzip -q -d $(PRIVATE_DIR) $< + $(copy-file-to-target) + +# For producing CTS coverage reports. +# Run "make cts-test-coverage" in the $ANDROID_BUILD_TOP directory. + +cts_api_coverage_exe := $(HOST_OUT_EXECUTABLES)/cts-api-coverage +dexdeps_exe := $(HOST_OUT_EXECUTABLES)/dexdeps + +coverage_out := $(HOST_OUT)/cts-api-coverage + +api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml + +napi_text_description := cts/tools/cts-api-coverage/etc/ndk-api.xml +napi_xml_description := $(coverage_out)/ndk-api.xml +$(napi_xml_description) : $(napi_text_description) $(ACP) + $(hide) echo "Preparing NDK API XML: $@" + $(hide) mkdir -p $(dir $@) + $(hide) $(ACP) $< $@ + +system_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml + +cts-test-coverage-report := $(coverage_out)/test-coverage.html +cts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html +cts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml +cts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html +cts-combined-coverage-report := $(coverage_out)/combined-coverage.html +cts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml + +cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description) +cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description) + +android_cts_zip := $(HOST_OUT)/cts/android-cts.zip +cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk + +$(cts-test-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts) +$(cts-test-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) +$(cts-test-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) +$(cts-test-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) +$(cts-test-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) +$(cts-test-coverage-report) : $(android_cts_zip) $(cts_api_coverage_dependencies) | $(ACP) + $(call generate-coverage-report-cts,"CTS Tests API-NDK Coverage Report",\ + $(PRIVATE_TEST_CASES),html) + +$(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts) +$(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) +$(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) +$(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description) +$(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := "" +$(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP) + $(call generate-coverage-report-cts,"CTS System API Coverage Report",\ + $(PRIVATE_TEST_CASES),html) + +$(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts) +$(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) +$(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) +$(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description) +$(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := "" +$(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP) + $(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\ + $(PRIVATE_TEST_CASES),xml) + +$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(cts_verifier_apk) +$(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) +$(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) +$(cts-verifier-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) +$(cts-verifier-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) +$(cts-verifier-coverage-report) : $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP) + $(call generate-coverage-report-cts,"CTS Verifier API Coverage Report",\ + $(PRIVATE_TEST_CASES),html) + +$(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c)) +$(cts-combined-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) +$(cts-combined-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) +$(cts-combined-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) +$(cts-combined-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) +$(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP) + $(call generate-coverage-report-cts,"CTS Combined API Coverage Report",\ + $(PRIVATE_TEST_CASES),html) + +$(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c)) +$(cts-combined-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) +$(cts-combined-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) +$(cts-combined-xml-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) +$(cts-combined-xml-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) +$(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP) + $(call generate-coverage-report-cts,"CTS Combined API Coverage Report - XML",\ + $(PRIVATE_TEST_CASES),xml) + +.PHONY: cts-test-coverage +cts-test-coverage : $(cts-test-coverage-report) + +.PHONY: cts-system-api-coverage +cts-system-api-coverage : $(cts-system-api-coverage-report) + +.PHONY: cts-system-api-xml-coverage +cts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report) + +.PHONY: cts-verifier-coverage +cts-verifier-coverage : $(cts-verifier-coverage-report) + +.PHONY: cts-combined-coverage +cts-combined-coverage : $(cts-combined-coverage-report) + +.PHONY: cts-combined-xml-coverage +cts-combined-xml-coverage : $(cts-combined-xml-coverage-report) + +.PHONY: cts-coverage-report-all cts-api-coverage +cts-coverage-report-all: cts-test-coverage cts-verifier-coverage cts-combined-coverage cts-combined-xml-coverage + +# Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals. +$(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html) +$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html) +$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml) +$(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html) +$(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html) +$(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml) + +ALL_TARGETS.$(cts-test-coverage-report).META_LIC:=$(module_license_metadata) +ALL_TARGETS.$(cts-system-api-coverage-report).META_LIC:=$(module_license_metadata) +ALL_TARGETS.$(cts-system-api-xml-coverage-report).META_LIC:=$(module_license_metadata) +ALL_TARGETS.$(cts-verifier-coverage-report).META_LIC:=$(module_license_metadata) +ALL_TARGETS.$(cts-combined-coverage-report).META_LIC:=$(module_license_metadata) +ALL_TARGETS.$(cts-combined-xml-coverage-report).META_LIC:=$(module_license_metadata) + +# Arguments; +# 1 - Name of the report printed out on the screen +# 2 - List of apk files that will be scanned to generate the report +# 3 - Format of the report +define generate-coverage-report-cts + $(hide) mkdir -p $(dir $@) + $(hide) $(PRIVATE_CTS_API_COVERAGE_EXE) -d $(PRIVATE_DEXDEPS_EXE) -a $(PRIVATE_API_XML_DESC) -n $(PRIVATE_NAPI_XML_DESC) -f $(3) -o $@ $(2) + @ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@) +endef + +# Reset temp vars +cts_api_coverage_dependencies := +cts_system_api_coverage_dependencies := +cts-combined-coverage-report := +cts-combined-xml-coverage-report := +cts-verifier-coverage-report := +cts-test-coverage-report := +cts-system-api-coverage-report := +cts-system-api-xml-coverage-report := +api_xml_description := +api_text_description := +system_api_xml_description := +napi_xml_description := +napi_text_description := +coverage_out := +dexdeps_exe := +cts_api_coverage_exe := +cts_verifier_apk := +android_cts_zip := diff --git a/make/core/tasks/cts_root.mk b/make/core/tasks/cts_root.mk new file mode 100644 index 0000000..b618121 --- /dev/null +++ b/make/core/tasks/cts_root.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ifneq ($(wildcard test/cts-root/README.md),) +test_suite_name := cts_root +test_suite_tradefed := cts-root-tradefed +test_suite_readme := test/cts-root/README.md + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: cts_root +cts_root: $(compatibility_zip) +$(call dist-for-goals, cts_root, $(compatibility_zip)) +endif diff --git a/make/core/tasks/device-tests.mk b/make/core/tasks/device-tests.mk new file mode 100644 index 0000000..3196f52 --- /dev/null +++ b/make/core/tasks/device-tests.mk @@ -0,0 +1,61 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.PHONY: device-tests + +device-tests-zip := $(PRODUCT_OUT)/device-tests.zip +# Create an artifact to include a list of test config files in device-tests. +device-tests-list-zip := $(PRODUCT_OUT)/device-tests_list.zip +# Create an artifact to include all test config files in device-tests. +device-tests-configs-zip := $(PRODUCT_OUT)/device-tests_configs.zip +my_host_shared_lib_for_device_tests := $(call copy-many-files,$(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES)) +device_tests_host_shared_libs_zip := $(PRODUCT_OUT)/device-tests_host-shared-libs.zip + +$(device-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(device-tests-list-zip) $(device-tests-configs-zip) $(device_tests_host_shared_libs_zip) +$(device-tests-zip) : PRIVATE_device_tests_list := $(PRODUCT_OUT)/device-tests_list +$(device-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests) +$(device-tests-zip) : PRIVATE_device_host_shared_libs_zip := $(device_tests_host_shared_libs_zip) +$(device-tests-zip) : $(COMPATIBILITY.device-tests.FILES) $(my_host_shared_lib_for_device_tests) $(SOONG_ZIP) + rm -f $@-shared-libs.list + echo $(sort $(COMPATIBILITY.device-tests.FILES)) | tr " " "\n" > $@.list + grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true + grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true + $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ + echo $$shared_lib >> $@-host.list; \ + echo $$shared_lib >> $@-shared-libs.list; \ + done + grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true + grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true + grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true + $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list + $(hide) $(SOONG_ZIP) -d -o $(device-tests-configs-zip) \ + -P host -C $(HOST_OUT) -l $@-host-test-configs.list \ + -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list + $(SOONG_ZIP) -d -o $(PRIVATE_device_host_shared_libs_zip) \ + -P host -C $(HOST_OUT) -l $@-host-shared-libs.list + rm -f $(PRIVATE_device_tests_list) + $(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_device_tests_list) + $(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_device_tests_list) + $(hide) $(SOONG_ZIP) -d -o $(device-tests-list-zip) -C $(dir $@) -f $(PRIVATE_device_tests_list) + rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \ + $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_device_tests_list) + +device-tests: $(device-tests-zip) +$(call dist-for-goals, device-tests, $(device-tests-zip) $(device-tests-list-zip) $(device-tests-configs-zip) $(device_tests_host_shared_libs_zip)) + +$(call declare-1p-container,$(device-tests-zip),) +$(call declare-container-license-deps,$(device-tests-zip),$(COMPATIBILITY.device-tests.FILES) $(my_host_shared_lib_for_device_tests),$(PRODUCT_OUT)/:/) + +tests: device-tests diff --git a/make/core/tasks/dex_preopt_check.mk b/make/core/tasks/dex_preopt_check.mk new file mode 100644 index 0000000..bfa1ec5 --- /dev/null +++ b/make/core/tasks/dex_preopt_check.mk @@ -0,0 +1,18 @@ +# Checks that some critical dexpreopt output files are installed. + +# Inputs: +# DISABLE_DEXPREOPT_CHECK: True if the check should be disabled. +# PRODUCT_PACKAGES: The list of packages to be installed for the product. +# ALL_DEFAULT_INSTALLED_MODULES: The full list of modules going to be installed. +# DEXPREOPT_SYSTEMSERVER_ARTIFACTS: The list of compilation artifacts of system server jars, which +# is generated by Soong in dexpreopt_check.go. + +ifneq (true,$(DISABLE_DEXPREOPT_CHECK)) + # Skip the check if the system server is not installed for the product. + ifneq (,$(filter services,$(PRODUCT_PACKAGES))) + $(call maybe-print-list-and-error,\ + $(filter-out $(ALL_DEFAULT_INSTALLED_MODULES),$(DEXPREOPT_SYSTEMSERVER_ARTIFACTS)),\ + Missing compilation artifacts. Dexpreopting is not working for some system server jars \ + ) + endif +endif diff --git a/make/core/tasks/find-shareduid-violation.mk b/make/core/tasks/find-shareduid-violation.mk new file mode 100644 index 0000000..b5feef1 --- /dev/null +++ b/make/core/tasks/find-shareduid-violation.mk @@ -0,0 +1,39 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +shareduid_violation_modules_filename := $(PRODUCT_OUT)/shareduid_violation_modules.json + +$(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(INSTALLED_USERDATAIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) + +$(shareduid_violation_modules_filename): $(HOST_OUT_EXECUTABLES)/find_shareduid_violation +$(shareduid_violation_modules_filename): $(AAPT2) + $(HOST_OUT_EXECUTABLES)/find_shareduid_violation \ + --product_out $(PRODUCT_OUT) \ + --aapt $(AAPT2) \ + --copy_out_system $(TARGET_COPY_OUT_SYSTEM) \ + --copy_out_vendor $(TARGET_COPY_OUT_VENDOR) \ + --copy_out_product $(TARGET_COPY_OUT_PRODUCT) \ + --copy_out_system_ext $(TARGET_COPY_OUT_SYSTEM_EXT) \ + > $@ + +$(call declare-0p-target,$(shareduid_violation_modules_filename)) +$(call dist-for-goals,droidcore,$(shareduid_violation_modules_filename)) diff --git a/make/core/tasks/general-tests.mk b/make/core/tasks/general-tests.mk new file mode 100644 index 0000000..5252394 --- /dev/null +++ b/make/core/tasks/general-tests.mk @@ -0,0 +1,102 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.PHONY: general-tests + +general_tests_tools := \ + $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/vts-tradefed.jar \ + +intermediates_dir := $(call intermediates-dir-for,PACKAGING,general-tests) +general_tests_zip := $(PRODUCT_OUT)/general-tests.zip +# Create an artifact to include a list of test config files in general-tests. +general_tests_list_zip := $(PRODUCT_OUT)/general-tests_list.zip + +# Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES, +# to avoid warning about overriding commands. +my_host_shared_lib_for_general_tests := \ + $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\ + $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m))) +my_general_tests_shared_lib_files := \ + $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\ + $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)) + +my_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files)) + +# Create an artifact to include all test config files in general-tests. +general_tests_configs_zip := $(PRODUCT_OUT)/general-tests_configs.zip +# Create an artifact to include all shared librariy files in general-tests. +general_tests_host_shared_libs_zip := $(PRODUCT_OUT)/general-tests_host-shared-libs.zip + +# Copy kernel test modules to testcases directories +include $(BUILD_SYSTEM)/tasks/tools/vts-kernel-tests.mk +kernel_test_copy_pairs := \ + $(call target-native-copy-pairs,$(kernel_test_modules),$(kernel_test_host_out)) +copy_kernel_tests := $(call copy-many-files,$(kernel_test_copy_pairs)) + +# PHONY target to be used to build and test `vts_kernel_tests` without building full vts +.PHONY: vts_kernel_tests +vts_kernel_tests: $(copy_kernel_tests) + +$(general_tests_zip) : $(copy_kernel_tests) +$(general_tests_zip) : PRIVATE_KERNEL_TEST_HOST_OUT := $(kernel_test_host_out) +$(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip) +$(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_host_shared_libs_zip) +$(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools) +$(general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir) +$(general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests) +$(general_tests_zip) : PRIVATE_general_tests_configs_zip := $(general_tests_configs_zip) +$(general_tests_zip) : PRIVATE_general_host_shared_libs_zip := $(general_tests_host_shared_libs_zip) +$(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(general_tests_tools) $(my_host_shared_lib_for_general_tests) $(SOONG_ZIP) + rm -rf $(PRIVATE_INTERMEDIATES_DIR) + rm -f $@ $(PRIVATE_general_tests_list_zip) + mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools + echo $(sort $(COMPATIBILITY.general-tests.FILES)) | tr " " "\n" > $(PRIVATE_INTERMEDIATES_DIR)/list + find $(PRIVATE_KERNEL_TEST_HOST_OUT) >> $(PRIVATE_INTERMEDIATES_DIR)/list + grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true + grep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true + grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true + grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list > $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list || true + $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ + echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/host.list; \ + echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \ + done + grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true + cp -fp $(PRIVATE_TOOLS) $(PRIVATE_INTERMEDIATES_DIR)/tools/ + $(SOONG_ZIP) -d -o $@ \ + -P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \ + -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \ + -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target.list + $(SOONG_ZIP) -d -o $(PRIVATE_general_tests_configs_zip) \ + -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \ + -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list + $(SOONG_ZIP) -d -o $(PRIVATE_general_host_shared_libs_zip) \ + -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list + grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list + grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list + $(SOONG_ZIP) -d -o $(PRIVATE_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list + +general-tests: $(general_tests_zip) +$(call dist-for-goals, general-tests, $(general_tests_zip) $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_host_shared_libs_zip)) + +$(call declare-1p-container,$(general_tests_zip),) +$(call declare-container-license-deps,$(general_tests_zip),$(COMPATIBILITY.general-tests.FILES) $(general_tests_tools) $(my_host_shared_lib_for_general_tests),$(PRODUCT_OUT)/:/) + +intermediates_dir := +general_tests_tools := +general_tests_zip := +general_tests_list_zip := +general_tests_configs_zip := +general_tests_host_shared_libs_zip := diff --git a/make/core/tasks/host-unit-tests.mk b/make/core/tasks/host-unit-tests.mk new file mode 100644 index 0000000..4453c29 --- /dev/null +++ b/make/core/tasks/host-unit-tests.mk @@ -0,0 +1,53 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# `host-unit-tests` shall only include hostside unittest that don't require a device to run. Tests +# included will be run as part of presubmit check. +# To add tests to the suite, do one of the following: +# * For test modules configured with Android.bp, set attribute `test_options: { unit_test: true }` +# * For test modules configured with mk, set `LOCAL_IS_UNIT_TEST := true` +.PHONY: host-unit-tests + +intermediates_dir := $(call intermediates-dir-for,PACKAGING,host-unit-tests) +host_unit_tests_zip := $(PRODUCT_OUT)/host-unit-tests.zip +# Get the hostside libraries to be packaged in the test zip. Unlike +# device-tests.mk or general-tests.mk, the files are not copied to the +# testcases directory. +my_host_shared_lib_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.HOST_SHARED_LIBRARY.FILES),$(strip \ + $(eval _cmf_tuple := $(subst :, ,$(f))) \ + $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ + $(_cmf_src))) + +$(host_unit_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_host_unit_tests) + +$(host_unit_tests_zip) : $(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests) $(SOONG_ZIP) + echo $(sort $(COMPATIBILITY.host-unit-tests.FILES)) | tr " " "\n" > $@.list + grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true + echo "" >> $@-host-libs.list + $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ + echo $$shared_lib >> $@-host-libs.list; \ + done + grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true + $(hide) $(SOONG_ZIP) -L 0 -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \ + -P target -C $(PRODUCT_OUT) -l $@-target.list \ + -P host/testcases -C $(HOST_OUT) -l $@-host-libs.list + rm -f $@.list $@-host.list $@-target.list $@-host-libs.list + +host-unit-tests: $(host_unit_tests_zip) +$(call dist-for-goals, host-unit-tests, $(host_unit_tests_zip)) + +$(call declare-1p-container,$(host_unit_tests_zip),) +$(call declare-container-license-deps,$(host_unit_tests_zip),$(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests),$(PRODUCT_OUT)/:/) + +tests: host-unit-tests diff --git a/make/core/tasks/host_init_verifier.mk b/make/core/tasks/host_init_verifier.mk new file mode 100644 index 0000000..e463710 --- /dev/null +++ b/make/core/tasks/host_init_verifier.mk @@ -0,0 +1,56 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +host_init_verifier_output := $(PRODUCT_OUT)/host_init_verifier_output.txt + +$(host_init_verifier_output): \ + $(INSTALLED_SYSTEMIMAGE_TARGET) \ + $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ + $(INSTALLED_VENDORIMAGE_TARGET) \ + $(INSTALLED_ODMIMAGE_TARGET) \ + $(INSTALLED_PRODUCTIMAGE_TARGET) \ + $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ + $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ + $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ + $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ + $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ + $(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ + $(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ + $(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ + $(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ + $(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts + +# Run host_init_verifier on the partition staging directories. +$(host_init_verifier_output): $(HOST_INIT_VERIFIER) + $(HOST_INIT_VERIFIER) \ + -p $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ + -p $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ + -p $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ + -p $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ + -p $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ + --property-contexts=$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ + --property-contexts=$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts \ + --out_system $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM) \ + --out_system_ext $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_EXT) \ + --out_vendor $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR) \ + --out_odm $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM) \ + --out_product $(PRODUCT_OUT)/$(TARGET_COPY_OUT_PRODUCT) \ + > $@ + +$(call dist-for-goals,droidcore-unbundled,$(host_init_verifier_output)) diff --git a/make/core/tasks/ide.mk b/make/core/tasks/ide.mk new file mode 100644 index 0000000..a3aa0cd --- /dev/null +++ b/make/core/tasks/ide.mk @@ -0,0 +1,61 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define filter-ide-goals +$(strip $(filter $(1)-%,$(MAKECMDGOALS))) +endef + +define filter-ide-modules +$(strip $(subst -,$(space),$(patsubst $(1)-%,%,$(2)))) +endef + +# eclipse +eclipse_project_goals := $(call filter-ide-goals,ECLIPSE) +ifdef eclipse_project_goals + ifneq ($(words $(eclipse_project_goals)),1) + $(error Only one ECLIPSE- goal may be specified: $(eclipse_project_goals)) + endif + eclipse_project_modules := $(call filter-ide-modules,ECLIPSE,$(eclipse_project_goals)) + + ifneq ($(filter lunch,$(eclipse_project_modules)),) + eclipse_project_modules := $(filter-out lunch,$(eclipse_project_modules)) + installed_modules := $(foreach m,$(ALL_DEFAULT_INSTALLED_MODULES),\ + $(INSTALLABLE_FILES.$(m).MODULE)) + java_modules := $(foreach m,$(installed_modules),\ + $(if $(filter JAVA_LIBRARIES APPS,$(ALL_MODULES.$(m).CLASS)),$(m),)) + eclipse_project_modules := $(sort $(eclipse_project_modules) $(java_modules)) + endif + + source_paths := $(foreach m,$(eclipse_project_modules),$(ALL_MODULES.$(m).PATH)) \ + $(foreach m,$(eclipse_project_modules),$(ALL_MODULES.$(m).INTERMEDIATE_SOURCE_DIR)) + source_paths := $(sort $(source_paths)) + +.classpath: PRIVATE_MODULES := $(eclipse_project_modules) +.classpath: PRIVATE_DIRS := $(source_paths) + +# the mess below with ./src tries to guess whether the src +$(eclipse_project_goals): .classpath +.classpath: FORCE + $(hide) echo Generating .classpath for eclipse + $(hide) echo '' > $@ + $(hide) for p in $(PRIVATE_DIRS) ; do \ + echo -n ' ' >> $@ ; \ + done + $(hide) echo '' >> $@ +endif + diff --git a/make/core/tasks/module-info.mk b/make/core/tasks/module-info.mk new file mode 100644 index 0000000..8097535 --- /dev/null +++ b/make/core/tasks/module-info.mk @@ -0,0 +1,39 @@ +# Print a list of the modules that could be built +# Currently runtime_dependencies only include the runtime libs information for cc binaries. + +MODULE_INFO_JSON := $(PRODUCT_OUT)/module-info.json + +$(MODULE_INFO_JSON): + @echo Generating $@ + $(hide) echo -ne '{\n ' > $@ + $(hide) echo -ne $(foreach m, $(sort $(ALL_MODULES)), \ + ' "$(m)": {' \ + '"class": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASS)),"$(w)", )], ' \ + '"path": [$(foreach w,$(sort $(ALL_MODULES.$(m).PATH)),"$(w)", )], ' \ + '"tags": [$(foreach w,$(sort $(ALL_MODULES.$(m).TAGS)),"$(w)", )], ' \ + '"installed": [$(foreach w,$(sort $(ALL_MODULES.$(m).INSTALLED)),"$(w)", )], ' \ + '"compatibility_suites": [$(foreach w,$(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES)),"$(w)", )], ' \ + '"auto_test_config": [$(ALL_MODULES.$(m).auto_test_config)], ' \ + '"module_name": "$(ALL_MODULES.$(m).MODULE_NAME)", ' \ + '"test_config": [$(foreach w,$(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)),"$(w)", )], ' \ + '"dependencies": [$(foreach w,$(sort $(ALL_DEPS.$(m).ALL_DEPS)),"$(w)", )], ' \ + '"shared_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SHARED_LIBS)),"$(w)", )], ' \ + '"system_shared_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS)),"$(w)", )], ' \ + '"srcs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SRCS)),"$(w)", )], ' \ + '"srcjars": [$(foreach w,$(sort $(ALL_MODULES.$(m).SRCJARS)),"$(w)", )], ' \ + '"classes_jar": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASSES_JAR)),"$(w)", )], ' \ + '"test_mainline_modules": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES)),"$(w)", )], ' \ + '"is_unit_test": "$(ALL_MODULES.$(m).IS_UNIT_TEST)", ' \ + '"data": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_DATA)),"$(w)", )], ' \ + '"runtime_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES)),"$(w)", )], ' \ + '"data_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_DATA_BINS)),"$(w)", )], ' \ + '"supported_variants": [$(foreach w,$(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS)),"$(w)", )], ' \ + '},\n' \ + ) | sed -e 's/, *\]/]/g' -e 's/, *\}/ }/g' -e '$$s/,$$//' >> $@ + $(hide) echo '}' >> $@ + + +droidcore-unbundled: $(MODULE_INFO_JSON) + +$(call dist-for-goals, general-tests, $(MODULE_INFO_JSON)) +$(call dist-for-goals, droidcore-unbundled, $(MODULE_INFO_JSON)) diff --git a/make/core/tasks/mts.mk b/make/core/tasks/mts.mk new file mode 100644 index 0000000..e084856 --- /dev/null +++ b/make/core/tasks/mts.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ifneq ($(wildcard test/mts/README.md),) + +mts_test_suites := +mts_test_suites += mts + +$(foreach module, $(mts_modules), $(eval mts_test_suites += mts-$(module))) + +$(foreach suite, $(mts_test_suites), \ + $(eval test_suite_name := $(suite)) \ + $(eval test_suite_tradefed := mts-tradefed) \ + $(eval test_suite_readme := test/mts/README.md) \ + $(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \ + $(eval .PHONY: $(suite)) \ + $(eval $(suite): $(compatibility_zip)) \ + $(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \ +) + +endif diff --git a/make/core/tasks/oem_image.mk b/make/core/tasks/oem_image.mk new file mode 100644 index 0000000..134be01 --- /dev/null +++ b/make/core/tasks/oem_image.mk @@ -0,0 +1,49 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# We build oem.img only if it's asked for. +ifneq ($(filter $(MAKECMDGOALS),oem_image),) +ifndef BOARD_OEMIMAGE_PARTITION_SIZE +$(error BOARD_OEMIMAGE_PARTITION_SIZE is not set.) +endif + +INTERNAL_OEMIMAGE_FILES := \ + $(filter $(TARGET_OUT_OEM)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) + +oemimage_intermediates := \ + $(call intermediates-dir-for,PACKAGING,oem) +BUILT_OEMIMAGE_TARGET := $(PRODUCT_OUT)/oem.img +# We just build this directly to the install location. +INSTALLED_OEMIMAGE_TARGET := $(BUILT_OEMIMAGE_TARGET) + +$(INSTALLED_OEMIMAGE_TARGET) : $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_OEMIMAGE_FILES) + $(call pretty,"Target oem fs image: $@") + @mkdir -p $(TARGET_OUT_OEM) + @mkdir -p $(oemimage_intermediates) && rm -rf $(oemimage_intermediates)/oem_image_info.txt + $(call generate-image-prop-dictionary, $(oemimage_intermediates)/oem_image_info.txt,oem,skip_fsck=true) + PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(TARGET_OUT_OEM) $(oemimage_intermediates)/oem_image_info.txt $@ $(TARGET_OUT) + $(call assert-max-image-size,$@,$(BOARD_OEMIMAGE_PARTITION_SIZE)) + +.PHONY: oem_image +oem_image : $(INSTALLED_OEMIMAGE_TARGET) +$(call dist-for-goals, oem_image, $(INSTALLED_OEMIMAGE_TARGET)) + +$(call declare-1p-container,$(INSTALLED_OEMIMAGE_TARGET),) +$(call declare-container-license-deps,$(INSTALLED_OEMIMAGE_TARGET),$(INTERNAL_USERIMAGE_DEPS) $(INTERNAL_OEMIMAGE_FILES),$(INSTALLED_OEMIMAGE_TARGET):) + +endif # oem_image in $(MAKECMDGOALS) diff --git a/make/core/tasks/owners.mk b/make/core/tasks/owners.mk new file mode 100644 index 0000000..806b8ee --- /dev/null +++ b/make/core/tasks/owners.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Create an artifact to include TEST_MAPPING files in source tree. + +.PHONY: owners + +intermediates := $(call intermediates-dir-for,PACKAGING,owners) +owners_zip := $(intermediates)/owners.zip +owners_list := $(OUT_DIR)/.module_paths/OWNERS.list +owners := $(file <$(owners_list)) +$(owners_zip) : PRIVATE_owners := $(subst $(newline),\n,$(owners)) + +$(owners_zip) : $(owners) $(SOONG_ZIP) + @echo "Building artifact to include OWNERS files." + rm -rf $@ + echo -e "$(PRIVATE_owners)" > $@.list + $(SOONG_ZIP) -o $@ -C . -l $@.list + rm -f $@.list + +owners : $(owners_zip) + +$(call dist-for-goals, general-tests, $(owners_zip)) + +$(call declare-0p-target,$(owners_zip)) diff --git a/make/core/tasks/platform_availability_check.mk b/make/core/tasks/platform_availability_check.mk new file mode 100644 index 0000000..1524758 --- /dev/null +++ b/make/core/tasks/platform_availability_check.mk @@ -0,0 +1,61 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Check whether there is any module that isn't available for platform +# is installed to the platform. + +# Skip for unbundled builds that don't produce a platform image. +ifeq (,$(TARGET_BUILD_UNBUNDLED)) + +# Filter FAKE and NON_INSTALLABLE modules out and then collect those are not +# available for platform +_modules_not_available_for_platform := \ +$(strip $(foreach m,$(product_MODULES),\ + $(if $(filter-out FAKE,$(ALL_MODULES.$(m).CLASS)),\ + $(if $(ALL_MODULES.$(m).INSTALLED),\ + $(if $(filter true,$(ALL_MODULES.$(m).NOT_AVAILABLE_FOR_PLATFORM)),\ + $(m)))))) + +ifndef ALLOW_MISSING_DEPENDENCIES + _violators_with_path := $(foreach m,$(sort $(_modules_not_available_for_platform)),\ + $(m):$(word 1,$(ALL_MODULES.$(m).PATH))\ + ) + + $(call maybe-print-list-and-error,$(_violators_with_path),\ +Following modules are requested to be installed. But are not available \ +for platform because they do not have "//apex_available:platform" or \ +they depend on other modules that are not available for platform) + +else + +# Don't error out immediately when ALLOW_MISSING_DEPENDENCIES is set. +# Instead, add a dependency on a rule that prints the error message. + define not_available_for_platform_rule + not_installable_file := $(patsubst $(OUT_DIR)/%,$(OUT_DIR)/NOT_AVAILABLE_FOR_PLATFORM/%,$(1)) + $(1): $$(not_installable_file) + $$(not_installable_file): + $(call echo-error,$(2),Module is requested to be installed but is not \ +available for platform because it does not have "//apex_available:platform" or \ +it depends on other modules that are not available for platform.) + exit 1 + endef + + $(foreach m,$(_modules_not_available_for_platform),\ + $(foreach i,$(filter-out $(HOST_OUT)/%,$(ALL_MODULES.$(m).INSTALLED)),\ + $(eval $(call not_available_for_platform_rule,$(i),$(m))))) +endif + +endif diff --git a/make/core/tasks/recovery_snapshot.mk b/make/core/tasks/recovery_snapshot.mk new file mode 100644 index 0000000..525273b --- /dev/null +++ b/make/core/tasks/recovery_snapshot.mk @@ -0,0 +1,34 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +current_makefile := $(lastword $(MAKEFILE_LIST)) + +# RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a recovery snapshot. +ifeq ($(RECOVERY_SNAPSHOT_VERSION),current) + +.PHONY: recovery-snapshot +recovery-snapshot: $(SOONG_RECOVERY_SNAPSHOT_ZIP) + +$(call dist-for-goals, recovery-snapshot, $(SOONG_RECOVERY_SNAPSHOT_ZIP)) + +else # RECOVERY_SNAPSHOT_VERSION is NOT set to 'current' + +.PHONY: recovery-snapshot +recovery-snapshot: PRIVATE_MAKEFILE := $(current_makefile) +recovery-snapshot: + $(call echo-error,$(PRIVATE_MAKEFILE),\ + "CANNOT generate Recovery snapshot. RECOVERY_SNAPSHOT_VERSION must be set to 'current'.") + exit 1 + +endif # RECOVERY_SNAPSHOT_VERSION diff --git a/make/core/tasks/sdk-addon.mk b/make/core/tasks/sdk-addon.mk new file mode 100644 index 0000000..5097f12 --- /dev/null +++ b/make/core/tasks/sdk-addon.mk @@ -0,0 +1,150 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.PHONY: sdk_addon + +# If they didn't define PRODUCT_SDK_ADDON_NAME, then we won't define +# any of these rules. +addon_name := $(PRODUCT_SDK_ADDON_NAME) +ifneq ($(addon_name),) + +addon_dir_leaf := $(addon_name)-$(FILE_NAME_TAG)-$(INTERNAL_SDK_HOST_OS_NAME) +addon_dir_img := $(addon_dir_leaf)-img +intermediates := $(HOST_OUT_INTERMEDIATES)/SDK_ADDON/$(addon_name)_intermediates +full_target := $(HOST_OUT_SDK_ADDON)/$(addon_dir_leaf).zip +full_target_img := $(HOST_OUT_SDK_ADDON)/$(addon_dir_img).zip +staging := $(intermediates) + +sdk_addon_deps := +files_to_copy := + +define stub-addon-jar-file +$(subst .jar,_stub-addon.jar,$(1)) +endef + +define stub-addon-jar +$(call stub-addon-jar-file,$(1)): $(1) | mkstubs + $(info Stubbing addon jar using $(PRODUCT_SDK_ADDON_STUB_DEFS)) + $(hide) $(JAVA) -jar $(call module-installed-files,mkstubs) $(if $(hide),,--v) \ + "$$<" "$$@" @$(PRODUCT_SDK_ADDON_STUB_DEFS) +endef + +# Files that are built and then copied into the sdk-addon +ifneq ($(PRODUCT_SDK_ADDON_COPY_MODULES),) +$(foreach cf,$(PRODUCT_SDK_ADDON_COPY_MODULES), \ + $(eval _src := $(call module-stubs-files,$(call word-colon,1,$(cf)))) \ + $(eval $(call stub-addon-jar,$(_src))) \ + $(eval _src := $(call stub-addon-jar-file,$(_src))) \ + $(if $(_src),,$(eval $(error Unknown or unlinkable module: $(call word-colon,1,$(cf)). Requested by $(INTERNAL_PRODUCT)))) \ + $(eval _dest := $(call word-colon,2,$(cf))) \ + $(eval files_to_copy += $(addon_dir_leaf):$(_src):$(_dest)) \ + ) +endif + +# Files that are copied directly into the sdk-addon +ifneq ($(PRODUCT_SDK_ADDON_COPY_FILES),) +$(foreach cf,$(PRODUCT_SDK_ADDON_COPY_FILES), \ + $(eval _src := $(call word-colon,1,$(cf))) \ + $(eval _dest := $(call word-colon,2,$(cf))) \ + $(if $(findstring images/,$(_dest)), $(eval _root := $(addon_dir_img)), $(eval _root := $(addon_dir_leaf))) \ + $(eval files_to_copy += $(_root):$(_src):$(_dest)) \ + ) +endif + +# Files copied in the system-image directory +files_to_copy += \ + $(addon_dir_img):$(INSTALLED_QEMU_SYSTEMIMAGE):images/$(TARGET_CPU_ABI)/system.img \ + $(addon_dir_img):$(INSTALLED_QEMU_VENDORIMAGE):images/$(TARGET_CPU_ABI)/vendor.img \ + $(addon_dir_img):$(INSTALLED_QEMU_RAMDISKIMAGE):images/$(TARGET_CPU_ABI)/ramdisk.img \ + $(addon_dir_img):$(PRODUCT_OUT)/system/build.prop:images/$(TARGET_CPU_ABI)/build.prop \ + $(addon_dir_img):device/generic/goldfish/data/etc/userdata.img:images/$(TARGET_CPU_ABI)/userdata.img \ + $(addon_dir_img):$(target_notice_file_txt):images/$(TARGET_CPU_ABI)/NOTICE.txt \ + $(addon_dir_img):$(PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP):images/source.properties + + +ifeq ($(BOARD_AVB_ENABLE),true) +files_to_copy += \ + $(addon_dir_img):$(QEMU_VERIFIED_BOOT_PARAMS):images/$(TARGET_CPU_ABI)/VerifiedBootParams.textproto +endif + +# Generate rules to copy the requested files +$(foreach cf,$(files_to_copy), \ + $(eval _root := $(call word-colon,1,$(cf))) \ + $(eval _src := $(call word-colon,2,$(cf))) \ + $(eval _dest := $(call append-path,$(call append-path,$(staging),$(_root)),$(call word-colon,3,$(cf)))) \ + $(eval $(call copy-one-file,$(_src),$(_dest))) \ + $(eval sdk_addon_deps += $(_dest)) \ + ) + +# The system-image source.properties is a template that we directly expand in-place +addon_img_source_prop := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI)/source.properties +sdk_addon_deps += $(addon_img_source_prop) + +$(addon_img_source_prop): $(PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP) + @echo Generate $@ + $(hide) mkdir -p $(dir $@) + $(hide) sed \ + -e 's/$${PLATFORM_VERSION}/$(PLATFORM_VERSION)/' \ + -e 's/$${PLATFORM_SDK_VERSION}/$(PLATFORM_SDK_VERSION)/' \ + -e 's/$${PLATFORM_VERSION_CODENAME}/$(subst REL,,$(PLATFORM_VERSION_CODENAME))/' \ + -e 's/$${TARGET_ARCH}/$(TARGET_ARCH)/' \ + -e 's/$${TARGET_CPU_ABI}/$(TARGET_CPU_ABI)/' \ + $< > $@ && sed -i -e '/^AndroidVersion.CodeName=\s*$$/d' $@ + + +# We don't know about all of the docs files, so depend on the timestamps for +# them, and record the directories, and the packaging rule will just copy the +# whole thing. +doc_modules := $(PRODUCT_SDK_ADDON_DOC_MODULES) +sdk_addon_deps += $(foreach dm, $(doc_modules), $(call doc-timestamp-for, $(dm))) +$(full_target): PRIVATE_DOCS_DIRS := $(addprefix $(OUT_DOCS)/, $(doc_modules)) + +$(full_target): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_leaf)) + +$(full_target): $(sdk_addon_deps) | $(SOONG_ZIP) + @echo Packaging SDK Addon: $@ + $(hide) mkdir -p $(PRIVATE_STAGING_DIR)/docs + $(hide) for d in $(PRIVATE_DOCS_DIRS); do \ + cp -R $$d $(PRIVATE_STAGING_DIR)/docs ;\ + done + $(hide) mkdir -p $(dir $@) + $(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR) + +$(full_target_img): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI) +$(full_target_img): $(full_target) $(addon_img_source_prop) | $(SOONG_ZIP) + @echo Packaging SDK Addon System-Image: $@ + $(hide) mkdir -p $(dir $@) + cp -R $(PRODUCT_OUT)/data $(PRIVATE_STAGING_DIR)/data + $(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR) + + +sdk_addon: $(full_target) $(full_target_img) + +ifneq ($(sdk_repo_goal),) +# If we're building the sdk_repo, keep the name of the addon zip +# around so that development/build/tools/sdk_repo.mk can dist it +# at the appropriate location. +ADDON_SDK_ZIP := $(full_target) +ADDON_SDK_IMG_ZIP := $(full_target_img) +else +# When not building an sdk_repo, just dist the addon zip file +# as-is. +$(call dist-for-goals, sdk_addon, $(full_target)) +endif + +else # addon_name +ifneq ($(filter sdk_addon,$(MAKECMDGOALS)),) +$(error Trying to build sdk_addon, but product '$(INTERNAL_PRODUCT)' does not define one) +endif +endif # addon_name diff --git a/make/core/tasks/sts-lite.mk b/make/core/tasks/sts-lite.mk new file mode 100644 index 0000000..65c65c3 --- /dev/null +++ b/make/core/tasks/sts-lite.mk @@ -0,0 +1,41 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ifneq ($(wildcard test/sts/README-sts-sdk.md),) +test_suite_name := sts-lite +test_suite_tradefed := sts-tradefed +test_suite_readme := test/sts/README-sts-sdk.md +sts_sdk_zip := $(HOST_OUT)/$(test_suite_name)/sts-sdk.zip + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +sts_sdk_samples := $(call intermediates-dir-for,ETC,sts-sdk-samples.zip)/sts-sdk-samples.zip + +$(sts_sdk_zip): STS_LITE_ZIP := $(compatibility_zip) +$(sts_sdk_zip): STS_SDK_SAMPLES := $(sts_sdk_samples) +$(sts_sdk_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(sts_sdk_samples) + rm -f $@ $(STS_LITE_ZIP)_filtered + $(ZIP2ZIP) -i $(STS_LITE_ZIP) -o $(STS_LITE_ZIP)_filtered \ + -x android-sts-lite/tools/sts-tradefed-tests.jar \ + 'android-sts-lite/tools/*:sts-test/libs/' \ + 'android-sts-lite/testcases/*:sts-test/utils/' \ + 'android-sts-lite/jdk/**/*:sts-test/jdk/' + $(MERGE_ZIPS) $@ $(STS_LITE_ZIP)_filtered $(STS_SDK_SAMPLES) + rm -f $(STS_LITE_ZIP)_filtered + +.PHONY: sts-sdk +sts-sdk: $(sts_sdk_zip) +$(call dist-for-goals, sts-sdk, $(sts_sdk_zip)) + +endif diff --git a/make/core/tasks/sts.mk b/make/core/tasks/sts.mk new file mode 100644 index 0000000..0c33e1c --- /dev/null +++ b/make/core/tasks/sts.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ifneq ($(wildcard test/sts/README.md),) +test_suite_name := sts +test_suite_tradefed := sts-tradefed +test_suite_readme := test/sts/README.md + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: sts +sts: $(compatibility_zip) +$(call dist-for-goals, sts, $(compatibility_zip)) +endif diff --git a/make/core/tasks/test_mapping.mk b/make/core/tasks/test_mapping.mk new file mode 100644 index 0000000..0b0c93c --- /dev/null +++ b/make/core/tasks/test_mapping.mk @@ -0,0 +1,40 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Create an artifact to include TEST_MAPPING files in source tree. Also include +# a file (out/disabled-presubmit-tests) containing the tests that should be +# skipped in presubmit check. + +.PHONY: test_mapping + +intermediates := $(call intermediates-dir-for,PACKAGING,test_mapping) +test_mappings_zip := $(intermediates)/test_mappings.zip +test_mapping_list := $(OUT_DIR)/.module_paths/TEST_MAPPING.list +test_mappings := $(file <$(test_mapping_list)) +$(test_mappings_zip) : PRIVATE_test_mappings := $(subst $(newline),\n,$(test_mappings)) +$(test_mappings_zip) : PRIVATE_all_disabled_presubmit_tests := $(ALL_DISABLED_PRESUBMIT_TESTS) + +$(test_mappings_zip) : $(test_mappings) $(SOONG_ZIP) + @echo "Building artifact to include TEST_MAPPING files and tests to skip in presubmit check." + rm -rf $@ $(dir $@)/disabled-presubmit-tests + echo $(sort $(PRIVATE_all_disabled_presubmit_tests)) | tr " " "\n" > $(dir $@)/disabled-presubmit-tests + echo -e "$(PRIVATE_test_mappings)" > $@.list + $(SOONG_ZIP) -o $@ -C . -l $@.list -C $(dir $@) -f $(dir $@)/disabled-presubmit-tests + rm -f $@.list $(dir $@)/disabled-presubmit-tests + +test_mapping : $(test_mappings_zip) + +$(call dist-for-goals, dist_files test_mapping,$(test_mappings_zip)) + +$(call declare-1p-target,$(test_mappings_zip),) diff --git a/make/core/tasks/tools/build_custom_image.mk b/make/core/tasks/tools/build_custom_image.mk new file mode 100644 index 0000000..f9ae2c1 --- /dev/null +++ b/make/core/tasks/tools/build_custom_image.mk @@ -0,0 +1,175 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +# Define rule to build one custom image. +# Input variables: my_custom_imag_makefile + +$(call clear-var-list, $(custom_image_parameter_variables)) + +include $(my_custom_imag_makefile) + +my_custom_image_name := $(basename $(notdir $(my_custom_imag_makefile))) + +intermediates := $(call intermediates-dir-for,PACKAGING,$(my_custom_image_name)) +my_built_custom_image := $(intermediates)/$(my_custom_image_name).img +my_staging_dir := $(intermediates)/$(CUSTOM_IMAGE_MOUNT_POINT) + +# Collect CUSTOM_IMAGE_MODULES's installd files and their PICKUP_FILES. +my_built_modules := +my_copy_pairs := +my_pickup_files := + +$(foreach m,$(CUSTOM_IMAGE_MODULES),\ + $(eval _pickup_files := $(strip $(ALL_MODULES.$(m).PICKUP_FILES)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\ + $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\ + $(if $(_pickup_files)$(_built_files),,\ + $(warning Unknown installed file for module '$(m)'))\ + $(eval my_pickup_files += $(_pickup_files))\ + $(foreach i, $(_built_files),\ + $(eval bui_ins := $(subst :,$(space),$(i)))\ + $(eval ins := $(word 2,$(bui_ins)))\ + $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\ + $(eval bui := $(word 1,$(bui_ins)))\ + $(eval my_built_modules += $(bui))\ + $(eval my_copy_dest := $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))\ + $(eval my_copy_dest := $(subst /,$(space),$(my_copy_dest)))\ + $(eval my_copy_dest := $(wordlist 2,999,$(my_copy_dest)))\ + $(eval my_copy_dest := $(subst $(space),/,$(my_copy_dest)))\ + $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\ + )) + +my_kernel_module_copy_files := +my_custom_image_modules_var := BOARD_$(strip $(call to-upper,$(my_custom_image_name)))_KERNEL_MODULES +ifdef $(my_custom_image_modules_var) +$(foreach kmod,\ + $(call build-image-kernel-modules,$($(my_custom_image_modules_var)),$(my_staging_dir),$(CUSTOM_IMAGE_MOUNT_POINT),$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)),$($(my_custom_image_modules_var)),modules.load,,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)_stripped)),\ + $(eval pair := $(subst :,$(space),$(kmod)))\ + $(eval my_kernel_module_copy_files += $(word 1,$(pair)):$(subst $(my_staging_dir)/,,$(word 2,$(pair))))) +endif + +# Collect CUSTOM_IMAGE_COPY_FILES. +my_image_copy_files := +$(foreach f,$(CUSTOM_IMAGE_COPY_FILES) $(my_kernel_module_copy_files),\ + $(eval pair := $(subst :,$(space),$(f)))\ + $(eval src := $(word 1,$(pair)))\ + $(eval my_image_copy_files += $(src))\ + $(eval my_copy_pairs += $(src):$(my_staging_dir)/$(word 2,$(pair)))) + +ifdef CUSTOM_IMAGE_AVB_KEY_PATH +ifndef CUSTOM_IMAGE_AVB_ALGORITHM + $(error CUSTOM_IMAGE_AVB_ALGORITHM is not defined) +endif +ifndef CUSTOM_IMAGE_AVB_ROLLBACK_INDEX + $(error CUSTOM_IMAGE_AVB_ROLLBACK_INDEX is not defined) +endif +# set rollback_index via footer args +CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS += --rollback_index $(CUSTOM_IMAGE_AVB_ROLLBACK_INDEX) +CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(CUSTOM_IMAGE_AVB_ROLLBACK_INDEX) +endif + +$(my_built_custom_image): PRIVATE_INTERMEDIATES := $(intermediates) +$(my_built_custom_image): PRIVATE_MOUNT_POINT := $(CUSTOM_IMAGE_MOUNT_POINT) +$(my_built_custom_image): PRIVATE_PARTITION_SIZE := $(CUSTOM_IMAGE_PARTITION_SIZE) +$(my_built_custom_image): PRIVATE_FILE_SYSTEM_TYPE := $(CUSTOM_IMAGE_FILE_SYSTEM_TYPE) +$(my_built_custom_image): PRIVATE_STAGING_DIR := $(my_staging_dir) +$(my_built_custom_image): PRIVATE_COPY_PAIRS := $(my_copy_pairs) +$(my_built_custom_image): PRIVATE_PICKUP_FILES := $(my_pickup_files) +$(my_built_custom_image): PRIVATE_SELINUX := $(CUSTOM_IMAGE_SELINUX) +$(my_built_custom_image): PRIVATE_SUPPORT_VERITY := $(CUSTOM_IMAGE_SUPPORT_VERITY) +$(my_built_custom_image): PRIVATE_SUPPORT_VERITY_FEC := $(CUSTOM_IMAGE_SUPPORT_VERITY_FEC) +$(my_built_custom_image): PRIVATE_VERITY_KEY := $(PRODUCT_VERITY_SIGNING_KEY) +$(my_built_custom_image): PRIVATE_VERITY_BLOCK_DEVICE := $(CUSTOM_IMAGE_VERITY_BLOCK_DEVICE) +$(my_built_custom_image): PRIVATE_DICT_FILE := $(CUSTOM_IMAGE_DICT_FILE) +$(my_built_custom_image): PRIVATE_AVB_AVBTOOL := $(AVBTOOL) +$(my_built_custom_image): PRIVATE_AVB_KEY_PATH := $(CUSTOM_IMAGE_AVB_KEY_PATH) +$(my_built_custom_image): PRIVATE_AVB_ALGORITHM:= $(CUSTOM_IMAGE_AVB_ALGORITHM) +$(my_built_custom_image): PRIVATE_AVB_HASH_ENABLE := $(CUSTOM_IMAGE_AVB_HASH_ENABLE) +$(my_built_custom_image): PRIVATE_AVB_ADD_HASH_FOOTER_ARGS := $(CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS) +$(my_built_custom_image): PRIVATE_AVB_HASHTREE_ENABLE := $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE) +$(my_built_custom_image): PRIVATE_AVB_ADD_HASHTREE_FOOTER_ARGS := $(CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS) +ifeq (true,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE))) + $(my_built_custom_image): $(AVBTOOL) +else ifneq (,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE))) + $(error Cannot set both CUSTOM_IMAGE_AVB_HASH_ENABLE and CUSTOM_IMAGE_AVB_HASHTREE_ENABLE to true) +endif +ifeq (true,$(CUSTOM_IMAGE_SUPPORT_VERITY_FEC)) + $(my_built_custom_image): $(FEC) +endif +$(my_built_custom_image): $(INTERNAL_USERIMAGES_DEPS) $(my_built_modules) $(my_image_copy_files) $(my_custom_image_modules_dep) \ + $(CUSTOM_IMAGE_DICT_FILE) + @echo "Build image $@" + $(hide) rm -rf $(PRIVATE_INTERMEDIATES) && mkdir -p $(PRIVATE_INTERMEDIATES) + $(hide) rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR) + # Copy all the files. + $(hide) $(foreach p,$(PRIVATE_COPY_PAIRS),\ + $(eval pair := $(subst :,$(space),$(p)))\ + mkdir -p $(dir $(word 2,$(pair)));\ + cp -Rf $(word 1,$(pair)) $(word 2,$(pair));) + $(if $($(PRIVATE_PICKUP_FILES)),$(hide) cp -Rf $(PRIVATE_PICKUP_FILES) $(PRIVATE_STAGING_DIR)) + # Generate the dict. + $(hide) echo "# For all accepted properties, see BuildImage() in tools/releasetools/build_image.py" > $(PRIVATE_INTERMEDIATES)/image_info.txt + $(hide) echo "mount_point=$(PRIVATE_MOUNT_POINT)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt + $(hide) echo "partition_name=$(PRIVATE_MOUNT_POINT)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt + $(hide) echo "fs_type=$(PRIVATE_FILE_SYSTEM_TYPE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt + $(hide) echo "partition_size=$(PRIVATE_PARTITION_SIZE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt + $(hide) echo "ext_mkuserimg=$(notdir $(MKEXTUSERIMG))" >> $(PRIVATE_INTERMEDIATES)/image_info.txt + $(if $(PRIVATE_SELINUX),$(hide) echo "selinux_fc=$(SELINUX_FC)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(if $(PRIVATE_SUPPORT_VERITY),\ + $(hide) echo "verity=$(PRIVATE_SUPPORT_VERITY)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + echo "verity_key=$(PRIVATE_VERITY_KEY)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + echo "verity_signer_cmd=$(VERITY_SIGNER)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + echo "verity_block_device=$(PRIVATE_VERITY_BLOCK_DEVICE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(if $(PRIVATE_SUPPORT_VERITY_FEC),\ + $(hide) echo "verity_fec=$(PRIVATE_SUPPORT_VERITY_FEC)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo "verity_disable=true" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(hide) echo "avb_avbtool=$(PRIVATE_AVB_AVBTOOL)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt + $(if $(PRIVATE_AVB_KEY_PATH),\ + $(hide) echo "avb_key_path=$(PRIVATE_AVB_KEY_PATH)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + echo "avb_algorithm=$(PRIVATE_AVB_ALGORITHM)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(if $(PRIVATE_AVB_HASH_ENABLE),\ + $(hide) echo "avb_hash_enable=$(PRIVATE_AVB_HASH_ENABLE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + echo "avb_add_hash_footer_args=$(PRIVATE_AVB_ADD_HASH_FOOTER_ARGS)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(if $(PRIVATE_AVB_HASHTREE_ENABLE),\ + $(hide) echo "avb_hashtree_enable=$(PRIVATE_AVB_HASHTREE_ENABLE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + echo "avb_add_hashtree_footer_args=$(PRIVATE_AVB_ADD_HASHTREE_FOOTER_ARGS)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + $(if $(PRIVATE_DICT_FILE),\ + $(hide) echo "# Properties from $(PRIVATE_DICT_FILE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ + cat $(PRIVATE_DICT_FILE) >> $(PRIVATE_INTERMEDIATES)/image_info.txt) + # Generate the image. + $(if $(filter oem,$(PRIVATE_MOUNT_POINT)), \ + $(hide) echo "oem.buildnumber=$(BUILD_NUMBER_FROM_FILE)" >> $(PRIVATE_STAGING_DIR)/oem.prop) + $(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ + $(BUILD_IMAGE) \ + $(PRIVATE_STAGING_DIR) $(PRIVATE_INTERMEDIATES)/image_info.txt $@ $(TARGET_OUT) + +my_installed_custom_image := $(PRODUCT_OUT)/$(notdir $(my_built_custom_image)) +$(my_installed_custom_image) : $(my_built_custom_image) + $(call copy-file-to-new-target-with-cp) + +.PHONY: $(my_custom_image_name) +custom_images $(my_custom_image_name) : $(my_installed_custom_image) + +# Archive the built image. +$(call dist-for-goals, $(my_custom_image_name) custom_images,$(my_installed_custom_image)) + +my_staging_dir := +my_built_modules := +my_copy_dest := +my_copy_pairs := +my_pickup_files := diff --git a/make/core/tasks/tools/compatibility.mk b/make/core/tasks/tools/compatibility.mk new file mode 100644 index 0000000..cfae490 --- /dev/null +++ b/make/core/tasks/tools/compatibility.mk @@ -0,0 +1,147 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Package up a compatibility test suite in a zip file. +# +# Input variables: +# test_suite_name: the name of this test suite eg. cts +# test_suite_tradefed: the name of this test suite's tradefed wrapper +# test_suite_dynamic_config: the path to this test suite's dynamic configuration file +# test_suite_readme: the path to a README file for this test suite +# test_suite_prebuilt_tools: the set of prebuilt tools to be included directly +# in the 'tools' subdirectory of the test suite. +# test_suite_tools: the set of tools for this test suite +# +# Output variables: +# compatibility_zip: the path to the output zip file. + +test_suite_subdir := android-$(test_suite_name) +out_dir := $(HOST_OUT)/$(test_suite_name)/$(test_suite_subdir) +test_artifacts := $(COMPATIBILITY.$(test_suite_name).FILES) +test_tools := $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/tradefed-test-framework.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/loganalysis.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/compatibility-tradefed.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed).jar \ + $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed)-tests.jar \ + $(HOST_OUT_EXECUTABLES)/$(test_suite_tradefed) \ + $(test_suite_readme) + +$(foreach f,$(test_suite_readme),$(if $(strip $(ALL_TARGETS.$(f).META_LIC)),,$(eval ALL_TARGETS.$(f).META_LIC := $(module_license_metadata)))) + +test_tools += $(test_suite_tools) + +# The JDK to package into the test suite zip file. Always package the linux JDK. +test_suite_jdk_dir := $(ANDROID_JAVA_HOME)/../linux-x86 +test_suite_jdk := $(call intermediates-dir-for,PACKAGING,$(test_suite_name)_jdk,HOST)/jdk.zip +$(test_suite_jdk): PRIVATE_JDK_DIR := $(test_suite_jdk_dir) +$(test_suite_jdk): PRIVATE_SUBDIR := $(test_suite_subdir) +$(test_suite_jdk): $(shell find $(test_suite_jdk_dir) -type f | sort) +$(test_suite_jdk): $(SOONG_ZIP) + $(SOONG_ZIP) -o $@ -P $(PRIVATE_SUBDIR)/jdk -C $(PRIVATE_JDK_DIR) -D $(PRIVATE_JDK_DIR) + +$(call declare-license-metadata,$(test_suite_jdk),SPDX-license-identifier-GPL-2.0-with-classpath-exception,restricted,\ + $(test_suite_jdk_dir)/legal/java.base/LICENSE,JDK,prebuilts/jdk/$(notdir $(patsubst %/,%,$(dir $(test_suite_jdk_dir))))) + + +# Include host shared libraries +host_shared_libs := $(call copy-many-files, $(COMPATIBILITY.$(test_suite_name).HOST_SHARED_LIBRARY.FILES)) + +$(if $(strip $(host_shared_libs)),\ + $(foreach p,$(COMPATIBILITY.$(test_suite_name).HOST_SHARED_LIBRARY.FILES),\ + $(eval _src := $(call word-colon,1,$(p)))\ + $(eval _dst := $(call word-colon,2,$(p)))\ + $(if $(strip $(ALL_TARGETS.$(_src).META_LIC)),\ + $(eval ALL_TARGETS.$(_dst).META_LIC := $(ALL_TARGETS.$(_src).META_LIC)),\ + $(warning $(_src) has no license metadata for $(_dst))\ + )\ + )\ +) + +compatibility_zip_deps := \ + $(test_artifacts) \ + $(test_tools) \ + $(test_suite_prebuilt_tools) \ + $(test_suite_dynamic_config) \ + $(test_suite_jdk) \ + $(MERGE_ZIPS) \ + $(SOONG_ZIP) \ + $(host_shared_libs) \ + $(test_suite_extra_deps) \ + +compatibility_zip_resources := $(out_dir)/tools $(out_dir)/testcases $(out_dir)/lib $(out_dir)/lib64 + +# Test Suite NOTICE files +test_suite_notice_txt := $(out_dir)/NOTICE.txt +test_suite_notice_html := $(out_dir)/NOTICE.html + +compatibility_zip_deps += $(test_suite_notice_txt) +compatibility_zip_resources += $(test_suite_notice_txt) + +compatibility_tests_list_zip := $(out_dir)-tests_list.zip + +compatibility_zip := $(out_dir).zip +$(compatibility_zip) : .KATI_IMPLICIT_OUTPUTS := $(compatibility_tests_list_zip) +$(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir) +$(compatibility_zip): PRIVATE_TOOLS := $(test_tools) $(test_suite_prebuilt_tools) +$(compatibility_zip): PRIVATE_SUITE_NAME := $(test_suite_name) +$(compatibility_zip): PRIVATE_DYNAMIC_CONFIG := $(test_suite_dynamic_config) +$(compatibility_zip): PRIVATE_RESOURCES := $(compatibility_zip_resources) +$(compatibility_zip): PRIVATE_JDK := $(test_suite_jdk) +$(compatibility_zip): PRIVATE_tests_list := $(out_dir)-tests_list +$(compatibility_zip): PRIVATE_tests_list_zip := $(compatibility_tests_list_zip) +$(compatibility_zip): $(compatibility_zip_deps) | $(ADB) $(ACP) +# Make dir structure + mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases + rm -f $@ $@.tmp $@.jdk + echo $(BUILD_NUMBER_FROM_FILE) > $(PRIVATE_OUT_DIR)/tools/version.txt +# Copy tools + cp $(PRIVATE_TOOLS) $(PRIVATE_OUT_DIR)/tools + $(if $(PRIVATE_DYNAMIC_CONFIG),$(hide) cp $(PRIVATE_DYNAMIC_CONFIG) $(PRIVATE_OUT_DIR)/testcases/$(PRIVATE_SUITE_NAME).dynamic) + find $(PRIVATE_RESOURCES) | sort >$@.list + $(SOONG_ZIP) -d -o $@.tmp -C $(dir $@) -l $@.list + $(MERGE_ZIPS) $@ $@.tmp $(PRIVATE_JDK) + rm -f $@.tmp +# Build a list of tests + rm -f $(PRIVATE_tests_list) + $(hide) grep -e .*\\.config$$ $@.list | sed s%$(PRIVATE_OUT_DIR)/testcases/%%g > $(PRIVATE_tests_list) + $(SOONG_ZIP) -d -o $(PRIVATE_tests_list_zip) -j -f $(PRIVATE_tests_list) + rm -f $(PRIVATE_tests_list) + +$(call declare-0p-target,$(compatibility_tests_list_zip),) + +$(call declare-1p-container,$(compatibility_zip),) +$(call declare-container-license-deps,$(compatibility_zip),$(compatibility_zip_deps) $(test_suite_jdk), $(out_dir)/:/) + +$(eval $(call html-notice-rule,$(test_suite_notice_html),"Test suites","Notices for files contained in the test suites filesystem image:",$(compatibility_zip),$(compatibility_zip))) +$(eval $(call text-notice-rule,$(test_suite_notice_txt),"Test suites","Notices for files contained in the test suites filesystem image:",$(compatibility_zip),$(compatibility_zip))) + +$(call declare-0p-target,$(test_suite_notice_html)) +$(call declare-0p-target,$(test_suite_notice_txt)) + +$(call declare-1p-copy-files,$(test_suite_dynamic_config),) +$(call declare-1p-copy-files,$(test_suite_prebuilt_tools),) + +# Reset all input variables +test_suite_name := +test_suite_tradefed := +test_suite_dynamic_config := +test_suite_readme := +test_suite_prebuilt_tools := +test_suite_tools := +test_suite_jdk := +test_suite_jdk_dir := +host_shared_libs := +test_suite_extra_deps := diff --git a/make/core/tasks/tools/package-modules.mk b/make/core/tasks/tools/package-modules.mk new file mode 100644 index 0000000..f89d51e --- /dev/null +++ b/make/core/tasks/tools/package-modules.mk @@ -0,0 +1,118 @@ +# Package up modules to a zip file. +# It preserves the install path of the modules' installed files. +# +# Input variables: +# my_modules: a list of module names +# my_package_name: the name of the output zip file. +# my_copy_pairs: a list of extra files to install (in src:dest format) +# Optional input variables: +# my_modules_strict: what happens when a module from my_modules does not exist +# "true": error out when a module is missing +# "false": print a warning when a module is missing +# "": defaults to false currently +# Output variables: +# my_package_zip: the path to the output zip file. +# +# + +my_makefile := $(lastword $(filter-out $(lastword $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) + +include $(CLEAR_VARS) +LOCAL_MODULE := $(my_package_name) +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_LICENSE_PACKAGE_NAME := Android +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_MODULE_CLASS := PACKAGING +LOCAL_MODULE_STEM := $(my_package_name).zip +LOCAL_UNINSTALLABLE_MODULE := true +include $(BUILD_SYSTEM)/base_rules.mk +my_staging_dir := $(intermediates) +my_package_zip := $(LOCAL_BUILT_MODULE) + +my_built_modules := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p))) +my_copy_pairs := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p)):$(my_staging_dir)/$(call word-colon,2,$(p))) +my_pickup_files := +my_missing_error := + +# Iterate over the modules and include their direct dependencies stated in the +# LOCAL_REQUIRED_MODULES. +my_modules_and_deps := $(my_modules) +$(foreach m,$(my_modules),\ + $(eval _explicitly_required := \ + $(strip $(ALL_MODULES.$(m).EXPLICITLY_REQUIRED_FROM_TARGET)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).EXPLICITLY_REQUIRED_FROM_TARGET)))\ + $(eval my_modules_and_deps += $(_explicitly_required))\ +) + +ifneq ($(filter-out true false,$(my_modules_strict)),) + $(shell $(call echo-error,$(my_makefile),$(my_package_name): Invalid value for 'my_module_strict' = '$(my_modules_strict)'. Valid values: 'true', 'false', '')) + $(error done) +endif + +my_missing_files = $(shell $(call echo-warning,$(my_makefile),$(my_package_name): Unknown installed file for module '$(1)')) +ifeq ($(ALLOW_MISSING_DEPENDENCIES),true) + # Ignore unknown installed files on partial builds + my_missing_files = +else ifneq ($(my_modules_strict),false) + my_missing_files = $(shell $(call echo-error,$(my_makefile),$(my_package_name): Unknown installed file for module '$(1)'))$(eval my_missing_error := true) +endif + +# Iterate over modules' built files and installed files; +# Calculate the dest files in the output zip file. + +$(foreach m,$(my_modules_and_deps),\ + $(eval _pickup_files := $(strip $(ALL_MODULES.$(m).PICKUP_FILES)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\ + $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\ + $(eval _module_class_folder := $($(strip MODULE_CLASS_$(word 1, $(strip $(ALL_MODULES.$(m).CLASS)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).CLASS))))))\ + $(if $(_pickup_files)$(_built_files),,\ + $(call my_missing_files,$(m)))\ + $(eval my_pickup_files += $(_pickup_files))\ + $(foreach i, $(_built_files),\ + $(eval bui_ins := $(subst :,$(space),$(i)))\ + $(eval ins := $(word 2,$(bui_ins)))\ + $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\ + $(eval bui := $(word 1,$(bui_ins)))\ + $(eval my_built_modules += $(bui))\ + $(if $(filter $(_module_class_folder), nativetest benchmarktest),\ + $(eval module_class_folder_stem := $(_module_class_folder)$(findstring 64, $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))),\ + $(eval module_class_folder_stem := $(_module_class_folder)))\ + $(eval my_copy_dest := $(patsubst data/%,DATA/%,\ + $(patsubst testcases/%,DATA/$(module_class_folder_stem)/%,\ + $(patsubst testcases/$(m)/$(TARGET_ARCH)/%,DATA/$(module_class_folder_stem)/$(m)/%,\ + $(patsubst testcases/$(m)/$(TARGET_2ND_ARCH)/%,DATA/$(module_class_folder_stem)/$(m)/%,\ + $(patsubst system/%,DATA/%,\ + $(patsubst $(PRODUCT_OUT)/%,%,$(ins))))))))\ + $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\ + )) + +ifneq ($(my_missing_error),) + $(error done) +endif + +$(my_package_zip): PRIVATE_COPY_PAIRS := $(my_copy_pairs) +$(my_package_zip): PRIVATE_PICKUP_FILES := $(my_pickup_files) +$(my_package_zip) : $(my_built_modules) + @echo "Package $@" + @rm -rf $(dir $@) && mkdir -p $(dir $@) + $(foreach p, $(PRIVATE_COPY_PAIRS),\ + $(eval pair := $(subst :,$(space),$(p)))\ + mkdir -p $(dir $(word 2,$(pair))) && \ + cp -Rf $(word 1,$(pair)) $(word 2,$(pair)) && ) true + $(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\ + cp -RfL $(f) $(dir $@) && ) true + $(hide) cd $(dir $@) && zip -rqX $(notdir $@) * + +my_makefile := +my_staging_dir := +my_built_modules := +my_copy_dest := +my_copy_pairs := +my_pickup_files := +my_missing_files := +my_missing_error := +my_modules_and_deps := +my_modules_strict := diff --git a/make/core/tasks/tools/vts-kernel-tests.mk b/make/core/tasks/tools/vts-kernel-tests.mk new file mode 100644 index 0000000..5fbb589 --- /dev/null +++ b/make/core/tasks/tools/vts-kernel-tests.mk @@ -0,0 +1,26 @@ +# 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. + +-include external/linux-kselftest/android/kselftest_test_list.mk +-include external/ltp/android/ltp_package_list.mk + +include $(BUILD_SYSTEM)/tasks/tools/vts_package_utils.mk + +# Copy kernel test modules to testcases directories +kernel_test_host_out := $(HOST_OUT_TESTCASES)/vts_kernel_tests +kernel_test_vts_out := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name)/testcases/vts_kernel_tests +kernel_test_modules := \ + $(kselftest_modules) \ + ltp \ + $(ltp_packages) \ No newline at end of file diff --git a/make/core/tasks/tools/vts_package_utils.mk b/make/core/tasks/tools/vts_package_utils.mk new file mode 100644 index 0000000..f1159b3 --- /dev/null +++ b/make/core/tasks/tools/vts_package_utils.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# $(1): List of target native files to copy. +# $(2): Copy destination directory. +# Evaluates to a list of ":"-separated pairs src:dst. +define target-native-copy-pairs +$(foreach m,$(1),\ + $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\ + $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\ + $(foreach i, $(_built_files),\ + $(eval bui_ins := $(subst :,$(space),$(i)))\ + $(eval ins := $(word 2,$(bui_ins)))\ + $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\ + $(eval bui := $(word 1,$(bui_ins)))\ + $(eval my_copy_dest := $(patsubst data/%,DATA/%,\ + $(patsubst system/%,DATA/%,\ + $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))))\ + $(eval ALL_TARGETS.$(2)/$(my_copy_dest).META_LIC := $(if $(strip $(ALL_MODULES.$(m).META_LIC)),$(ALL_MODULES.$(m).META_LIC),$(ALL_MODULES.$(m).DELAYED_META_LIC)))\ + $(bui):$(2)/$(my_copy_dest)))) +endef diff --git a/make/core/tasks/tradefed-tests-list.mk b/make/core/tasks/tradefed-tests-list.mk new file mode 100644 index 0000000..61bf136 --- /dev/null +++ b/make/core/tasks/tradefed-tests-list.mk @@ -0,0 +1,40 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# List all TradeFed tests from COMPATIBILITY.tradefed_tests_dir +.PHONY: tradefed-tests-list + +tradefed_tests := +$(foreach dir, $(COMPATIBILITY.tradefed_tests_dir), \ + $(eval tradefed_tests += $(shell find $(dir) -type f -name "*.xml"))) +tradefed_tests_list_intermediates := $(call intermediates-dir-for,PACKAGING,tradefed_tests_list,HOST,COMMON) +tradefed_tests_list_zip := $(tradefed_tests_list_intermediates)/tradefed-tests_list.zip +all_tests := +$(foreach test, $(tradefed_tests), \ + $(eval all_tests += $(word 2,$(subst /res/config/,$(space),$(test))))) +$(tradefed_tests_list_zip) : PRIVATE_tradefed_tests := $(subst .xml,,$(subst $(space),\n,$(sort $(all_tests)))) +$(tradefed_tests_list_zip) : PRIVATE_tradefed_tests_list := $(tradefed_tests_list_intermediates)/tradefed-tests_list + +$(tradefed_tests_list_zip) : $(tradefed_tests) $(SOONG_ZIP) + @echo "Package: $@" + $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@) + $(hide) echo -e "$(PRIVATE_tradefed_tests)" > $(PRIVATE_tradefed_tests_list) + $(hide) $(SOONG_ZIP) -d -o $@ -C $(dir $@) -f $(PRIVATE_tradefed_tests_list) + +tradefed-tests-list : $(tradefed_tests_list_zip) +$(call dist-for-goals, tradefed-tests-list, $(tradefed_tests_list_zip)) + +$(call declare-1p-target,$(tradefed_tests_list_zip),) + +tests: tradefed-tests-list diff --git a/make/core/tasks/vendor_module_check.mk b/make/core/tasks/vendor_module_check.mk new file mode 100644 index 0000000..4d7d67e --- /dev/null +++ b/make/core/tasks/vendor_module_check.mk @@ -0,0 +1,140 @@ +# +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Restrict the vendor module owners here. +_vendor_owner_allowed_list := \ + asus \ + audience \ + atmel \ + broadcom \ + csr \ + elan \ + fpc \ + google \ + htc \ + huawei \ + imgtec \ + invensense \ + intel \ + lge \ + moto \ + mtk \ + nvidia \ + nxp \ + nxpsw \ + qcom \ + qti \ + samsung \ + samsung_arm \ + sony \ + synaptics \ + ti \ + trusted_logic \ + verizon \ + waves \ + widevine + + +_restrictions := $(PRODUCT_RESTRICT_VENDOR_FILES) + +ifneq (,$(_restrictions)) +ifneq (,$(VENDOR_PRODUCT_RESTRICT_VENDOR_FILES)) +$(error Error: cannot set both PRODUCT_RESTRICT_VENDOR_FILES and VENDOR_PRODUCT_RESTRICT_VENDOR_FILES) +endif +_vendor_exception_path_prefix := +_vendor_exception_modules := +else +_restrictions := $(VENDOR_PRODUCT_RESTRICT_VENDOR_FILES) +_vendor_exception_path_prefix := $(patsubst %, vendor/%/%, $(VENDOR_EXCEPTION_PATHS)) +_vendor_exception_modules := $(VENDOR_EXCEPTION_MODULES) +endif + + +ifneq (,$(_restrictions)) + +_vendor_check_modules := \ +$(foreach m, $(filter-out $(_vendor_exception_modules), $(product_MODULES)), \ + $(if $(filter-out FAKE, $(ALL_MODULES.$(m).CLASS)),\ + $(if $(filter vendor/%, $(ALL_MODULES.$(m).PATH)),\ + $(if $(filter-out $(_vendor_exception_path_prefix), $(ALL_MODULES.$(m).PATH)),\ + $(m))))) + +_vendor_module_owner_info := +# Restrict owners +ifneq (,$(filter true owner all, $(_restrictions))) + +_vendor_package_overlays := $(filter-out $(_vendor_exception_path_prefix),\ + $(filter vendor/%, $(PRODUCT_PACKAGE_OVERLAYS) $(DEVICE_PACKAGE_OVERLAYS))) +ifneq (,$(_vendor_package_overlays)) +$(error Error: Product "$(TARGET_PRODUCT)" cannot have overlay in vendor tree: $(_vendor_package_overlays)) +endif +_vendor_package_overlays := + +_vendor_check_copy_files := $(filter-out $(_vendor_exception_path_prefix),\ + $(filter vendor/%, $(PRODUCT_COPY_FILES))) +ifneq (,$(_vendor_check_copy_files)) +$(foreach c, $(_vendor_check_copy_files), \ + $(if $(filter $(_vendor_owner_allowed_list), $(call word-colon,3,$(c))),,\ + $(error Error: vendor PRODUCT_COPY_FILES file "$(c)" has unknown owner))\ + $(eval _vendor_module_owner_info += $(call word-colon,2,$(c)):$(call word-colon,3,$(c)))) +endif +_vendor_check_copy_files := + +$(foreach m, $(_vendor_check_modules), \ + $(if $(filter $(_vendor_owner_allowed_list), $(ALL_MODULES.$(m).OWNER)),,\ + $(error Error: vendor module "$(m)" in $(ALL_MODULES.$(m).PATH) with unknown owner \ + "$(ALL_MODULES.$(m).OWNER)" in product "$(TARGET_PRODUCT)"))\ + $(if $(ALL_MODULES.$(m).INSTALLED),\ + $(eval _vendor_module_owner_info += $(patsubst $(PRODUCT_OUT)/%,%,$(ALL_MODULES.$(m).INSTALLED)):$(ALL_MODULES.$(m).OWNER)))) + +endif + + +# Restrict paths +ifneq (,$(filter path all, $(_restrictions))) + +$(foreach m, $(_vendor_check_modules), \ + $(if $(filter-out ,$(ALL_MODULES.$(m).INSTALLED)),\ + $(if $(filter $(TARGET_OUT_VENDOR)/% $(TARGET_OUT_ODM)/% $(TARGET_OUT_VENDOR_DLKM)/% $(TARGET_OUT_ODM_DLKM)/% $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED)),,\ + $(error Error: vendor module "$(m)" in $(ALL_MODULES.$(m).PATH) \ + in product "$(TARGET_PRODUCT)" being installed to \ + $(ALL_MODULES.$(m).INSTALLED) which is not in the vendor, odm, vendor_dlkm or odm_dlkm tree)))) + +endif + +_vendor_module_owner_info_txt := $(call intermediates-dir-for,PACKAGING,vendor_owner_info)/vendor_owner_info.txt +$(_vendor_module_owner_info_txt): PRIVATE_INFO := $(_vendor_module_owner_info) +$(_vendor_module_owner_info_txt): + @echo "Write vendor module owner info $@" + @mkdir -p $(dir $@) && rm -f $@ +ifdef _vendor_module_owner_info + @for w in $(PRIVATE_INFO); \ + do \ + echo $$w >> $@; \ + done +else + @echo "No vendor module owner info." > $@ +endif + +$(call dist-for-goals, droidcore, $(_vendor_module_owner_info_txt)) + +_vendor_module_owner_info_txt := +_vendor_module_owner_info := +_vendor_check_modules := +_vendor_exception_path_prefix := +_vendor_exception_modules := +_restrictions := +endif diff --git a/make/core/tasks/vendor_snapshot.mk b/make/core/tasks/vendor_snapshot.mk new file mode 100644 index 0000000..83c1379 --- /dev/null +++ b/make/core/tasks/vendor_snapshot.mk @@ -0,0 +1,46 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +current_makefile := $(lastword $(MAKEFILE_LIST)) + +# BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot. +ifeq ($(BOARD_VNDK_VERSION),current) + +.PHONY: vendor-snapshot +vendor-snapshot: $(SOONG_VENDOR_SNAPSHOT_ZIP) + +$(call dist-for-goals, vendor-snapshot, $(SOONG_VENDOR_SNAPSHOT_ZIP)) + +.PHONY: vendor-fake-snapshot +vendor-fake-snapshot: $(SOONG_VENDOR_FAKE_SNAPSHOT_ZIP) + +$(call dist-for-goals, vendor-fake-snapshot, $(SOONG_VENDOR_FAKE_SNAPSHOT_ZIP):fake/$(notdir $(SOONG_VENDOR_FAKE_SNAPSHOT_ZIP))) + +else # BOARD_VNDK_VERSION is NOT set to 'current' + +.PHONY: vendor-snapshot +vendor-snapshot: PRIVATE_MAKEFILE := $(current_makefile) +vendor-snapshot: + $(call echo-error,$(PRIVATE_MAKEFILE),\ + "CANNOT generate Vendor snapshot. BOARD_VNDK_VERSION must be set to 'current'.") + exit 1 + +.PHONY: vendor-fake-snapshot +vendor-fake-snapshot: PRIVATE_MAKEFILE := $(current_makefile) +vendor-fake-snapshot: + $(call echo-error,$(PRIVATE_MAKEFILE),\ + "CANNOT generate Vendor snapshot. BOARD_VNDK_VERSION must be set to 'current'.") + exit 1 + +endif # BOARD_VNDK_VERSION diff --git a/make/core/tasks/vndk.mk b/make/core/tasks/vndk.mk new file mode 100644 index 0000000..ebe9bd4 --- /dev/null +++ b/make/core/tasks/vndk.mk @@ -0,0 +1,44 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +current_makefile := $(lastword $(MAKEFILE_LIST)) + +# BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot. +ifeq ($(BOARD_VNDK_VERSION),current) + +# PLATFORM_VNDK_VERSION must be set. +ifneq (,$(PLATFORM_VNDK_VERSION)) + +.PHONY: vndk +vndk: $(SOONG_VNDK_SNAPSHOT_ZIP) + +$(call dist-for-goals, vndk, $(SOONG_VNDK_SNAPSHOT_ZIP)) + +else # PLATFORM_VNDK_VERSION is NOT set +error_msg := "CANNOT generate VNDK snapshot. PLATFORM_VNDK_VERSION must be set." +endif # PLATFORM_VNDK_VERSION + +else # BOARD_VNDK_VERSION is NOT set to 'current' +error_msg := "CANNOT generate VNDK snapshot. BOARD_VNDK_VERSION must be set to 'current'." +endif # BOARD_VNDK_VERSION + +ifneq (,$(error_msg)) + +.PHONY: vndk +vndk: PRIVATE_MAKEFILE := $(current_makefile) +vndk: + $(call echo-error,$(PRIVATE_MAKEFILE),$(error_msg)) + exit 1 + +endif diff --git a/make/core/tasks/vts-core-tests.mk b/make/core/tasks/vts-core-tests.mk new file mode 100644 index 0000000..5e1b5d5 --- /dev/null +++ b/make/core/tasks/vts-core-tests.mk @@ -0,0 +1,34 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +test_suite_name := vts +test_suite_tradefed := vts-tradefed +test_suite_readme := test/vts/tools/vts-core-tradefed/README + +include $(BUILD_SYSTEM)/tasks/tools/vts-kernel-tests.mk + +kernel_test_copy_pairs := \ + $(call target-native-copy-pairs,$(kernel_test_modules),$(kernel_test_vts_out)) + +copy_kernel_tests := $(call copy-many-files,$(kernel_test_copy_pairs)) + +test_suite_extra_deps := $(copy_kernel_tests) + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: vts +vts: $(compatibility_zip) $(compatibility_tests_list_zip) +$(call dist-for-goals, vts, $(compatibility_zip) $(compatibility_tests_list_zip)) + +tests: vts diff --git a/make/core/tasks/with-license.mk b/make/core/tasks/with-license.mk new file mode 100644 index 0000000..d41e77a --- /dev/null +++ b/make/core/tasks/with-license.mk @@ -0,0 +1,58 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.PHONY: with-license + +name := $(TARGET_PRODUCT) +ifeq ($(TARGET_BUILD_TYPE),debug) + name := $(name)_debug +endif + +name := $(name)-flashable-$(FILE_NAME_TAG)-with-license + +with_license_intermediates := \ + $(call intermediates-dir-for,PACKAGING,with_license) + +# Create a with-license artifact target +license_image_input_zip := $(with_license_intermediates)/$(name).zip +$(license_image_input_zip) : $(BUILT_TARGET_FILES_PACKAGE) $(ZIP2ZIP) +# DO NOT PROCEED without a license file. +ifndef VENDOR_BLOBS_LICENSE + @echo "with-license requires VENDOR_BLOBS_LICENSE to be set." + exit 1 +else + $(ZIP2ZIP) -i $(BUILT_TARGET_FILES_PACKAGE) -o $@ \ + RADIO/bootloader.img:bootloader.img RADIO/radio.img:radio.img \ + IMAGES/*.img:. OTA/android-info.txt:android-info.txt +endif + +$(call declare-1p-container,$(license_image_input_zip),build) +$(call declare-container-deps,$(license_image_input_zip),$(BUILT_TARGET_FILES_PACKAGE)) + +with_license_zip := $(PRODUCT_OUT)/$(name).sh +$(with_license_zip): PRIVATE_NAME := $(name) +$(with_license_zip): PRIVATE_INPUT_ZIP := $(license_image_input_zip) +$(with_license_zip): PRIVATE_VENDOR_BLOBS_LICENSE := $(VENDOR_BLOBS_LICENSE) +$(with_license_zip): $(license_image_input_zip) $(VENDOR_BLOBS_LICENSE) +$(with_license_zip): $(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive + # Args: + $(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive $@ \ + $(PRIVATE_INPUT_ZIP) $(PRIVATE_NAME) $(PRIVATE_VENDOR_BLOBS_LICENSE) +with-license : $(with_license_zip) +$(call dist-for-goals, with-license, $(with_license_zip)) + +$(call declare-1p-container,$(with_license_zip),) +$(call declare-container-license-deps,$(with_license_zip),$(license_image_input_zip),$(with_license_zip):) + diff --git a/make/core/use_lld_setup.mk b/make/core/use_lld_setup.mk new file mode 100644 index 0000000..8f47d68 --- /dev/null +++ b/make/core/use_lld_setup.mk @@ -0,0 +1,20 @@ +############################################################# +## Set up flags based on LOCAL_USE_CLANG_LLD. +## Input variables: LOCAL_USE_CLANG_LLD +## Output variables: my_use_clang_lld +############################################################# + +# Use LLD by default. +# Do not use LLD if LOCAL_USE_CLANG_LLD is false or 0 +my_use_clang_lld := true +ifneq (,$(LOCAL_USE_CLANG_LLD)) + ifneq (,$(filter 0 false,$(LOCAL_USE_CLANG_LLD))) + my_use_clang_lld := false + endif +endif + +# Do not use LLD for Darwin host executables or shared libraries. See +# https://lld.llvm.org/AtomLLD.html for status of lld for Mach-O. +ifeq ($($(my_prefix)OS),darwin) +my_use_clang_lld := false +endif diff --git a/make/core/version_defaults.mk b/make/core/version_defaults.mk new file mode 100644 index 0000000..4fa5425 --- /dev/null +++ b/make/core/version_defaults.mk @@ -0,0 +1,109 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Handle various build version information. +# +# Guarantees that the following are defined: +# PLATFORM_VERSION +# PLATFORM_DISPLAY_VERSION +# PLATFORM_SDK_VERSION +# PLATFORM_VERSION_CODENAME +# DEFAULT_APP_TARGET_SDK +# BUILD_ID +# BUILD_NUMBER +# PLATFORM_SECURITY_PATCH +# PLATFORM_VNDK_VERSION +# PLATFORM_SYSTEMSDK_VERSIONS +# + +# Look for an optional file containing overrides of the defaults, +# but don't cry if we don't find it. We could just use -include, but +# the build.prop target also wants INTERNAL_BUILD_ID_MAKEFILE to be set +# if the file exists. +# +INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk) +ifdef INTERNAL_BUILD_ID_MAKEFILE + include $(INTERNAL_BUILD_ID_MAKEFILE) +endif + +DEFAULT_PLATFORM_VERSION := TP1A +.KATI_READONLY := DEFAULT_PLATFORM_VERSION +MIN_PLATFORM_VERSION := TP1A +MAX_PLATFORM_VERSION := TP1A + +# The last stable version name of the platform that was released. During +# development, this stays at that previous version, while the codename indicates +# further work based on the previous version. +PLATFORM_VERSION_LAST_STABLE := 13 +.KATI_READONLY := PLATFORM_VERSION_LAST_STABLE + +# These are the current development codenames, if the build is not a final +# release build. If this is a final release build, it is simply "REL". +PLATFORM_VERSION_CODENAME.TP1A := REL + +# This is the user-visible version. In a final release build it should +# be empty to use PLATFORM_VERSION as the user-visible version. For +# a preview release it can be set to a user-friendly value like `12 Preview 1` +PLATFORM_DISPLAY_VERSION := 13 + +ifndef PLATFORM_SDK_VERSION + # This is the canonical definition of the SDK version, which defines + # the set of APIs and functionality available in the platform. It + # is a single integer that increases monotonically as updates to + # the SDK are released. It should only be incremented when the APIs for + # the new release are frozen (so that developers don't write apps against + # intermediate builds). During development, this number remains at the + # SDK version the branch is based on and PLATFORM_VERSION_CODENAME holds + # the code-name of the new development work. + + # When you increment the PLATFORM_SDK_VERSION please ensure you also + # clear out the following text file of all older PLATFORM_VERSION's: + # cts/tests/tests/os/assets/platform_versions.txt + PLATFORM_SDK_VERSION := 33 +endif +.KATI_READONLY := PLATFORM_SDK_VERSION + +# This is the sdk extension version of this tree. +PLATFORM_SDK_EXTENSION_VERSION := 3 +.KATI_READONLY := PLATFORM_SDK_EXTENSION_VERSION + +# This is the sdk extension version that PLATFORM_SDK_VERSION ships with. +PLATFORM_BASE_SDK_EXTENSION_VERSION := 3 +.KATI_READONLY := PLATFORM_BASE_SDK_EXTENSION_VERSION + +# This are all known codenames. +PLATFORM_VERSION_KNOWN_CODENAMES := \ +Base Base11 Cupcake Donut Eclair Eclair01 EclairMr1 Froyo Gingerbread GingerbreadMr1 \ +Honeycomb HoneycombMr1 HoneycombMr2 IceCreamSandwich IceCreamSandwichMr1 \ +JellyBean JellyBeanMr1 JellyBeanMr2 Kitkat KitkatWatch Lollipop LollipopMr1 M N NMr1 O OMr1 P \ +Q R S Sv2 Tiramisu + +# Convert from space separated list to comma separated +PLATFORM_VERSION_KNOWN_CODENAMES := \ + $(call normalize-comma-list,$(PLATFORM_VERSION_KNOWN_CODENAMES)) +.KATI_READONLY := PLATFORM_VERSION_KNOWN_CODENAMES + +ifndef PLATFORM_SECURITY_PATCH + # Used to indicate the security patch that has been applied to the device. + # It must signify that the build includes all security patches issued up through the designated Android Public Security Bulletin. + # It must be of the form "YYYY-MM-DD" on production devices. + # It must match one of the Android Security Patch Level strings of the Public Security Bulletins. + # If there is no $PLATFORM_SECURITY_PATCH set, keep it empty. + PLATFORM_SECURITY_PATCH := 2023-08-05 +endif + +include $(BUILD_SYSTEM)/version_util.mk diff --git a/make/core/version_util.mk b/make/core/version_util.mk new file mode 100644 index 0000000..b68b766 --- /dev/null +++ b/make/core/version_util.mk @@ -0,0 +1,261 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# + +ALLOWED_VERSIONS := $(call allowed-platform-versions,\ + $(MIN_PLATFORM_VERSION),\ + $(MAX_PLATFORM_VERSION),\ + $(DEFAULT_PLATFORM_VERSION)) + +ifndef TARGET_PLATFORM_VERSION + TARGET_PLATFORM_VERSION := $(DEFAULT_PLATFORM_VERSION) +endif + +ifeq (,$(filter $(ALLOWED_VERSIONS), $(TARGET_PLATFORM_VERSION))) + $(warning Invalid TARGET_PLATFORM_VERSION '$(TARGET_PLATFORM_VERSION)', must be one of) + $(error $(ALLOWED_VERSIONS)) +endif +ALLOWED_VERSIONS := +MIN_PLATFORM_VERSION := +MAX_PLATFORM_VERSION := + +.KATI_READONLY := TARGET_PLATFORM_VERSION + +# Default versions for each TARGET_PLATFORM_VERSION +# TODO: PLATFORM_VERSION, PLATFORM_SDK_VERSION, etc. should be conditional +# on this + +# This is the canonical definition of the platform version, +# which is the version that we reveal to the end user. +# Update this value when the platform version changes (rather +# than overriding it somewhere else). Can be an arbitrary string. + +# When you change PLATFORM_VERSION for a given PLATFORM_SDK_VERSION +# please add that PLATFORM_VERSION as well as clean up obsolete PLATFORM_VERSION's +# in the following text file: +# cts/tests/tests/os/assets/platform_versions.txt + +# Note that there should be one PLATFORM_VERSION and PLATFORM_VERSION_CODENAME +# entry for each unreleased API level, regardless of +# MIN_PLATFORM_VERSION/MAX_PLATFORM_VERSION. PLATFORM_VERSION is used to +# generate the range of allowed SDK versions, so it must have an entry for every +# unreleased API level targetable by this branch, not just those that are valid +# lunch targets for this branch. + +ifndef PLATFORM_VERSION_CODENAME + PLATFORM_VERSION_CODENAME := $(PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION)) + ifndef PLATFORM_VERSION_CODENAME + # PLATFORM_VERSION_CODENAME falls back to TARGET_PLATFORM_VERSION + PLATFORM_VERSION_CODENAME := $(TARGET_PLATFORM_VERSION) + endif + + # This is all of the *active* development codenames. + # This confusing name is needed because + # all_codenames has been baked into build.prop for ages. + # + # Should be either the same as PLATFORM_VERSION_CODENAME or a comma-separated + # list of additional codenames after PLATFORM_VERSION_CODENAME. + PLATFORM_VERSION_ALL_CODENAMES := + + # Build a list of all active code names. Avoid duplicates, and stop when we + # reach a codename that matches PLATFORM_VERSION_CODENAME (anything beyond + # that is not included in our build). + _versions_in_target := \ + $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION)) + $(foreach version,$(_versions_in_target),\ + $(eval _codename := $(PLATFORM_VERSION_CODENAME.$(version)))\ + $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\ + $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename)))) + + # And convert from space separated to comma separated. + PLATFORM_VERSION_ALL_CODENAMES := \ + $(subst $(space),$(comma),$(strip $(PLATFORM_VERSION_ALL_CODENAMES))) + +endif +.KATI_READONLY := \ + PLATFORM_VERSION_CODENAME \ + PLATFORM_VERSION_ALL_CODENAMES + +ifneq (REL,$(PLATFORM_VERSION_CODENAME)) + codenames := \ + $(subst $(comma),$(space),$(strip $(PLATFORM_VERSION_KNOWN_CODENAMES))) + ifeq ($(filter $(PLATFORM_VERSION_CODENAME),$(codenames)),) + $(error '$(PLATFORM_VERSION_CODENAME)' is not in '$(codenames)'. \ + Add PLATFORM_VERSION_CODENAME to PLATFORM_VERSION_KNOWN_CODENAMES) + endif +endif + +ifndef PLATFORM_VERSION + ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE) + else + PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME) + endif +endif +.KATI_READONLY := PLATFORM_VERSION + +ifndef PLATFORM_DISPLAY_VERSION + PLATFORM_DISPLAY_VERSION := $(PLATFORM_VERSION) +endif +.KATI_READONLY := PLATFORM_DISPLAY_VERSION + +ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + PLATFORM_PREVIEW_SDK_VERSION := 0 +else + ifndef PLATFORM_PREVIEW_SDK_VERSION + # This is the definition of a preview SDK version over and above the current + # platform SDK version. Unlike the platform SDK version, a higher value + # for preview SDK version does NOT mean that all prior preview APIs are + # included. Packages reading this value to determine compatibility with + # known APIs should check that this value is precisely equal to the preview + # SDK version the package was built for, otherwise it should fall back to + # assuming the device can only support APIs as of the previous official + # public release. + # This value will always be forced to 0 for release builds by the logic + # in the "ifeq" block above, so the value below will be used on any + # non-release builds, and it should always be at least 1, to indicate that + # APIs may have changed since the claimed PLATFORM_SDK_VERSION. + PLATFORM_PREVIEW_SDK_VERSION := 1 + endif +endif +.KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION + +ifndef DEFAULT_APP_TARGET_SDK + # This is the default minSdkVersion and targetSdkVersion to use for + # all .apks created by the build system. It can be overridden by explicitly + # setting these in the .apk's AndroidManifest.xml. It is either the code + # name of the development build or, if this is a release build, the official + # SDK version of this release. + ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION) + else + DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME) + endif +endif +.KATI_READONLY := DEFAULT_APP_TARGET_SDK + +ifndef PLATFORM_VNDK_VERSION + # This is the definition of the VNDK version for the current VNDK libraries. + # The version is only available when PLATFORM_VERSION_CODENAME == REL. + # Otherwise, it will be set to a CODENAME version. The ABI is allowed to be + # changed only before the Android version is released. Once + # PLATFORM_VNDK_VERSION is set to actual version, the ABI for this version + # will be frozon and emit build errors if any ABI for the VNDK libs are + # changed. + # After that the snapshot of the VNDK with this version will be generated. + # + # The VNDK version follows PLATFORM_SDK_VERSION. + ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION) + else + PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME) + endif +endif +.KATI_READONLY := PLATFORM_VNDK_VERSION + +ifndef PLATFORM_SYSTEMSDK_MIN_VERSION + # This is the oldest version of system SDK that the platform supports. Contrary + # to the public SDK where platform essentially supports all previous SDK versions, + # platform supports only a few number of recent system SDK versions as some of + # old system APIs are gradually deprecated, removed and then deleted. + PLATFORM_SYSTEMSDK_MIN_VERSION := 28 +endif +.KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION + +# This is the list of system SDK versions that the current platform supports. +PLATFORM_SYSTEMSDK_VERSIONS := +ifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION)) + $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\ + $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION))) + PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION)) +endif +# Platform always supports the current version +ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION) +else + PLATFORM_SYSTEMSDK_VERSIONS += $(subst $(comma),$(space),$(PLATFORM_VERSION_ALL_CODENAMES)) +endif +PLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS))) +.KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS + +.KATI_READONLY := PLATFORM_SECURITY_PATCH + +ifndef PLATFORM_SECURITY_PATCH_TIMESTAMP + # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH. + PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ="GMT" $(PLATFORM_SECURITY_PATCH)' +%s) +endif +.KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP + +ifndef PLATFORM_BASE_OS + # Used to indicate the base os applied to the device. + # Can be an arbitrary string, but must be a single word. + # + # If there is no $PLATFORM_BASE_OS set, keep it empty. + PLATFORM_BASE_OS := +endif +.KATI_READONLY := PLATFORM_BASE_OS + +ifndef BUILD_ID + # Used to signify special builds. E.g., branches and/or releases, + # like "M5-RC7". Can be an arbitrary string, but must be a single + # word and a valid file name. + # + # If there is no BUILD_ID set, make it obvious. + BUILD_ID := UNKNOWN +endif +.KATI_READONLY := BUILD_ID + +ifndef BUILD_DATETIME + # Used to reproduce builds by setting the same time. Must be the number + # of seconds since the Epoch. + BUILD_DATETIME := $(shell date +%s) +endif + +DATE := date -d @$(BUILD_DATETIME) +.KATI_READONLY := DATE + +# Everything should be using BUILD_DATETIME_FROM_FILE instead. +# BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves +# to soong_ui. +$(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE) + +HAS_BUILD_NUMBER := true +ifdef ROCKCHIP_BUILD_NUMBER + BUILD_NUMBER := $(ROCKCHIP_BUILD_NUMBER) +endif +ifndef BUILD_NUMBER + # BUILD_NUMBER should be set to the source control value that + # represents the current state of the source code. E.g., a + # perforce changelist number or a git hash. Can be an arbitrary string + # (to allow for source control that uses something other than numbers), + # but must be a single word and a valid file name. + # + # If no BUILD_NUMBER is set, create a useful "I am an engineering build + # from this date/time" value. Make it start with a non-digit so that + # anyone trying to parse it as an integer will probably get "0". + BUILD_NUMBER := eng.$(shell echo $${BUILD_USERNAME:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S) + HAS_BUILD_NUMBER := false +endif +.KATI_READONLY := BUILD_NUMBER HAS_BUILD_NUMBER + +ifndef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION + # Used to set minimum supported target sdk version. Apps targeting sdk + # version lower than the set value will result in a warning being shown + # when any activity from the app is started. + PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 23 +endif +.KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION diff --git a/make/envsetup.sh b/make/envsetup.sh new file mode 100644 index 0000000..5feca04 --- /dev/null +++ b/make/envsetup.sh @@ -0,0 +1,1983 @@ +function hmm() { +cat <- + Selects as the product to build, and as the variant to + build, and stores those selections in the environment to be read by subsequent + invocations of 'm' etc. +- tapas: tapas [ ...] [arm|x86|arm64|x86_64] [eng|userdebug|user] + Sets up the build environment for building unbundled apps (APKs). +- banchan: banchan [ ...] [arm|x86|arm64|x86_64] [eng|userdebug|user] + Sets up the build environment for building unbundled modules (APEXes). +- croot: Changes directory to the top of the tree, or a subdirectory thereof. +- m: Makes from the top of the tree. +- mm: Builds and installs all of the modules in the current directory, and their + dependencies. +- mmm: Builds and installs all of the modules in the supplied directories, and their + dependencies. + To limit the modules being built use the syntax: mmm dir/:target1,target2. +- mma: Same as 'mm' +- mmma: Same as 'mmm' +- provision: Flash device with all required partitions. Options will be passed on to fastboot. +- cgrep: Greps on all local C/C++ files. +- ggrep: Greps on all local Gradle files. +- gogrep: Greps on all local Go files. +- jgrep: Greps on all local Java files. +- ktgrep: Greps on all local Kotlin files. +- resgrep: Greps on all local res/*.xml files. +- mangrep: Greps on all local AndroidManifest.xml files. +- mgrep: Greps on all local Makefiles and *.bp files. +- owngrep: Greps on all local OWNERS files. +- rsgrep: Greps on all local Rust files. +- sepgrep: Greps on all local sepolicy files. +- sgrep: Greps on all local source files. +- godir: Go to the directory containing a file. +- allmod: List all modules. +- gomod: Go to the directory containing a module. +- pathmod: Get the directory containing a module. +- outmod: Gets the location of a module's installed outputs with a certain extension. +- dirmods: Gets the modules defined in a given directory. +- installmod: Adb installs a module's built APK. +- refreshmod: Refresh list of modules for allmod/gomod/pathmod/outmod/installmod. +- syswrite: Remount partitions (e.g. system.img) as writable, rebooting if necessary. + +Environment options: +- SANITIZE_HOST: Set to 'address' to use ASAN for all host modules. +- ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages. + +Look at the source to view more functions. The complete list is: +EOF + local T=$(gettop) + local A="" + local i + for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do + A="$A $i" + done + echo $A +} + +# Get all the build variables needed by this script in a single call to the build system. +function build_build_var_cache() +{ + local T=$(gettop) + # Grep out the variable names from the script. + cached_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/get_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`) + cached_abs_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/get_abs_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`) + # Call the build system to dump the "=" pairs as a shell script. + build_dicts_script=`\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \ + --vars="${cached_vars[*]}" \ + --abs-vars="${cached_abs_vars[*]}" \ + --var-prefix=var_cache_ \ + --abs-var-prefix=abs_var_cache_` + local ret=$? + if [ $ret -ne 0 ] + then + unset build_dicts_script + return $ret + fi + # Execute the script to store the "=" pairs as shell variables. + eval "$build_dicts_script" + ret=$? + unset build_dicts_script + if [ $ret -ne 0 ] + then + return $ret + fi + BUILD_VAR_CACHE_READY="true" +} + +# Delete the build var cache, so that we can still call into the build system +# to get build variables not listed in this script. +function destroy_build_var_cache() +{ + unset BUILD_VAR_CACHE_READY + local v + for v in $cached_vars; do + unset var_cache_$v + done + unset cached_vars + for v in $cached_abs_vars; do + unset abs_var_cache_$v + done + unset cached_abs_vars +} + +# Get the value of a build variable as an absolute path. +function get_abs_build_var() +{ + if [ "$BUILD_VAR_CACHE_READY" = "true" ] + then + eval "echo \"\${abs_var_cache_$1}\"" + return + fi + + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." >&2 + return + fi + (\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1) +} + +# Get the exact value of a build variable. +function get_build_var() +{ + if [ "$BUILD_VAR_CACHE_READY" = "true" ] + then + eval "echo \"\${var_cache_$1}\"" + return 0 + fi + + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." >&2 + return 1 + fi + (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1) +} + +# check to see if the supplied product is one we can build +function check_product() +{ + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." >&2 + return + fi + TARGET_PRODUCT=$1 \ + TARGET_BUILD_VARIANT= \ + TARGET_BUILD_TYPE= \ + TARGET_BUILD_APPS= \ + get_build_var TARGET_DEVICE > /dev/null + # hide successful answers, but allow the errors to show +} + +VARIANT_CHOICES=(user userdebug eng) + +# check to see if the supplied variant is valid +function check_variant() +{ + local v + for v in ${VARIANT_CHOICES[@]} + do + if [ "$v" = "$1" ] + then + return 0 + fi + done + return 1 +} + +function setpaths() +{ + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." + return + fi + + ################################################################## + # # + # Read me before you modify this code # + # # + # This function sets ANDROID_BUILD_PATHS to what it is adding # + # to PATH, and the next time it is run, it removes that from # + # PATH. This is required so lunch can be run more than once # + # and still have working paths. # + # # + ################################################################## + + # Note: on windows/cygwin, ANDROID_BUILD_PATHS will contain spaces + # due to "C:\Program Files" being in the path. + + # out with the old + if [ -n "$ANDROID_BUILD_PATHS" ] ; then + export PATH=${PATH/$ANDROID_BUILD_PATHS/} + fi + if [ -n "$ANDROID_PRE_BUILD_PATHS" ] ; then + export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/} + # strip leading ':', if any + export PATH=${PATH/:%/} + fi + + # and in with the new + local prebuiltdir=$(getprebuilt) + local gccprebuiltdir=$(get_abs_build_var ANDROID_GCC_PREBUILTS) + + # defined in core/config.mk + local targetgccversion=$(get_build_var TARGET_GCC_VERSION) + local targetgccversion2=$(get_build_var 2ND_TARGET_GCC_VERSION) + export TARGET_GCC_VERSION=$targetgccversion + + # The gcc toolchain does not exists for windows/cygwin. In this case, do not reference it. + export ANDROID_TOOLCHAIN= + export ANDROID_TOOLCHAIN_2ND_ARCH= + local ARCH=$(get_build_var TARGET_ARCH) + local toolchaindir toolchaindir2= + case $ARCH in + x86) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin + ;; + x86_64) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin + ;; + arm) toolchaindir=arm/arm-linux-androideabi-$targetgccversion/bin + ;; + arm64) toolchaindir=aarch64/aarch64-linux-android-$targetgccversion/bin; + toolchaindir2=arm/arm-linux-androideabi-$targetgccversion2/bin + ;; + *) + echo "Can't find toolchain for unknown architecture: $ARCH" + toolchaindir=xxxxxxxxx + ;; + esac + if [ -d "$gccprebuiltdir/$toolchaindir" ]; then + export ANDROID_TOOLCHAIN=$gccprebuiltdir/$toolchaindir + fi + + if [ "$toolchaindir2" -a -d "$gccprebuiltdir/$toolchaindir2" ]; then + export ANDROID_TOOLCHAIN_2ND_ARCH=$gccprebuiltdir/$toolchaindir2 + fi + + export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools + + # add kernel specific binaries + case $(uname -s) in + Linux) + export ANDROID_DEV_SCRIPTS=$ANDROID_DEV_SCRIPTS:$T/prebuilts/misc/linux-x86/dtc:$T/prebuilts/misc/linux-x86/libufdt + ;; + *) + ;; + esac + + ANDROID_BUILD_PATHS=$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_TOOLCHAIN + ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_TOOLCHAIN_2ND_ARCH + ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_DEV_SCRIPTS + + # Append llvm binutils prebuilts path to ANDROID_BUILD_PATHS. + local ANDROID_LLVM_BINUTILS=$(get_abs_build_var ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable + ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_LLVM_BINUTILS + + # Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds. + export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer + + # If prebuilts/android-emulator// exists, prepend it to our PATH + # to ensure that the corresponding 'emulator' binaries are used. + case $(uname -s) in + Darwin) + ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64 + ;; + Linux) + ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64 + ;; + *) + ANDROID_EMULATOR_PREBUILTS= + ;; + esac + if [ -n "$ANDROID_EMULATOR_PREBUILTS" -a -d "$ANDROID_EMULATOR_PREBUILTS" ]; then + ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_EMULATOR_PREBUILTS + export ANDROID_EMULATOR_PREBUILTS + fi + + # Append asuite prebuilts path to ANDROID_BUILD_PATHS. + local os_arch=$(get_build_var HOST_PREBUILT_TAG) + local ACLOUD_PATH="$T/prebuilts/asuite/acloud/$os_arch" + local AIDEGEN_PATH="$T/prebuilts/asuite/aidegen/$os_arch" + local ATEST_PATH="$T/prebuilts/asuite/atest/$os_arch" + ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ACLOUD_PATH:$AIDEGEN_PATH:$ATEST_PATH + + export ANDROID_BUILD_PATHS=$(tr -s : <<<"${ANDROID_BUILD_PATHS}:") + export PATH=$ANDROID_BUILD_PATHS$PATH + + # out with the duplicate old + if [ -n $ANDROID_PYTHONPATH ]; then + export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/} + fi + # and in with the new + export ANDROID_PYTHONPATH=$T/development/python-packages: + if [ -n $VENDOR_PYTHONPATH ]; then + ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH + fi + export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH + + export ANDROID_JAVA_HOME=$(get_abs_build_var ANDROID_JAVA_HOME) + export JAVA_HOME=$ANDROID_JAVA_HOME + export ANDROID_JAVA_TOOLCHAIN=$(get_abs_build_var ANDROID_JAVA_TOOLCHAIN) + export ANDROID_PRE_BUILD_PATHS=$ANDROID_JAVA_TOOLCHAIN: + export PATH=$ANDROID_PRE_BUILD_PATHS$PATH + + unset ANDROID_PRODUCT_OUT + export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT) + export OUT=$ANDROID_PRODUCT_OUT + + unset ANDROID_HOST_OUT + export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT) + + unset ANDROID_SOONG_HOST_OUT + export ANDROID_SOONG_HOST_OUT=$(get_abs_build_var SOONG_HOST_OUT) + + unset ANDROID_HOST_OUT_TESTCASES + export ANDROID_HOST_OUT_TESTCASES=$(get_abs_build_var HOST_OUT_TESTCASES) + + unset ANDROID_TARGET_OUT_TESTCASES + export ANDROID_TARGET_OUT_TESTCASES=$(get_abs_build_var TARGET_OUT_TESTCASES) + + unset TARGET_BOARD_PLATFORM + export TARGET_BOARD_PLATFORM=$(get_build_var TARGET_BOARD_PLATFORM) + + unset TARGET_BOARD_PLATFORM_GPU + export TARGET_BOARD_PLATFORM_GPU=$(get_build_var TARGET_BOARD_PLATFORM_GPU) + + unset BOARD_HS_DYNAMIC_AFBC_TARGET + export BOARD_HS_DYNAMIC_AFBC_TARGET=$(get_build_var BOARD_HS_DYNAMIC_AFBC_TARGET) + + unset TARGET_RK_GRALLOC_VERSION + export TARGET_RK_GRALLOC_VERSION=$(get_build_var TARGET_RK_GRALLOC_VERSION) + + unset BOARD_SUPPORT_MULTIAUDIO + export BOARD_SUPPORT_MULTIAUDIO=$(get_build_var BOARD_SUPPORT_MULTIAUDIO) + + unset TARGET_BOARD_PLATFORM_EBOOK + export TARGET_BOARD_PLATFORM_EBOOK=$(get_build_var BUILD_WITH_RK_EBOOK) + + # needed for building linux on MacOS + # TODO: fix the path + #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) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." >&2 + return + fi + get_build_var report_config +} + +function set_stuff_for_environment() +{ + setpaths + set_sequence_number + + export ANDROID_BUILD_TOP=$(gettop) + # With this environment variable new GCC can apply colors to warnings/errors + export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' +} + +function set_sequence_number() +{ + export BUILD_ENV_SEQUENCE_NUMBER=13 +} + +# Takes a command name, and check if it's in ENVSETUP_NO_COMPLETION or not. +function should_add_completion() { + local cmd="$(basename $1| sed 's/_completion//' |sed 's/\.\(.*\)*sh$//')" + case :"$ENVSETUP_NO_COMPLETION": in + *:"$cmd":*) + return 1 + ;; + esac + return 0 +} + +function addcompletions() +{ + local f= + + # Keep us from trying to run in something that's neither bash nor zsh. + if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then + return + fi + + # Keep us from trying to run in bash that's too old. + if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then + return + fi + + local completion_files=( + system/core/adb/adb.bash + system/core/fastboot/fastboot.bash + tools/asuite/asuite.sh + ) + # Completion can be disabled selectively to allow users to use non-standard completion. + # e.g. + # ENVSETUP_NO_COMPLETION=adb # -> disable adb completion + # ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion + for f in ${completion_files[*]}; do + if [ -f "$f" ] && should_add_completion "$f"; then + . $f + fi + done + + if should_add_completion bit ; then + complete -C "bit --tab" bit + fi + if [ -z "$ZSH_VERSION" ]; then + # Doesn't work in zsh. + complete -o nospace -F _croot croot + fi + complete -F _lunch lunch + + complete -F _complete_android_module_names pathmod + complete -F _complete_android_module_names gomod + complete -F _complete_android_module_names outmod + complete -F _complete_android_module_names installmod + complete -F _complete_android_module_names m +} + +function multitree_lunch_help() +{ + echo "usage: lunch PRODUCT-VARIANT" 1>&2 + echo " Set up android build environment based on a product short name and variant" 1>&2 + echo 1>&2 + echo "lunch COMBO_FILE VARIANT" 1>&2 + echo " Set up android build environment based on a specific lunch combo file" 1>&2 + echo " and variant." 1>&2 + echo 1>&2 + echo "lunch --print [CONFIG]" 1>&2 + echo " Print the contents of a configuration. If CONFIG is supplied, that config" 1>&2 + echo " will be flattened and printed. If CONFIG is not supplied, the currently" 1>&2 + echo " selected config will be printed. Returns 0 on success or nonzero on error." 1>&2 + echo 1>&2 + echo "lunch --list" 1>&2 + echo " List all possible combo files available in the current tree" 1>&2 + echo 1>&2 + echo "lunch --help" 1>&2 + echo "lunch -h" 1>&2 + echo " Prints this message." 1>&2 +} + +function multitree_lunch() +{ + local code + local results + if $(echo "$1" | grep -q '^-') ; then + # Calls starting with a -- argument are passed directly and the function + # returns with the lunch.py exit code. + build/make/orchestrator/core/lunch.py "$@" + code=$? + if [[ $code -eq 2 ]] ; then + echo 1>&2 + multitree_lunch_help + return $code + elif [[ $code -ne 0 ]] ; then + return $code + fi + else + # All other calls go through the --lunch variant of lunch.py + results=($(build/make/orchestrator/core/lunch.py --lunch "$@")) + code=$? + if [[ $code -eq 2 ]] ; then + echo 1>&2 + multitree_lunch_help + return $code + elif [[ $code -ne 0 ]] ; then + return $code + fi + + export TARGET_BUILD_COMBO=${results[0]} + export TARGET_BUILD_VARIANT=${results[1]} + fi +} + +function choosetype() +{ + echo "Build type choices are:" + echo " 1. release" + echo " 2. debug" + echo + + local DEFAULT_NUM DEFAULT_VALUE + DEFAULT_NUM=1 + DEFAULT_VALUE=release + + export TARGET_BUILD_TYPE= + local ANSWER + while [ -z $TARGET_BUILD_TYPE ] + do + echo -n "Which would you like? ["$DEFAULT_NUM"] " + if [ -z "$1" ] ; then + read ANSWER + else + echo $1 + ANSWER=$1 + fi + case $ANSWER in + "") + export TARGET_BUILD_TYPE=$DEFAULT_VALUE + ;; + 1) + export TARGET_BUILD_TYPE=release + ;; + release) + export TARGET_BUILD_TYPE=release + ;; + 2) + export TARGET_BUILD_TYPE=debug + ;; + debug) + export TARGET_BUILD_TYPE=debug + ;; + *) + echo + echo "I didn't understand your response. Please try again." + echo + ;; + esac + if [ -n "$1" ] ; then + break + fi + done + + build_build_var_cache + set_stuff_for_environment + destroy_build_var_cache +} + +# +# This function isn't really right: It chooses a TARGET_PRODUCT +# based on the list of boards. Usually, that gets you something +# that kinda works with a generic product, but really, you should +# pick a product by name. +# +function chooseproduct() +{ + local default_value + if [ "x$TARGET_PRODUCT" != x ] ; then + default_value=$TARGET_PRODUCT + else + default_value=aosp_arm + fi + + export TARGET_BUILD_APPS= + export TARGET_PRODUCT= + local ANSWER + while [ -z "$TARGET_PRODUCT" ] + do + echo -n "Which product would you like? [$default_value] " + if [ -z "$1" ] ; then + read ANSWER + else + echo $1 + ANSWER=$1 + fi + + if [ -z "$ANSWER" ] ; then + export TARGET_PRODUCT=$default_value + else + if check_product $ANSWER + then + export TARGET_PRODUCT=$ANSWER + else + echo "** Not a valid product: $ANSWER" + fi + fi + if [ -n "$1" ] ; then + break + fi + done + + build_build_var_cache + set_stuff_for_environment + destroy_build_var_cache +} + +function choosevariant() +{ + echo "Variant choices are:" + local index=1 + local v + for v in ${VARIANT_CHOICES[@]} + do + # The product name is the name of the directory containing + # the makefile we found, above. + echo " $index. $v" + index=$(($index+1)) + done + + local default_value=eng + local ANSWER + + export TARGET_BUILD_VARIANT= + while [ -z "$TARGET_BUILD_VARIANT" ] + do + echo -n "Which would you like? [$default_value] " + if [ -z "$1" ] ; then + read ANSWER + else + echo $1 + ANSWER=$1 + fi + + if [ -z "$ANSWER" ] ; then + export TARGET_BUILD_VARIANT=$default_value + elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ; then + if [ "$ANSWER" -le "${#VARIANT_CHOICES[@]}" ] ; then + export TARGET_BUILD_VARIANT=${VARIANT_CHOICES[@]:$(($ANSWER-1)):1} + fi + else + if check_variant $ANSWER + then + export TARGET_BUILD_VARIANT=$ANSWER + else + echo "** Not a valid variant: $ANSWER" + fi + fi + if [ -n "$1" ] ; then + break + fi + done +} + +function choosecombo() +{ + choosetype $1 + + echo + echo + chooseproduct $2 + + echo + echo + choosevariant $3 + + echo + build_build_var_cache + set_stuff_for_environment + printconfig + destroy_build_var_cache +} + +function add_lunch_combo() +{ + if [ -n "$ZSH_VERSION" ]; then + echo -n "${funcfiletrace[1]}: " + else + echo -n "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: " + fi + echo "add_lunch_combo is obsolete. Use COMMON_LUNCH_CHOICES in your AndroidProducts.mk instead." +} + +function print_lunch_menu() +{ + local uname=$(uname) + local choices + choices=$(TARGET_BUILD_APPS= TARGET_PRODUCT= TARGET_BUILD_VARIANT= get_build_var COMMON_LUNCH_CHOICES 2>/dev/null) + local ret=$? + + echo + echo "You're building on" $uname + echo + + if [ $ret -ne 0 ] + then + echo "Warning: Cannot display lunch menu." + echo + echo "Note: You can invoke lunch with an explicit target:" + echo + echo " usage: lunch [target]" >&2 + echo + return + fi + + echo "Lunch menu .. Here are the common combinations:" + + local i=1 + local choice + for choice in $(echo $choices) + do + echo " $i. $choice" + i=$(($i+1)) + done + + echo +} + +function lunch() +{ + local answer + + if [[ $# -gt 1 ]]; then + echo "usage: lunch [target]" >&2 + return 1 + fi + + local used_lunch_menu=0 + + if [ "$1" ]; then + answer=$1 + else + print_lunch_menu + echo "Which would you like? [aosp_arm-eng]" + echo -n "Pick from common choices above (e.g. 13) or specify your own (e.g. aosp_barbet-eng): " + read answer + used_lunch_menu=1 + fi + + local selection= + + if [ -z "$answer" ] + then + selection=aosp_arm-eng + elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$") + then + local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)) + if [ $answer -le ${#choices[@]} ] + then + # array in zsh starts from 1 instead of 0. + if [ -n "$ZSH_VERSION" ] + then + selection=${choices[$(($answer))]} + else + selection=${choices[$(($answer-1))]} + fi + fi + else + selection=$answer + fi + + export TARGET_BUILD_APPS= + + local product variant_and_version variant version + product=${selection%%-*} # Trim everything after first dash + variant_and_version=${selection#*-} # Trim everything up to first dash + if [ "$variant_and_version" != "$selection" ]; then + variant=${variant_and_version%%-*} + if [ "$variant" != "$variant_and_version" ]; then + version=${variant_and_version#*-} + fi + fi + + if [ -z "$product" ] + then + echo + echo "Invalid lunch combo: $selection" + return 1 + fi + + TARGET_PRODUCT=$product \ + TARGET_BUILD_VARIANT=$variant \ + TARGET_PLATFORM_VERSION=$version \ + build_build_var_cache + if [ $? -ne 0 ] + then + if [[ "$product" =~ .*_(eng|user|userdebug) ]] + then + echo "Did you mean -${product/*_/}? (dash instead of underscore)" + fi + return 1 + fi + export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT) + export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT) + if [ -n "$version" ]; then + export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION) + else + unset TARGET_PLATFORM_VERSION + fi + export TARGET_BUILD_TYPE=release + + if [ $used_lunch_menu -eq 1 ]; then + echo + echo "Hint: next time you can simply run 'lunch $selection'" + fi + + [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || echo + + set_stuff_for_environment + [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || printconfig + destroy_build_var_cache + + if [[ -n "${CHECK_MU_CONFIG:-}" ]]; then + check_mu_config + fi +} + +unset COMMON_LUNCH_CHOICES_CACHE +# Tab completion for lunch. +function _lunch() +{ + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [ -z "$COMMON_LUNCH_CHOICES_CACHE" ]; then + COMMON_LUNCH_CHOICES_CACHE=$(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES) + fi + + COMPREPLY=( $(compgen -W "${COMMON_LUNCH_CHOICES_CACHE}" -- ${cur}) ) + return 0 +} + +# Configures the build to build unbundled apps. +# Run tapas with one or more app names (from LOCAL_PACKAGE_NAME) +function tapas() +{ + local showHelp="$(echo $* | xargs -n 1 echo | \grep -E '^(help)$' | xargs)" + local arch="$(echo $* | xargs -n 1 echo | \grep -E '^(arm|x86|arm64|x86_64)$' | xargs)" + local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)" + local density="$(echo $* | xargs -n 1 echo | \grep -E '^(ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi)$' | xargs)" + local keys="$(echo $* | xargs -n 1 echo | \grep -E '^(devkeys)$' | xargs)" + local apps="$(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|arm|x86|arm64|x86_64|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi|devkeys)$' | xargs)" + + + if [ "$showHelp" != "" ]; then + $(gettop)/build/make/tapasHelp.sh + return + fi + + if [ $(echo $arch | wc -w) -gt 1 ]; then + echo "tapas: Error: Multiple build archs supplied: $arch" + return + fi + if [ $(echo $variant | wc -w) -gt 1 ]; then + echo "tapas: Error: Multiple build variants supplied: $variant" + return + fi + if [ $(echo $density | wc -w) -gt 1 ]; then + echo "tapas: Error: Multiple densities supplied: $density" + return + fi + if [ $(echo $keys | wc -w) -gt 1 ]; then + echo "tapas: Error: Multiple keys supplied: $keys" + return + fi + + local product=aosp_arm + case $arch in + x86) product=aosp_x86;; + arm64) product=aosp_arm64;; + x86_64) product=aosp_x86_64;; + esac + if [ -n "$keys" ]; then + product=${product/aosp_/aosp_${keys}_} + fi; + + if [ -z "$variant" ]; then + variant=eng + fi + if [ -z "$apps" ]; then + apps=all + fi + if [ -z "$density" ]; then + density=alldpi + fi + + export TARGET_PRODUCT=$product + export TARGET_BUILD_VARIANT=$variant + export TARGET_BUILD_DENSITY=$density + export TARGET_BUILD_TYPE=release + export TARGET_BUILD_APPS=$apps + + build_build_var_cache + set_stuff_for_environment + printconfig + destroy_build_var_cache +} + +# Configures the build to build unbundled Android modules (APEXes). +# Run banchan with one or more module names (from apex{} modules). +function banchan() +{ + local showHelp="$(echo $* | xargs -n 1 echo | \grep -E '^(help)$' | xargs)" + local product="$(echo $* | xargs -n 1 echo | \grep -E '^(.*_)?(arm|x86|arm64|x86_64)$' | xargs)" + local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)" + local apps="$(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|(.*_)?(arm|x86|arm64|x86_64))$' | xargs)" + + if [ "$showHelp" != "" ]; then + $(gettop)/build/make/banchanHelp.sh + return + fi + + if [ -z "$product" ]; then + product=arm + elif [ $(echo $product | wc -w) -gt 1 ]; then + echo "banchan: Error: Multiple build archs or products supplied: $products" + return + fi + if [ $(echo $variant | wc -w) -gt 1 ]; then + echo "banchan: Error: Multiple build variants supplied: $variant" + return + fi + if [ -z "$apps" ]; then + echo "banchan: Error: No modules supplied" + return + fi + + case $product in + arm) product=module_arm;; + x86) product=module_x86;; + arm64) product=module_arm64;; + x86_64) product=module_x86_64;; + esac + if [ -z "$variant" ]; then + variant=eng + fi + + export TARGET_PRODUCT=$product + export TARGET_BUILD_VARIANT=$variant + export TARGET_BUILD_DENSITY=alldpi + export TARGET_BUILD_TYPE=release + + # This setup currently uses TARGET_BUILD_APPS just like tapas, but the use + # case is different and it may diverge in the future. + export TARGET_BUILD_APPS=$apps + + build_build_var_cache + set_stuff_for_environment + printconfig + destroy_build_var_cache +} + +function gettop +{ + local TOPFILE=build/make/core/envsetup.mk + if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then + # The following circumlocution ensures we remove symlinks from TOP. + (cd "$TOP"; PWD= /bin/pwd) + else + if [ -f $TOPFILE ] ; then + # The following circumlocution (repeated below as well) ensures + # that we record the true directory name and not one that is + # faked up with symlink names. + PWD= /bin/pwd + else + local HERE=$PWD + local T= + while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do + \cd .. + T=`PWD= /bin/pwd -P` + done + \cd "$HERE" + if [ -f "$T/$TOPFILE" ]; then + echo "$T" + fi + fi + fi +} + +function croot() +{ + local T=$(gettop) + if [ "$T" ]; then + if [ "$1" ]; then + \cd $(gettop)/$1 + else + \cd $(gettop) + fi + else + echo "Couldn't locate the top of the tree. Try setting TOP." + fi +} + +function _croot() +{ + local T=$(gettop) + if [ "$T" ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" + k=0 + for c in $(compgen -d ${T}/${cur}); do + COMPREPLY[k++]=${c#${T}/}/ + done + fi +} + +function cproj() +{ + local TOPFILE=build/make/core/envsetup.mk + local HERE=$PWD + local T= + while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do + T=$PWD + if [ -f "$T/Android.mk" ]; then + \cd $T + return + fi + \cd .. + done + \cd $HERE + echo "can't find Android.mk" +} + +# simplified version of ps; output in the form +# +function qpid() { + local prepend='' + local append='' + if [ "$1" = "--exact" ]; then + prepend=' ' + append='$' + shift + elif [ "$1" = "--help" -o "$1" = "-h" ]; then + echo "usage: qpid [[--exact] " + return 255 + fi + + local EXE="$1" + if [ "$EXE" ] ; then + qpid | \grep "$prepend$EXE$append" + else + adb shell ps \ + | tr -d '\r' \ + | sed -e 1d -e 's/^[^ ]* *\([0-9]*\).* \([^ ]*\)$/\1 \2/' + fi +} + +# syswrite - disable verity, reboot if needed, and remount image +# +# Easy way to make system.img/etc writable +function syswrite() { + adb wait-for-device && adb root || return 1 + if [[ $(adb disable-verity | grep "reboot") ]]; then + echo "rebooting" + adb reboot && adb wait-for-device && adb root || return 1 + fi + adb wait-for-device && adb remount || return 1 +} + +# coredump_setup - enable core dumps globally for any process +# that has the core-file-size limit set correctly +# +# NOTE: You must call also coredump_enable for a specific process +# if its core-file-size limit is not set already. +# NOTE: Core dumps are written to ramdisk; they will not survive a reboot! + +function coredump_setup() +{ + echo "Getting root..."; + adb root; + adb wait-for-device; + + echo "Remounting root partition read-write..."; + adb shell mount -w -o remount -t rootfs rootfs; + sleep 1; + adb wait-for-device; + adb shell mkdir -p /cores; + adb shell mount -t tmpfs tmpfs /cores; + adb shell chmod 0777 /cores; + + echo "Granting SELinux permission to dump in /cores..."; + adb shell restorecon -R /cores; + + echo "Set core pattern."; + adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern'; + + echo "Done." +} + +# coredump_enable - enable core dumps for the specified process +# $1 = PID of process (e.g., $(pid mediaserver)) +# +# NOTE: coredump_setup must have been called as well for a core +# dump to actually be generated. + +function coredump_enable() +{ + local PID=$1; + if [ -z "$PID" ]; then + printf "Expecting a PID!\n"; + return; + fi; + echo "Setting core limit for $PID to infinite..."; + adb shell /system/bin/ulimit -p $PID -c unlimited +} + +# core - send SIGV and pull the core for process +# $1 = PID of process (e.g., $(pid mediaserver)) +# +# NOTE: coredump_setup must be called once per boot for core dumps to be +# enabled globally. + +function core() +{ + local PID=$1; + + if [ -z "$PID" ]; then + printf "Expecting a PID!\n"; + return; + fi; + + local CORENAME=core.$PID; + local COREPATH=/cores/$CORENAME; + local SIG=SEGV; + + coredump_enable $1; + + local done=0; + while [ $(adb shell "[ -d /proc/$PID ] && echo -n yes") ]; do + printf "\tSending SIG%s to %d...\n" $SIG $PID; + adb shell kill -$SIG $PID; + sleep 1; + done; + + adb shell "while [ ! -f $COREPATH ] ; do echo waiting for $COREPATH to be generated; sleep 1; done" + echo "Done: core is under $COREPATH on device."; +} + +# systemstack - dump the current stack trace of all threads in the system process +# to the usual ANR traces file +function systemstack() +{ + stacks system_server +} + +# Read the ELF header from /proc/$PID/exe to determine if the process is +# 64-bit. +function is64bit() +{ + local PID="$1" + if [ "$PID" ] ; then + if [[ "$(adb shell cat /proc/$PID/exe | xxd -l 1 -s 4 -p)" -eq "02" ]] ; then + echo "64" + else + echo "" + fi + else + echo "" + fi +} + +case `uname -s` in + Darwin) + function sgrep() + { + find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cc|cpp|hpp|S|java|kt|xml|sh|mk|aidl|vts|proto)' \ + -exec grep --color -n "$@" {} + + } + + ;; + *) + function sgrep() + { + find . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.\(c\|h\|cc\|cpp\|hpp\|S\|java\|kt\|xml\|sh\|mk\|aidl\|vts\|proto\)' \ + -exec grep --color -n "$@" {} + + } + ;; +esac + +function gettargetarch +{ + get_build_var TARGET_ARCH +} + +function ggrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \ + -exec grep --color -n "$@" {} + +} + +function gogrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \ + -exec grep --color -n "$@" {} + +} + +function jgrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \ + -exec grep --color -n "$@" {} + +} + +function rsgrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rs" \ + -exec grep --color -n "$@" {} + +} + +function ktgrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.kt" \ + -exec grep --color -n "$@" {} + +} + +function cgrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \ + -exec grep --color -n "$@" {} + +} + +function resgrep() +{ + local dir + for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do + find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} + + done +} + +function mangrep() +{ + find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \ + -exec grep --color -n "$@" {} + +} + +function owngrep() +{ + find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'OWNERS' \ + -exec grep --color -n "$@" {} + +} + +function sepgrep() +{ + find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \ + -exec grep --color -n -r --exclude-dir=\.git "$@" {} + +} + +function rcgrep() +{ + find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \ + -exec grep --color -n "$@" {} + +} + +case `uname -s` in + Darwin) + function mgrep() + { + find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \ + -exec grep --color -n "$@" {} + + } + + function treegrep() + { + find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cpp|hpp|S|java|kt|xml)' \ + -exec grep --color -n -i "$@" {} + + } + + ;; + *) + function mgrep() + { + find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regextype posix-extended -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \ + -exec grep --color -n "$@" {} + + } + + function treegrep() + { + find . -name .repo -prune -o -name .git -prune -o -regextype posix-egrep -iregex '.*\.(c|h|cpp|hpp|S|java|kt|xml)' -type f \ + -exec grep --color -n -i "$@" {} + + } + + ;; +esac + +function getprebuilt +{ + get_abs_build_var ANDROID_PREBUILTS +} + +function tracedmdump() +{ + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." + return + fi + local prebuiltdir=$(getprebuilt) + local arch=$(gettargetarch) + local KERNEL=$T/prebuilts/qemu-kernel/$arch/vmlinux-qemu + + local TRACE=$1 + if [ ! "$TRACE" ] ; then + echo "usage: tracedmdump tracename" + return + fi + + if [ ! -r "$KERNEL" ] ; then + echo "Error: cannot find kernel: '$KERNEL'" + return + fi + + local BASETRACE=$(basename $TRACE) + if [ "$BASETRACE" = "$TRACE" ] ; then + TRACE=$ANDROID_PRODUCT_OUT/traces/$TRACE + fi + + echo "post-processing traces..." + rm -f $TRACE/qtrace.dexlist + post_trace $TRACE + if [ $? -ne 0 ]; then + echo "***" + echo "*** Error: malformed trace. Did you remember to exit the emulator?" + echo "***" + return + fi + echo "generating dexlist output..." + /bin/ls $ANDROID_PRODUCT_OUT/system/framework/*.jar $ANDROID_PRODUCT_OUT/system/app/*.apk $ANDROID_PRODUCT_OUT/data/app/*.apk 2>/dev/null | xargs dexlist > $TRACE/qtrace.dexlist + echo "generating dmtrace data..." + q2dm -r $ANDROID_PRODUCT_OUT/symbols $TRACE $KERNEL $TRACE/dmtrace || return + echo "generating html file..." + dmtracedump -h $TRACE/dmtrace >| $TRACE/dmtrace.html || return + echo "done, see $TRACE/dmtrace.html for details" + echo "or run:" + echo " traceview $TRACE/dmtrace" +} + +# communicate with a running device or emulator, set up necessary state, +# and run the hat command. +function runhat() +{ + # process standard adb options + local adbTarget="" + if [ "$1" = "-d" -o "$1" = "-e" ]; then + adbTarget=$1 + shift 1 + elif [ "$1" = "-s" ]; then + adbTarget="$1 $2" + shift 2 + fi + local adbOptions=${adbTarget} + #echo adbOptions = ${adbOptions} + + # runhat options + local targetPid=$1 + + if [ "$targetPid" = "" ]; then + echo "Usage: runhat [ -d | -e | -s serial ] target-pid" + return + fi + + # confirm hat is available + if [ -z $(which hat) ]; then + echo "hat is not available in this configuration." + return + fi + + # issue "am" command to cause the hprof dump + local devFile=/data/local/tmp/hprof-$targetPid + echo "Poking $targetPid and waiting for data..." + echo "Storing data at $devFile" + adb ${adbOptions} shell am dumpheap $targetPid $devFile + echo "Press enter when logcat shows \"hprof: heap dump completed\"" + echo -n "> " + read + + local localFile=/tmp/$$-hprof + + echo "Retrieving file $devFile..." + adb ${adbOptions} pull $devFile $localFile + + adb ${adbOptions} shell rm $devFile + + echo "Running hat on $localFile" + echo "View the output by pointing your browser at http://localhost:7000/" + echo "" + hat -JXmx512m $localFile +} + +function getbugreports() +{ + local reports=(`adb shell ls /sdcard/bugreports | tr -d '\r'`) + + if [ ! "$reports" ]; then + echo "Could not locate any bugreports." + return + fi + + local report + for report in ${reports[@]} + do + echo "/sdcard/bugreports/${report}" + adb pull /sdcard/bugreports/${report} ${report} + gunzip ${report} + done +} + +function getsdcardpath() +{ + adb ${adbOptions} shell echo -n \$\{EXTERNAL_STORAGE\} +} + +function getscreenshotpath() +{ + echo "$(getsdcardpath)/Pictures/Screenshots" +} + +function getlastscreenshot() +{ + local screenshot_path=$(getscreenshotpath) + local screenshot=`adb ${adbOptions} ls ${screenshot_path} | grep Screenshot_[0-9-]*.*\.png | sort -rk 3 | cut -d " " -f 4 | head -n 1` + if [ "$screenshot" = "" ]; then + echo "No screenshots found." + return + fi + echo "${screenshot}" + adb ${adbOptions} pull ${screenshot_path}/${screenshot} +} + +function startviewserver() +{ + local port=4939 + if [ $# -gt 0 ]; then + port=$1 + fi + adb shell service call window 1 i32 $port +} + +function stopviewserver() +{ + adb shell service call window 2 +} + +function isviewserverstarted() +{ + adb shell service call window 3 +} + +function key_home() +{ + adb shell input keyevent 3 +} + +function key_back() +{ + adb shell input keyevent 4 +} + +function key_menu() +{ + adb shell input keyevent 82 +} + +function smoketest() +{ + if [ ! "$ANDROID_PRODUCT_OUT" ]; then + echo "Couldn't locate output files. Try running 'lunch' first." >&2 + return + fi + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." >&2 + return + fi + + (\cd "$T" && mmm tests/SmokeTest) && + adb uninstall com.android.smoketest > /dev/null && + adb uninstall com.android.smoketest.tests > /dev/null && + adb install $ANDROID_PRODUCT_OUT/data/app/SmokeTestApp.apk && + adb install $ANDROID_PRODUCT_OUT/data/app/SmokeTest.apk && + adb shell am instrument -w com.android.smoketest.tests/android.test.InstrumentationTestRunner +} + +# simple shortcut to the runtest command +function runtest() +{ + local T=$(gettop) + if [ ! "$T" ]; then + echo "Couldn't locate the top of the tree. Try setting TOP." >&2 + return + fi + ("$T"/development/testrunner/runtest.py $@) +} + +function godir () { + if [[ -z "$1" ]]; then + echo "Usage: godir " + return + fi + local T=$(gettop) + local FILELIST + if [ ! "$OUT_DIR" = "" ]; then + mkdir -p $OUT_DIR + FILELIST=$OUT_DIR/filelist + else + FILELIST=$T/filelist + fi + if [[ ! -f $FILELIST ]]; then + echo -n "Creating index..." + (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST) + echo " Done" + echo "" + fi + local lines + lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq)) + if [[ ${#lines[@]} = 0 ]]; then + echo "Not found" + return + fi + local pathname + local choice + if [[ ${#lines[@]} > 1 ]]; then + while [[ -z "$pathname" ]]; do + local index=1 + local line + for line in ${lines[@]}; do + printf "%6s %s\n" "[$index]" $line + index=$(($index + 1)) + done + echo + echo -n "Select one: " + unset choice + read choice + if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then + echo "Invalid choice" + continue + fi + pathname=${lines[@]:$(($choice-1)):1} + done + else + pathname=${lines[@]:0:1} + fi + \cd $T/$pathname +} + +# Update module-info.json in out. +function refreshmod() { + if [ ! "$ANDROID_PRODUCT_OUT" ]; then + echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2 + return 1 + fi + + echo "Refreshing modules (building module-info.json). Log at $ANDROID_PRODUCT_OUT/module-info.json.build.log." >&2 + + # for the output of the next command + mkdir -p $ANDROID_PRODUCT_OUT || return 1 + + # Note, can't use absolute path because of the way make works. + m $(get_build_var PRODUCT_OUT)/module-info.json \ + > $ANDROID_PRODUCT_OUT/module-info.json.build.log 2>&1 +} + +# Verifies that module-info.txt exists, returning nonzero if it doesn't. +function verifymodinfo() { + if [ ! "$ANDROID_PRODUCT_OUT" ]; then + if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then + echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2 + fi + return 1 + fi + + if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then + if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then + echo "Could not find module-info.json. Please run 'refreshmod' first." >&2 + fi + return 1 + 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. +function allmod() { + verifymodinfo || return 1 + + python3 -c "import json; print('\n'.join(sorted(json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')).keys())))" +} + +# Get the path of a specific module in the android tree, as cached in module-info.json. +# If any build change is made, and it should be reflected in the output, you should run +# 'refreshmod' first. Note: This is the inverse of dirmods. +function pathmod() { + if [[ $# -ne 1 ]]; then + echo "usage: pathmod " >&2 + return 1 + fi + + verifymodinfo || return 1 + + local relpath=$(python3 -c "import json, os +module = '$1' +module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')) +if module not in module_info: + exit(1) +print(module_info[module]['path'][0])" 2>/dev/null) + + if [ -z "$relpath" ]; then + echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)." >&2 + return 1 + else + echo "$ANDROID_BUILD_TOP/$relpath" + fi +} + +# Get the path of a specific module in the android tree, as cached in module-info.json. +# If any build change is made, and it should be reflected in the output, you should run +# 'refreshmod' first. Note: This is the inverse of pathmod. +function dirmods() { + if [[ $# -ne 1 ]]; then + echo "usage: dirmods " >&2 + return 1 + fi + + verifymodinfo || return 1 + + python3 -c "import json, os +dir = '$1' +while dir.endswith('/'): + dir = dir[:-1] +prefix = dir + '/' +module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')) +results = set() +for m in module_info.values(): + for path in m.get(u'path', []): + if path == dir or path.startswith(prefix): + name = m.get(u'module_name') + if name: + results.add(name) +for name in sorted(results): + print(name) +" +} + + +# Go to a specific module in the android tree, as cached in module-info.json. If any build change +# is made, and it should be reflected in the output, you should run 'refreshmod' first. +function gomod() { + if [[ $# -ne 1 ]]; then + echo "usage: gomod " >&2 + return 1 + fi + + local path="$(pathmod $@)" + if [ -z "$path" ]; then + return 1 + fi + cd $path +} + +# Gets the list of a module's installed outputs, 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. +function outmod() { + if [[ $# -ne 1 ]]; then + echo "usage: outmod " >&2 + return 1 + fi + + verifymodinfo || return 1 + + local relpath + relpath=$(python3 -c "import json, os +module = '$1' +module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')) +if module not in module_info: + exit(1) +for output in module_info[module]['installed']: + print(os.path.join('$ANDROID_BUILD_TOP', output))" 2>/dev/null) + + if [ $? -ne 0 ]; then + echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)" >&2 + return 1 + elif [ ! -z "$relpath" ]; then + echo "$relpath" + fi +} + +# adb install a module's apk, 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. +# Usage: installmod [adb install arguments] +# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk +function installmod() { + if [[ $# -eq 0 ]]; then + echo "usage: installmod [adb install arguments] " >&2 + echo "" >&2 + echo "Only flags to be passed after the \"install\" in adb install are supported," >&2 + echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2 + echo "-s must be the first flag passed if it exists." >&2 + return 1 + fi + + local _path + _path=$(outmod ${@:$#:1}) + if [ $? -ne 0 ]; then + return 1 + fi + + _path=$(echo "$_path" | grep -E \\.apk$ | head -n 1) + if [ -z "$_path" ]; then + echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2 + return 1 + fi + local serial_device="" + if [[ "$1" == "-s" ]]; then + if [[ $# -le 2 ]]; then + echo "-s requires an argument" >&2 + return 1 + fi + serial_device="-s $2" + shift 2 + fi + local length=$(( $# - 1 )) + echo adb $serial_device install ${@:1:$length} $_path + adb $serial_device install ${@:1:$length} $_path +} + +function _complete_android_module_names() { + local word=${COMP_WORDS[COMP_CWORD]} + COMPREPLY=( $(QUIET_VERIFYMODINFO=true allmod | grep -E "^$word") ) +} + +# Print colored exit condition +function pez { + "$@" + local retval=$? + if [ $retval -ne 0 ] + then + echo $'\E'"[0;31mFAILURE\e[00m" + else + echo $'\E'"[0;32mSUCCESS\e[00m" + fi + return $retval +} + +function get_make_command() +{ + # If we're in the top of an Android tree, use soong_ui.bash instead of make + if [ -f build/soong/soong_ui.bash ]; then + # Always use the real make if -C is passed in + for arg in "$@"; do + if [[ $arg == -C* ]]; then + echo command make + return + fi + done + echo build/soong/soong_ui.bash --make-mode + else + echo command make + fi +} + +function _wrap_build() +{ + if [[ "${ANDROID_QUIET_BUILD:-}" == true ]]; then + "$@" + return $? + fi + local start_time=$(date +"%s") + "$@" + local ret=$? + local end_time=$(date +"%s") + local tdiff=$(($end_time-$start_time)) + local hours=$(($tdiff / 3600 )) + local mins=$((($tdiff % 3600) / 60)) + local secs=$(($tdiff % 60)) + local ncolors=$(tput colors 2>/dev/null) + if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then + color_failed=$'\E'"[0;31m" + color_success=$'\E'"[0;32m" + color_warning=$'\E'"[0;33m" + color_reset=$'\E'"[00m" + else + color_failed="" + color_success="" + color_reset="" + fi + + if [[ "x${USE_RBE}" == "x" && $mins -gt 15 && "${ANDROID_BUILD_ENVIRONMENT_CONFIG}" == "googler" ]]; then + echo + echo "${color_warning}Start using RBE (http://go/build-fast) to get faster builds!${color_reset}" + fi + + echo + if [ $ret -eq 0 ] ; then + echo -n "${color_success}#### build completed successfully " + else + echo -n "${color_failed}#### failed to build some targets " + fi + if [ $hours -gt 0 ] ; then + printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs + elif [ $mins -gt 0 ] ; then + printf "(%02g:%02g (mm:ss))" $mins $secs + elif [ $secs -gt 0 ] ; then + printf "(%s seconds)" $secs + fi + echo " ####${color_reset}" + echo + return $ret +} + +function _trigger_build() +( + local -r bc="$1"; shift + if T="$(gettop)"; then + _wrap_build "$T/build/soong/soong_ui.bash" --build-mode --${bc} --dir="$(pwd)" "$@" + else + >&2 echo "Couldn't locate the top of the tree. Try setting TOP." + return 1 + fi +) + +# Convenience entry point (like m) to use Bazel in AOSP. +function b() +( + # Generate BUILD, bzl files into the synthetic Bazel workspace (out/soong/workspace). + _trigger_build "all-modules" bp2build USE_BAZEL_ANALYSIS= || return 1 + # Then, run Bazel using the synthetic workspace as the --package_path. + if [[ -z "$@" ]]; then + # If there are no args, show help. + bazel help + else + # Else, always run with the bp2build configuration, which sets Bazel's package path to the synthetic workspace. + bazel "$@" --config=bp2build + fi +) + +function m() +( + _trigger_build "all-modules" "$@" +) + +function mm() +( + _trigger_build "modules-in-a-dir-no-deps" "$@" +) + +function mmm() +( + _trigger_build "modules-in-dirs-no-deps" "$@" +) + +function mma() +( + _trigger_build "modules-in-a-dir" "$@" +) + +function mmma() +( + _trigger_build "modules-in-dirs" "$@" +) + +function make() +{ + _wrap_build $(get_make_command "$@") "$@" +} + +function provision() +{ + if [ ! "$ANDROID_PRODUCT_OUT" ]; then + echo "Couldn't locate output files. Try running 'lunch' first." >&2 + return 1 + fi + if [ ! -e "$ANDROID_PRODUCT_OUT/provision-device" ]; then + echo "There is no provisioning script for the device." >&2 + return 1 + fi + + # Check if user really wants to do this. + if [ "$1" = "--no-confirmation" ]; then + shift 1 + else + echo "This action will reflash your device." + echo "" + echo "ALL DATA ON THE DEVICE WILL BE IRREVOCABLY ERASED." + echo "" + echo -n "Are you sure you want to do this (yes/no)? " + read + if [[ "${REPLY}" != "yes" ]] ; then + echo "Not taking any action. Exiting." >&2 + return 1 + fi + fi + "$ANDROID_PRODUCT_OUT/provision-device" "$@" +} + +# Zsh needs bashcompinit called to support bash-style completion. +function enable_zsh_completion() { + # Don't override user's options if bash-style completion is already enabled. + if ! declare -f complete >/dev/null; then + autoload -U compinit && compinit + autoload -U bashcompinit && bashcompinit + fi +} + +function validate_current_shell() { + local current_sh="$(ps -o command -p $$)" + case "$current_sh" in + *bash*) + function check_type() { type -t "$1"; } + ;; + *zsh*) + function check_type() { type "$1"; } + enable_zsh_completion ;; + *) + echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results." + ;; + esac +} + +# Execute the contents of any vendorsetup.sh files we can find. +# Unless we find an allowed-vendorsetup_sh-files file, in which case we'll only +# load those. +# +# This allows loading only approved vendorsetup.sh files +function source_vendorsetup() { + unset VENDOR_PYTHONPATH + local T="$(gettop)" + allowed= + for f in $(cd "$T" && find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do + if [ -n "$allowed" ]; then + echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:" + echo " $allowed" + echo " $f" + return + fi + allowed="$T/$f" + done + + allowed_files= + [ -n "$allowed" ] && allowed_files=$(cat "$allowed") + for dir in device vendor product; do + for f in $(cd "$T" && test -d $dir && \ + find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do + + if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then + echo "including $f"; . "$T/$f" + else + echo "ignoring $f, not in $allowed" + fi + done + done +} + +function showcommands() { + local T=$(gettop) + if [[ -z "$TARGET_PRODUCT" ]]; then + >&2 echo "TARGET_PRODUCT not set. Run lunch." + return + fi + case $(uname -s) in + Darwin) + PREBUILT_NAME=darwin-x86 + ;; + Linux) + PREBUILT_NAME=linux-x86 + ;; + *) + >&2 echo Unknown host $(uname -s) + return + ;; + esac + 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 + if [[ "$1" == "--regenerate" ]]; then + shift 1 + NINJA_ARGS="-t commands $@" m + else + (cd $T && prebuilts/build-tools/$PREBUILT_NAME/bin/ninja \ + -f $OUT_DIR/combined-${TARGET_PRODUCT}.ninja \ + -t commands "$@") + fi +} + +validate_current_shell +source_vendorsetup +addcompletions diff --git a/make/finalize_branch_for_release.sh b/make/finalize_branch_for_release.sh new file mode 100755 index 0000000..d498beb --- /dev/null +++ b/make/finalize_branch_for_release.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -e + +source ../envsetup.sh + +# default target to modify tree and build SDK +lunch aosp_arm64-userdebug + +set -x + +# This script is WIP and only finalizes part of the Android branch for release. +# The full process can be found at (INTERNAL) go/android-sdk-finalization. + +# VNDK snapshot (TODO) +# SDK snapshots (TODO) +# Update references in the codebase to new API version (TODO) +# ... + +AIDL_TRANSITIVE_FREEZE=true m aidl-freeze-api + +# TODO(b/229413853): test while simulating 'rel' for more requirements AIDL_FROZEN_REL=true +m # test build + +# Build SDK (TODO) +# lunch sdk... +# m ... diff --git a/make/help.sh b/make/help.sh new file mode 100755 index 0000000..e51adc1 --- /dev/null +++ b/make/help.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# locate some directories +cd "$(dirname $0)" +SCRIPT_DIR="${PWD}" +cd ../.. +TOP="${PWD}" + +message='The basic Android build process is: + +cd '"${TOP}"' +source build/envsetup.sh # Add "lunch" (and other utilities and variables) + # to the shell environment. +lunch [-] # Choose the device to target. +m [] # Execute the configured build. + +Usage of "m" imitates usage of the program "make". +See '"${SCRIPT_DIR}"'/Usage.txt for more info about build usage and concepts. + +The parallelism of the build can be set with a -jN argument to "m". If you +don'\''t provide a -j argument, the build system automatically selects a parallel +task count that it thinks is optimal for your system. + +Common goals are: + + clean (aka clobber) equivalent to rm -rf out/ + checkbuild Build every module defined in the source tree + droid Default target + nothing Do not build anything, just parse and validate the build structure + + java Build all the java code in the source tree + native Build all the native code in the source tree + + host Build all the host code (not to be run on a device) in the source tree + target Build all the target code (to be run on the device) in the source tree + + (java|native)-(host|target) + (host|target)-(java|native) + Build the intersection of the two given arguments + + snod Quickly rebuild the system image from built packages + Stands for "System, NO Dependencies" + vnod Quickly rebuild the vendor image from built packages + Stands for "Vendor, NO Dependencies" + pnod Quickly rebuild the product image from built packages + Stands for "Product, NO Dependencies" + senod Quickly rebuild the system_ext image from built packages + Stands for "SystemExt, NO Dependencies" + onod Quickly rebuild the odm image from built packages + Stands for "Odm, NO Dependencies" + vdnod Quickly rebuild the vendor_dlkm image from built packages + Stands for "VendorDlkm, NO Dependencies" + odnod Quickly rebuild the odm_dlkm image from built packages + Stands for "OdmDlkm, NO Dependencies" + sdnod Quickly rebuild the system_dlkm image from built packages + Stands for "SystemDlkm, NO Dependencies" + + +So, for example, you could run: + +cd '"${TOP}"' +source build/envsetup.sh +lunch aosp_arm-userdebug +m -j java + +to build all of the java code for the userdebug variant of the aosp_arm device. +' + +echo "$message" diff --git a/make/navbar.md b/make/navbar.md new file mode 100644 index 0000000..e218a78 --- /dev/null +++ b/make/navbar.md @@ -0,0 +1,4 @@ +* [Home](/README.md) +* [Usage](/Usage.txt) +* [Changes](/Changes.md) +* [Outdated Reference](/core/build-system.html) diff --git a/make/orchestrator/core/lunch.py b/make/orchestrator/core/lunch.py new file mode 100755 index 0000000..35dac73 --- /dev/null +++ b/make/orchestrator/core/lunch.py @@ -0,0 +1,329 @@ +#!/usr/bin/python3 +# +# 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. + +import argparse +import glob +import json +import os +import sys + +EXIT_STATUS_OK = 0 +EXIT_STATUS_ERROR = 1 +EXIT_STATUS_NEED_HELP = 2 + +def FindDirs(path, name, ttl=6): + """Search at most ttl directories deep inside path for a directory called name.""" + # The dance with subdirs is so that we recurse in sorted order. + subdirs = [] + with os.scandir(path) as it: + for dirent in sorted(it, key=lambda x: x.name): + try: + if dirent.is_dir(): + if dirent.name == name: + yield os.path.join(path, dirent.name) + elif ttl > 0: + subdirs.append(dirent.name) + except OSError: + # Consume filesystem errors, e.g. too many links, permission etc. + pass + for subdir in subdirs: + yield from FindDirs(os.path.join(path, subdir), name, ttl-1) + + +def WalkPaths(path, matcher, ttl=10): + """Do a traversal of all files under path yielding each file that matches + matcher.""" + # First look for files, then recurse into directories as needed. + # The dance with subdirs is so that we recurse in sorted order. + subdirs = [] + with os.scandir(path) as it: + for dirent in sorted(it, key=lambda x: x.name): + try: + if dirent.is_file(): + if matcher(dirent.name): + yield os.path.join(path, dirent.name) + if dirent.is_dir(): + if ttl > 0: + subdirs.append(dirent.name) + except OSError: + # Consume filesystem errors, e.g. too many links, permission etc. + pass + for subdir in sorted(subdirs): + yield from WalkPaths(os.path.join(path, subdir), matcher, ttl-1) + + +def FindFile(path, filename): + """Return a file called filename inside path, no more than ttl levels deep. + + Directories are searched alphabetically. + """ + for f in WalkPaths(path, lambda x: x == filename): + return f + + +def FindConfigDirs(workspace_root): + """Find the configuration files in the well known locations inside workspace_root + + /build/orchestrator/multitree_combos + (AOSP devices, such as cuttlefish) + + /vendor/**/multitree_combos + (specific to a vendor and not open sourced) + + /device/**/multitree_combos + (specific to a vendor and are open sourced) + + Directories are returned specifically in this order, so that aosp can't be + overridden, but vendor overrides device. + """ + + # TODO: When orchestrator is in its own git project remove the "make/" here + yield os.path.join(workspace_root, "build/make/orchestrator/multitree_combos") + + dirs = ["vendor", "device"] + for d in dirs: + yield from FindDirs(os.path.join(workspace_root, d), "multitree_combos") + + +def FindNamedConfig(workspace_root, shortname): + """Find the config with the given shortname inside workspace_root. + + Config directories are searched in the order described in FindConfigDirs, + and inside those directories, alphabetically.""" + filename = shortname + ".mcombo" + for config_dir in FindConfigDirs(workspace_root): + found = FindFile(config_dir, filename) + if found: + return found + return None + + +def ParseProductVariant(s): + """Split a PRODUCT-VARIANT name, or return None if it doesn't match that pattern.""" + split = s.split("-") + if len(split) != 2: + return None + return split + + +def ChooseConfigFromArgs(workspace_root, args): + """Return the config file we should use for the given argument, + or null if there's no file that matches that.""" + if len(args) == 1: + # Prefer PRODUCT-VARIANT syntax so if there happens to be a matching + # file we don't match that. + pv = ParseProductVariant(args[0]) + if pv: + config = FindNamedConfig(workspace_root, pv[0]) + if config: + return (config, pv[1]) + return None, None + # Look for a specifically named file + if os.path.isfile(args[0]): + return (args[0], args[1] if len(args) > 1 else None) + # That file didn't exist, return that we didn't find it. + return None, None + + +class ConfigException(Exception): + ERROR_PARSE = "parse" + ERROR_CYCLE = "cycle" + + def __init__(self, kind, message, locations, line=0): + """Error thrown when loading and parsing configurations. + + Args: + message: Error message to display to user + locations: List of filenames of the include history. The 0 index one + the location where the actual error occurred + """ + if len(locations): + s = locations[0] + if line: + s += ":" + s += str(line) + s += ": " + else: + s = "" + s += message + if len(locations): + for loc in locations[1:]: + s += "\n included from %s" % loc + super().__init__(s) + self.kind = kind + self.message = message + self.locations = locations + self.line = line + + +def LoadConfig(filename): + """Load a config, including processing the inherits fields. + + Raises: + ConfigException on errors + """ + def LoadAndMerge(fn, visited): + with open(fn) as f: + try: + contents = json.load(f) + except json.decoder.JSONDecodeError as ex: + if True: + raise ConfigException(ConfigException.ERROR_PARSE, ex.msg, visited, ex.lineno) + else: + sys.stderr.write("exception %s" % ex.__dict__) + raise ex + # Merge all the parents into one data, with first-wins policy + inherited_data = {} + for parent in contents.get("inherits", []): + if parent in visited: + raise ConfigException(ConfigException.ERROR_CYCLE, "Cycle detected in inherits", + visited) + DeepMerge(inherited_data, LoadAndMerge(parent, [parent,] + visited)) + # Then merge inherited_data into contents, but what's already there will win. + DeepMerge(contents, inherited_data) + contents.pop("inherits", None) + return contents + return LoadAndMerge(filename, [filename,]) + + +def DeepMerge(merged, addition): + """Merge all fields of addition into merged. Pre-existing fields win.""" + for k, v in addition.items(): + if k in merged: + if isinstance(v, dict) and isinstance(merged[k], dict): + DeepMerge(merged[k], v) + else: + merged[k] = v + + +def Lunch(args): + """Handle the lunch command.""" + # Check that we're at the top of a multitree workspace + # TODO: Choose the right sentinel file + if not os.path.exists("build/make/orchestrator"): + sys.stderr.write("ERROR: lunch.py must be run from the root of a multi-tree workspace\n") + return EXIT_STATUS_ERROR + + # Choose the config file + config_file, variant = ChooseConfigFromArgs(".", args) + + if config_file == None: + sys.stderr.write("Can't find lunch combo file for: %s\n" % " ".join(args)) + return EXIT_STATUS_NEED_HELP + if variant == None: + sys.stderr.write("Can't find variant for: %s\n" % " ".join(args)) + return EXIT_STATUS_NEED_HELP + + # Parse the config file + try: + config = LoadConfig(config_file) + except ConfigException as ex: + sys.stderr.write(str(ex)) + return EXIT_STATUS_ERROR + + # Fail if the lunchable bit isn't set, because this isn't a usable config + if not config.get("lunchable", False): + sys.stderr.write("%s: Lunch config file (or inherited files) does not have the 'lunchable'" + % config_file) + sys.stderr.write(" flag set, which means it is probably not a complete lunch spec.\n") + + # All the validation has passed, so print the name of the file and the variant + sys.stdout.write("%s\n" % config_file) + sys.stdout.write("%s\n" % variant) + + return EXIT_STATUS_OK + + +def FindAllComboFiles(workspace_root): + """Find all .mcombo files in the prescribed locations in the tree.""" + for dir in FindConfigDirs(workspace_root): + for file in WalkPaths(dir, lambda x: x.endswith(".mcombo")): + yield file + + +def IsFileLunchable(config_file): + """Parse config_file, flatten the inheritance, and return whether it can be + used as a lunch target.""" + try: + config = LoadConfig(config_file) + except ConfigException as ex: + sys.stderr.write("%s" % ex) + return False + return config.get("lunchable", False) + + +def FindAllLunchable(workspace_root): + """Find all mcombo files in the tree (rooted at workspace_root) that when + parsed (and inheritance is flattened) have lunchable: true.""" + for f in [x for x in FindAllComboFiles(workspace_root) if IsFileLunchable(x)]: + yield f + + +def List(): + """Handle the --list command.""" + for f in sorted(FindAllLunchable(".")): + print(f) + + +def Print(args): + """Handle the --print command.""" + # Parse args + if len(args) == 0: + config_file = os.environ.get("TARGET_BUILD_COMBO") + if not config_file: + sys.stderr.write("TARGET_BUILD_COMBO not set. Run lunch or pass a combo file.\n") + return EXIT_STATUS_NEED_HELP + elif len(args) == 1: + config_file = args[0] + else: + return EXIT_STATUS_NEED_HELP + + # Parse the config file + try: + config = LoadConfig(config_file) + except ConfigException as ex: + sys.stderr.write(str(ex)) + return EXIT_STATUS_ERROR + + # Print the config in json form + json.dump(config, sys.stdout, indent=4) + + return EXIT_STATUS_OK + + +def main(argv): + if len(argv) < 2 or argv[1] == "-h" or argv[1] == "--help": + return EXIT_STATUS_NEED_HELP + + if len(argv) == 2 and argv[1] == "--list": + List() + return EXIT_STATUS_OK + + if len(argv) == 2 and argv[1] == "--print": + return Print(argv[2:]) + return EXIT_STATUS_OK + + if (len(argv) == 2 or len(argv) == 3) and argv[1] == "--lunch": + return Lunch(argv[2:]) + + sys.stderr.write("Unknown lunch command: %s\n" % " ".join(argv[1:])) + return EXIT_STATUS_NEED_HELP + +if __name__ == "__main__": + sys.exit(main(sys.argv)) + + +# vim: sts=4:ts=4:sw=4 diff --git a/make/orchestrator/core/test/configs/another/bad.mcombo b/make/orchestrator/core/test/configs/another/bad.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/another/bad.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/another/dir/a b/make/orchestrator/core/test/configs/another/dir/a new file mode 100644 index 0000000..7898192 --- /dev/null +++ b/make/orchestrator/core/test/configs/another/dir/a @@ -0,0 +1 @@ +a diff --git a/make/orchestrator/core/test/configs/b-eng b/make/orchestrator/core/test/configs/b-eng new file mode 100644 index 0000000..eceb3f3 --- /dev/null +++ b/make/orchestrator/core/test/configs/b-eng @@ -0,0 +1 @@ +INVALID FILE diff --git a/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/b.mcombo b/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/b.mcombo new file mode 100644 index 0000000..8cc8370 --- /dev/null +++ b/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/b.mcombo @@ -0,0 +1,3 @@ +{ + "lunchable": "true" +} diff --git a/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/nested/nested.mcombo b/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/nested/nested.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/nested/nested.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/not_a_combo.txt b/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/not_a_combo.txt new file mode 100644 index 0000000..f9805f2 --- /dev/null +++ b/make/orchestrator/core/test/configs/build/make/orchestrator/multitree_combos/not_a_combo.txt @@ -0,0 +1 @@ +not a combo file diff --git a/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/b.mcombo b/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/b.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/b.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/d.mcombo b/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/d.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/d.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/v.mcombo b/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/v.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/device/aa/bb/multitree_combos/v.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/device/this/one/is/deeper/than/will/be/found/by/the/ttl/multitree_combos/too_deep.mcombo b/make/orchestrator/core/test/configs/device/this/one/is/deeper/than/will/be/found/by/the/ttl/multitree_combos/too_deep.mcombo new file mode 100644 index 0000000..e69de29 diff --git a/make/orchestrator/core/test/configs/parsing/cycles/1.mcombo b/make/orchestrator/core/test/configs/parsing/cycles/1.mcombo new file mode 100644 index 0000000..ab8fe33 --- /dev/null +++ b/make/orchestrator/core/test/configs/parsing/cycles/1.mcombo @@ -0,0 +1,5 @@ +{ + "inherits": [ + "test/configs/parsing/cycles/2.mcombo" + ] +} diff --git a/make/orchestrator/core/test/configs/parsing/cycles/2.mcombo b/make/orchestrator/core/test/configs/parsing/cycles/2.mcombo new file mode 100644 index 0000000..2b774d0 --- /dev/null +++ b/make/orchestrator/core/test/configs/parsing/cycles/2.mcombo @@ -0,0 +1,6 @@ +{ + "inherits": [ + "test/configs/parsing/cycles/3.mcombo" + ] +} + diff --git a/make/orchestrator/core/test/configs/parsing/cycles/3.mcombo b/make/orchestrator/core/test/configs/parsing/cycles/3.mcombo new file mode 100644 index 0000000..41b629b --- /dev/null +++ b/make/orchestrator/core/test/configs/parsing/cycles/3.mcombo @@ -0,0 +1,6 @@ +{ + "inherits": [ + "test/configs/parsing/cycles/1.mcombo" + ] +} + diff --git a/make/orchestrator/core/test/configs/parsing/merge/1.mcombo b/make/orchestrator/core/test/configs/parsing/merge/1.mcombo new file mode 100644 index 0000000..a5a57d7 --- /dev/null +++ b/make/orchestrator/core/test/configs/parsing/merge/1.mcombo @@ -0,0 +1,13 @@ +{ + "inherits": [ + "test/configs/parsing/merge/2.mcombo", + "test/configs/parsing/merge/3.mcombo" + ], + "in_1": "1", + "in_1_2": "1", + "merged": { + "merged_1": "1", + "merged_1_2": "1" + }, + "dict_1": { "a" : "b" } +} diff --git a/make/orchestrator/core/test/configs/parsing/merge/2.mcombo b/make/orchestrator/core/test/configs/parsing/merge/2.mcombo new file mode 100644 index 0000000..00963e2 --- /dev/null +++ b/make/orchestrator/core/test/configs/parsing/merge/2.mcombo @@ -0,0 +1,12 @@ +{ + "in_1_2": "2", + "in_2": "2", + "in_2_3": "2", + "merged": { + "merged_1_2": "2", + "merged_2": "2", + "merged_2_3": "2" + }, + "dict_2": { "a" : "b" } +} + diff --git a/make/orchestrator/core/test/configs/parsing/merge/3.mcombo b/make/orchestrator/core/test/configs/parsing/merge/3.mcombo new file mode 100644 index 0000000..5fc9d90 --- /dev/null +++ b/make/orchestrator/core/test/configs/parsing/merge/3.mcombo @@ -0,0 +1,10 @@ +{ + "in_3": "3", + "in_2_3": "3", + "merged": { + "merged_3": "3", + "merged_2_3": "3" + }, + "dict_3": { "a" : "b" } +} + diff --git a/make/orchestrator/core/test/configs/vendor/aa/bb/multitree_combos/b.mcombo b/make/orchestrator/core/test/configs/vendor/aa/bb/multitree_combos/b.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/vendor/aa/bb/multitree_combos/b.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/vendor/aa/bb/multitree_combos/v.mcombo b/make/orchestrator/core/test/configs/vendor/aa/bb/multitree_combos/v.mcombo new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/make/orchestrator/core/test/configs/vendor/aa/bb/multitree_combos/v.mcombo @@ -0,0 +1 @@ +{} diff --git a/make/orchestrator/core/test/configs/vendor/this/one/is/deeper/than/will/be/found/by/the/ttl/multitree_combos/too_deep.mcombo b/make/orchestrator/core/test/configs/vendor/this/one/is/deeper/than/will/be/found/by/the/ttl/multitree_combos/too_deep.mcombo new file mode 100644 index 0000000..e69de29 diff --git a/make/orchestrator/core/test_lunch.py b/make/orchestrator/core/test_lunch.py new file mode 100755 index 0000000..3c39493 --- /dev/null +++ b/make/orchestrator/core/test_lunch.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import unittest + +sys.dont_write_bytecode = True +import lunch + +class TestStringMethods(unittest.TestCase): + + def test_find_dirs(self): + self.assertEqual([x for x in lunch.FindDirs("test/configs", "multitree_combos")], [ + "test/configs/build/make/orchestrator/multitree_combos", + "test/configs/device/aa/bb/multitree_combos", + "test/configs/vendor/aa/bb/multitree_combos"]) + + def test_find_file(self): + # Finds the one in device first because this is searching from the root, + # not using FindNamedConfig. + self.assertEqual(lunch.FindFile("test/configs", "v.mcombo"), + "test/configs/device/aa/bb/multitree_combos/v.mcombo") + + def test_find_config_dirs(self): + self.assertEqual([x for x in lunch.FindConfigDirs("test/configs")], [ + "test/configs/build/make/orchestrator/multitree_combos", + "test/configs/vendor/aa/bb/multitree_combos", + "test/configs/device/aa/bb/multitree_combos"]) + + def test_find_named_config(self): + # Inside build/orchestrator, overriding device and vendor + self.assertEqual(lunch.FindNamedConfig("test/configs", "b"), + "test/configs/build/make/orchestrator/multitree_combos/b.mcombo") + + # Nested dir inside a combo dir + self.assertEqual(lunch.FindNamedConfig("test/configs", "nested"), + "test/configs/build/make/orchestrator/multitree_combos/nested/nested.mcombo") + + # Inside vendor, overriding device + self.assertEqual(lunch.FindNamedConfig("test/configs", "v"), + "test/configs/vendor/aa/bb/multitree_combos/v.mcombo") + + # Inside device + self.assertEqual(lunch.FindNamedConfig("test/configs", "d"), + "test/configs/device/aa/bb/multitree_combos/d.mcombo") + + # Make sure we don't look too deep (for performance) + self.assertIsNone(lunch.FindNamedConfig("test/configs", "too_deep")) + + + def test_choose_config_file(self): + # Empty string argument + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", [""]), + (None, None)) + + # A PRODUCT-VARIANT name + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", ["v-eng"]), + ("test/configs/vendor/aa/bb/multitree_combos/v.mcombo", "eng")) + + # A PRODUCT-VARIANT name that conflicts with a file + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", ["b-eng"]), + ("test/configs/build/make/orchestrator/multitree_combos/b.mcombo", "eng")) + + # A PRODUCT-VARIANT that doesn't exist + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", ["z-user"]), + (None, None)) + + # An explicit file + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", + ["test/configs/build/make/orchestrator/multitree_combos/b.mcombo", "eng"]), + ("test/configs/build/make/orchestrator/multitree_combos/b.mcombo", "eng")) + + # An explicit file that doesn't exist + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", + ["test/configs/doesnt_exist.mcombo", "eng"]), + (None, None)) + + # An explicit file without a variant should fail + self.assertEqual(lunch.ChooseConfigFromArgs("test/configs", + ["test/configs/build/make/orchestrator/multitree_combos/b.mcombo"]), + ("test/configs/build/make/orchestrator/multitree_combos/b.mcombo", None)) + + + def test_config_cycles(self): + # Test that we catch cycles + with self.assertRaises(lunch.ConfigException) as context: + lunch.LoadConfig("test/configs/parsing/cycles/1.mcombo") + self.assertEqual(context.exception.kind, lunch.ConfigException.ERROR_CYCLE) + + def test_config_merge(self): + # Test the merge logic + self.assertEqual(lunch.LoadConfig("test/configs/parsing/merge/1.mcombo"), { + "in_1": "1", + "in_1_2": "1", + "merged": {"merged_1": "1", + "merged_1_2": "1", + "merged_2": "2", + "merged_2_3": "2", + "merged_3": "3"}, + "dict_1": {"a": "b"}, + "in_2": "2", + "in_2_3": "2", + "dict_2": {"a": "b"}, + "in_3": "3", + "dict_3": {"a": "b"} + }) + + def test_list(self): + self.assertEqual(sorted(lunch.FindAllLunchable("test/configs")), + ["test/configs/build/make/orchestrator/multitree_combos/b.mcombo"]) + +if __name__ == "__main__": + unittest.main() + +# vim: sts=4:ts=4:sw=4 diff --git a/make/orchestrator/multitree_combos/test.mcombo b/make/orchestrator/multitree_combos/test.mcombo new file mode 100644 index 0000000..3ad0717 --- /dev/null +++ b/make/orchestrator/multitree_combos/test.mcombo @@ -0,0 +1,16 @@ +{ + "lunchable": true, + "system": { + "tree": "inner_tree_system", + "product": "system_lunch_product" + }, + "vendor": { + "tree": "inner_tree_vendor", + "product": "vendor_lunch_product" + }, + "modules": { + "com.android.something": { + "tree": "inner_tree_module" + } + } +} diff --git a/make/packaging/distdir.mk b/make/packaging/distdir.mk new file mode 100644 index 0000000..264a8b0 --- /dev/null +++ b/make/packaging/distdir.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# From the Android.mk pass: +DIST_GOAL_OUTPUT_PAIRS := +DIST_SRC_DST_PAIRS := +include $(KATI_PACKAGE_MK_DIR)/dist.mk + +$(foreach pair,$(DIST_GOAL_OUTPUT_PAIRS), \ + $(eval goal := $(call word-colon,1,$(pair))) \ + $(eval output := $(call word-colon,2,$(pair))) \ + $(eval .PHONY: _dist_$$(goal)) \ + $(if $(call streq,$(DIST),true),\ + $(eval _dist_$$(goal): $$(DIST_DIR)/$$(output)), \ + $(eval _dist_$$(goal):))) + +define copy-one-dist-file +$(2): $(1) + @echo "Dist: $$@" + rm -f $$@ + cp $$< $$@ +endef + +ifeq ($(DIST),true) + $(foreach pair,$(DIST_SRC_DST_PAIRS), \ + $(eval src := $(call word-colon,1,$(pair))) \ + $(eval dst := $(DIST_DIR)/$(call word-colon,2,$(pair))) \ + $(eval $(call copy-one-dist-file,$(src),$(dst)))) +endif + +copy-one-dist-file := +DIST_GOAL_OUTPUT_PAIRS := +DIST_SRC_DST_PAIRS := diff --git a/make/packaging/main.mk b/make/packaging/main.mk new file mode 100644 index 0000000..0b746a8 --- /dev/null +++ b/make/packaging/main.mk @@ -0,0 +1,37 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Create a default rule. This is unused currently, as the real default rule is +# still in the Kati build step. +.PHONY: _packaging_default_rule_ +_packaging_default_rule_: + +ifndef KATI +$(error Only Kati is supported.) +endif + +$(info [1/3] initializing packaging system ...) + +.KATI_READONLY := KATI_PACKAGE_MK_DIR + +include build/make/common/core.mk +include build/make/common/strings.mk + +$(info [2/3] including distdir.mk ...) + +include build/make/packaging/distdir.mk + +$(info [3/3] writing packaging rules ...) diff --git a/make/rbesetup.sh b/make/rbesetup.sh new file mode 100644 index 0000000..3b0e7cf --- /dev/null +++ b/make/rbesetup.sh @@ -0,0 +1,82 @@ +function _source_env_setup_script() { + local -r ENV_SETUP_SCRIPT="build/make/envsetup.sh" + local -r TOP_DIR=$( + while [[ ! -f "${ENV_SETUP_SCRIPT}" ]] && [[ "${PWD}" != "/" ]]; do + \cd .. + done + if [[ -f "${ENV_SETUP_SCRIPT}" ]]; then + echo "$(PWD= /bin/pwd -P)" + fi + ) + + local -r FULL_PATH_ENV_SETUP_SCRIPT="${TOP_DIR}/${ENV_SETUP_SCRIPT}" + if [[ ! -f "${FULL_PATH_ENV_SETUP_SCRIPT}" ]]; then + echo "ERROR: Unable to source ${ENV_SETUP_SCRIPT}" + return 1 + fi + + # Need to change directory to the repo root so vendor scripts can be sourced + # as well. + local -r CUR_DIR=$PWD + \cd "${TOP_DIR}" + source "${FULL_PATH_ENV_SETUP_SCRIPT}" + \cd "${CUR_DIR}" +} + +# This function needs to run first as the remaining defining functions may be +# using the envsetup.sh defined functions. Skip this part if this script is already +# being invoked from envsetup.sh. +if [[ "$1" != "--skip-envsetup" ]]; then + _source_env_setup_script || return +fi + +# This function prefixes the given command with appropriate variables needed +# for the build to be executed with RBE. +function use_rbe() { + local RBE_LOG_DIR="/tmp" + local RBE_BINARIES_DIR="prebuilts/remoteexecution-client/latest" + local DOCKER_IMAGE="gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62" + + # Do not set an invocation-ID and let reproxy auto-generate one. + USE_RBE="true" \ + FLAG_server_address="unix:///tmp/reproxy_$RANDOM.sock" \ + FLAG_exec_root="$(gettop)" \ + FLAG_platform="container-image=docker://${DOCKER_IMAGE}" \ + RBE_use_application_default_credentials="true" \ + RBE_log_dir="${RBE_LOG_DIR}" \ + RBE_reproxy_wait_seconds="20" \ + RBE_output_dir="${RBE_LOG_DIR}" \ + RBE_log_path="text://${RBE_LOG_DIR}/reproxy_log.txt" \ + RBE_CXX_EXEC_STRATEGY="remote_local_fallback" \ + RBE_cpp_dependency_scanner_plugin="${RBE_BINARIES_DIR}/dependency_scanner_go_plugin.so" \ + RBE_DIR=${RBE_BINARIES_DIR} \ + RBE_re_proxy="${RBE_BINARIES_DIR}/reproxy" \ + $@ +} + +# This function detects if the uploader is available and sets the path of it to +# ANDROID_ENABLE_METRICS_UPLOAD. +function _export_metrics_uploader() { + local uploader_path="$(gettop)/vendor/google/misc/metrics_uploader_prebuilt/metrics_uploader.sh" + if [[ -x "${uploader_path}" ]]; then + export ANDROID_ENABLE_METRICS_UPLOAD="${uploader_path}" + fi +} + +# This function sets RBE specific environment variables needed for the build to +# executed by RBE. This file should be sourced once per checkout of Android code. +function _set_rbe_vars() { + unset USE_GOMA + export USE_RBE="true" + export RBE_CXX_EXEC_STRATEGY="racing" + export RBE_JAVAC_EXEC_STRATEGY="racing" + export RBE_R8_EXEC_STRATEGY="racing" + export RBE_D8_EXEC_STRATEGY="racing" + export RBE_use_unified_cas_ops="true" + export RBE_JAVAC=1 + export RBE_R8=1 + export RBE_D8=1 +} + +_export_metrics_uploader +_set_rbe_vars diff --git a/make/tapasHelp.sh b/make/tapasHelp.sh new file mode 100755 index 0000000..7cb5f2c --- /dev/null +++ b/make/tapasHelp.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# locate some directories +cd "$(dirname $0)" +SCRIPT_DIR="${PWD}" +cd ../.. +TOP="${PWD}" + +message='usage: tapas [ ...] [arm|x86|arm64|x86_64] [eng|userdebug|user] [devkeys] + +tapas selects individual apps to be built by the Android build system. Unlike +"lunch", "tapas" does not request the building of images for a device. +Additionally, an app built with "tapas" will have its dex file inside its apk, +which should cause it to be suitable for installing on any api-compatible +device. In other words, "tapas" configures the build of unbundled apps. + +The names ... should match LOCAL_PACKAGE_NAME as defined in an +Android.mk + +The usage of the other arguments matches that of the rest of the platform +build system and can be found by running `m help`' + +echo "$message" diff --git a/make/target/OWNERS b/make/target/OWNERS new file mode 100644 index 0000000..feb2742 --- /dev/null +++ b/make/target/OWNERS @@ -0,0 +1 @@ +hansson@google.com diff --git a/make/target/board/Android.mk b/make/target/board/Android.mk new file mode 100644 index 0000000..baa3d3a --- /dev/null +++ b/make/target/board/Android.mk @@ -0,0 +1,158 @@ +# +# Set up product-global definitions and include product-specific rules. +# + +LOCAL_PATH := $(call my-dir) + +-include $(TARGET_DEVICE_DIR)/AndroidBoard.mk + +# Generate a file that contains various information about the +# device we're building for. This file is typically packaged up +# with everything else. +# +# If TARGET_BOARD_INFO_FILE (which can be set in BoardConfig.mk) is +# defined, it is used, otherwise board-info.txt is looked for in +# $(TARGET_DEVICE_DIR). +# +INSTALLED_ANDROID_INFO_TXT_TARGET := $(PRODUCT_OUT)/android-info.txt +board_info_txt := $(TARGET_BOARD_INFO_FILE) +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) + $(call pretty,"Generated: ($@)") +ifdef board_info_txt + $(hide) grep -v '#' $< > $@ +else ifdef TARGET_BOOTLOADER_BOARD_NAME + $(hide) echo "board=$(TARGET_BOOTLOADER_BOARD_NAME)" > $@ +else + $(hide) echo "" > $@ +endif + +$(call declare-0p-target,$(INSTALLED_ANDROID_INFO_TXT_TARGET)) + +# Copy compatibility metadata to the device. + +# Device Manifest +ifdef DEVICE_MANIFEST_FILE +# $(DEVICE_MANIFEST_FILE) can be a list of files +include $(CLEAR_VARS) +LOCAL_MODULE := vendor_manifest.xml +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution +LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice +LOCAL_MODULE_STEM := manifest.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/vintf + +GEN := $(local-generated-sources-dir)/manifest.xml +$(GEN): PRIVATE_DEVICE_MANIFEST_FILE := $(DEVICE_MANIFEST_FILE) +$(GEN): $(DEVICE_MANIFEST_FILE) $(HOST_OUT_EXECUTABLES)/assemble_vintf + BOARD_SEPOLICY_VERS=$(BOARD_SEPOLICY_VERS) \ + PRODUCT_ENFORCE_VINTF_MANIFEST=$(PRODUCT_ENFORCE_VINTF_MANIFEST) \ + PRODUCT_SHIPPING_API_LEVEL=$(PRODUCT_SHIPPING_API_LEVEL) \ + $(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \ + -i $(call normalize-path-list,$(PRIVATE_DEVICE_MANIFEST_FILE)) + +LOCAL_PREBUILT_MODULE_FILE := $(GEN) +include $(BUILD_PREBUILT) +endif + +# DEVICE_MANIFEST_SKUS: a list of SKUS where DEVICE_MANIFEST__FILES is defined. +ifdef DEVICE_MANIFEST_SKUS + +# Install /vendor/etc/vintf/manifest_$(sku).xml +# $(1): sku +define _add_device_sku_manifest +my_fragment_files_var := DEVICE_MANIFEST_$$(call to-upper,$(1))_FILES +ifndef $$(my_fragment_files_var) +$$(error $(1) is in DEVICE_MANIFEST_SKUS but $$(my_fragment_files_var) is not defined) +endif +my_fragment_files := $$($$(my_fragment_files_var)) +include $$(CLEAR_VARS) +LOCAL_MODULE := vendor_manifest_$(1).xml +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution +LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice +LOCAL_MODULE_STEM := manifest_$(1).xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/vintf + +GEN := $$(local-generated-sources-dir)/manifest_$(1).xml +$$(GEN): PRIVATE_SRC_FILES := $$(my_fragment_files) +$$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf + BOARD_SEPOLICY_VERS=$$(BOARD_SEPOLICY_VERS) \ + PRODUCT_ENFORCE_VINTF_MANIFEST=$$(PRODUCT_ENFORCE_VINTF_MANIFEST) \ + PRODUCT_SHIPPING_API_LEVEL=$$(PRODUCT_SHIPPING_API_LEVEL) \ + $$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \ + -i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES)) + +LOCAL_PREBUILT_MODULE_FILE := $$(GEN) +include $$(BUILD_PREBUILT) +my_fragment_files_var := +my_fragment_files := +endef + +$(foreach sku, $(DEVICE_MANIFEST_SKUS), $(eval $(call _add_device_sku_manifest,$(sku)))) +_add_device_sku_manifest := + +endif # DEVICE_MANIFEST_SKUS + +# ODM manifest +ifdef ODM_MANIFEST_FILES +# ODM_MANIFEST_FILES is a list of files that is combined and installed as the default ODM manifest. +include $(CLEAR_VARS) +LOCAL_MODULE := odm_manifest.xml +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution +LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice +LOCAL_MODULE_STEM := manifest.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_RELATIVE_PATH := vintf +LOCAL_ODM_MODULE := true + +GEN := $(local-generated-sources-dir)/manifest.xml +$(GEN): PRIVATE_SRC_FILES := $(ODM_MANIFEST_FILES) +$(GEN): $(ODM_MANIFEST_FILES) $(HOST_OUT_EXECUTABLES)/assemble_vintf + # Set VINTF_IGNORE_TARGET_FCM_VERSION to true because it should only be in device manifest. + VINTF_IGNORE_TARGET_FCM_VERSION=true \ + $(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \ + -i $(call normalize-path-list,$(PRIVATE_SRC_FILES)) + +LOCAL_PREBUILT_MODULE_FILE := $(GEN) +include $(BUILD_PREBUILT) +endif # ODM_MANIFEST_FILES + +# ODM_MANIFEST_SKUS: a list of SKUS where ODM_MANIFEST__FILES are defined. +ifdef ODM_MANIFEST_SKUS + +# Install /odm/etc/vintf/manifest_$(sku).xml +# $(1): sku +define _add_odm_sku_manifest +my_fragment_files_var := ODM_MANIFEST_$$(call to-upper,$(1))_FILES +ifndef $$(my_fragment_files_var) +$$(error $(1) is in ODM_MANIFEST_SKUS but $$(my_fragment_files_var) is not defined) +endif +my_fragment_files := $$($$(my_fragment_files_var)) +include $$(CLEAR_VARS) +LOCAL_MODULE := odm_manifest_$(1).xml +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution +LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice +LOCAL_MODULE_STEM := manifest_$(1).xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_RELATIVE_PATH := vintf +LOCAL_ODM_MODULE := true +GEN := $$(local-generated-sources-dir)/manifest_$(1).xml +$$(GEN): PRIVATE_SRC_FILES := $$(my_fragment_files) +$$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf + VINTF_IGNORE_TARGET_FCM_VERSION=true \ + $$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \ + -i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES)) +LOCAL_PREBUILT_MODULE_FILE := $$(GEN) +include $$(BUILD_PREBUILT) +my_fragment_files_var := +my_fragment_files := +endef + +$(foreach sku, $(ODM_MANIFEST_SKUS), $(eval $(call _add_odm_sku_manifest,$(sku)))) +_add_odm_sku_manifest := + +endif # ODM_MANIFEST_SKUS diff --git a/make/target/board/BoardConfigEmuCommon.mk b/make/target/board/BoardConfigEmuCommon.mk new file mode 100644 index 0000000..cc5e3ab --- /dev/null +++ b/make/target/board/BoardConfigEmuCommon.mk @@ -0,0 +1,75 @@ +# BoardConfigEmuCommon.mk +# +# Common compile-time definitions for emulator +# + +HAVE_HTC_AUDIO_DRIVER := true +BOARD_USES_GENERIC_AUDIO := true +TARGET_BOOTLOADER_BOARD_NAME := goldfish_$(TARGET_ARCH) + +# No Kernel +TARGET_NO_KERNEL := true + +# no hardware camera +USE_CAMERA_STUB := true + +NUM_FRAMEBUFFER_SURFACE_BUFFERS := 3 + +# Build OpenGLES emulation guest and host libraries +BUILD_EMULATOR_OPENGL := true +BUILD_QEMU_IMAGES := true + +# Build and enable the OpenGL ES View renderer. When running on the emulator, +# the GLES renderer disables itself if host GL acceleration isn't available. +USE_OPENGL_RENDERER := true + +# Emulator doesn't support sparse image format. +TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true + +# emulator is Non-A/B device +AB_OTA_UPDATER := false + +# emulator needs super.img +BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true + +# 8G + 8M +BOARD_SUPER_PARTITION_SIZE := 8598323200 +BOARD_SUPER_PARTITION_GROUPS := emulator_dynamic_partitions + +BOARD_EMULATOR_DYNAMIC_PARTITIONS_PARTITION_LIST := \ + system \ + system_dlkm \ + system_ext \ + product \ + vendor + +TARGET_COPY_OUT_PRODUCT := product +BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 +TARGET_COPY_OUT_SYSTEM_EXT := system_ext +BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 + +BOARD_USES_SYSTEM_DLKMIMAGE := true +BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE := erofs +TARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm + +# 8G +BOARD_EMULATOR_DYNAMIC_PARTITIONS_SIZE := 8589934592 + +#vendor boot +BOARD_INCLUDE_DTB_IN_BOOTIMG := false +BOARD_BOOT_HEADER_VERSION := 4 +BOARD_MKBOOTIMG_ARGS += --header_version $(BOARD_BOOT_HEADER_VERSION) +BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 0x06000000 +BOARD_RAMDISK_USE_LZ4 := true + +# Enable chain partition for system. +BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem +BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048 +BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) +BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 + +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_FLASH_BLOCK_SIZE := 512 +DEVICE_MATRIX_FILE := device/generic/goldfish/compatibility_matrix.xml + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/common diff --git a/make/target/board/BoardConfigGsiCommon.mk b/make/target/board/BoardConfigGsiCommon.mk new file mode 100644 index 0000000..53714a8 --- /dev/null +++ b/make/target/board/BoardConfigGsiCommon.mk @@ -0,0 +1,85 @@ +# BoardConfigGsiCommon.mk +# +# Common compile-time definitions for GSI +# Builds upon the mainline config. +# + +include build/make/target/board/BoardConfigMainlineCommon.mk + +TARGET_NO_KERNEL := true + +# This flag is set by mainline but isn't desired for GSI. +BOARD_USES_SYSTEM_OTHER_ODEX := + +# system.img is ext4/erofs and non-sparsed. +GSI_FILE_SYSTEM_TYPE ?= ext4 +BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := $(GSI_FILE_SYSTEM_TYPE) +TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true +TARGET_USERIMAGES_SPARSE_EROFS_DISABLED := true + +# GSI also includes make_f2fs to support userdata parition in f2fs +# for some devices +TARGET_USERIMAGES_USE_F2FS := true + +# Enable dynamic system image size and reserved 64MB in it. +BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE := 67108864 + +# GSI forces product and system_ext packages to /system for now. +TARGET_COPY_OUT_PRODUCT := system/product +TARGET_COPY_OUT_SYSTEM_EXT := system/system_ext +BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := + +# Creates metadata partition mount point under root for +# the devices with metadata parition +BOARD_USES_METADATA_PARTITION := true + +# Android Verified Boot (AVB): +# Set the rollback index to zero, to prevent the device bootloader from +# updating the last seen rollback index in the tamper-evident storage. +BOARD_AVB_ROLLBACK_INDEX := 0 + +# The chained vbmeta settings for boot images. +BOARD_AVB_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem +BOARD_AVB_BOOT_ALGORITHM := SHA256_RSA4096 +BOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) +BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2 + +# Enable AVB chained partition for system. +# https://android.googlesource.com/platform/external/avb/+/master/README.md +BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem +BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048 +BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) +BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 + +# Using sha256 for dm-verity partitions. b/156162446 +BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 + +ifdef BUILDING_GSI +# super.img spec for GSI targets +BOARD_SUPER_PARTITION_SIZE := 3229614080 +BOARD_SUPER_PARTITION_GROUPS := gsi_dynamic_partitions +BOARD_GSI_DYNAMIC_PARTITIONS_PARTITION_LIST := system +BOARD_GSI_DYNAMIC_PARTITIONS_SIZE := 3221225472 +endif + +# TODO(b/123695868, b/146149698): +# This flag is set by mainline but isn't desired for GSI +BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := + +# GSI specific System Properties +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +TARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext.prop +else +TARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext_user.prop +endif + +# Set this to create /cache mount point for non-A/B devices that mounts /cache. +# The partition size doesn't matter, just to make build pass. +BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_CACHEIMAGE_PARTITION_SIZE := 16777216 + +# Setup a vendor image to let PRODUCT_VENDOR_PROPERTIES does not affect GSI +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 + +# Disable 64 bit mediadrmserver +TARGET_ENABLE_MEDIADRM_64 := diff --git a/make/target/board/BoardConfigMainlineCommon.mk b/make/target/board/BoardConfigMainlineCommon.mk new file mode 100644 index 0000000..00f6e5b --- /dev/null +++ b/make/target/board/BoardConfigMainlineCommon.mk @@ -0,0 +1,51 @@ +# BoardConfigMainlineCommon.mk +# +# Common compile-time definitions for mainline images. + +# The generic product target doesn't have any hardware-specific pieces. +TARGET_NO_BOOTLOADER := true +TARGET_NO_RECOVERY := true + +BOARD_EXT4_SHARE_DUP_BLOCKS := true + +TARGET_USERIMAGES_USE_EXT4 := true + +# Mainline devices must have /system_ext, /vendor and /product partitions. +TARGET_COPY_OUT_SYSTEM_EXT := system_ext +TARGET_COPY_OUT_VENDOR := vendor +TARGET_COPY_OUT_PRODUCT := product + +# Creates metadata partition mount point under root for +# the devices with metadata parition +BOARD_USES_METADATA_PARTITION := true + +# Default is current, but allow devices to override vndk version if needed. +BOARD_VNDK_VERSION ?= current + +# Required flag for non-64 bit devices from P. +TARGET_USES_64_BIT_BINDER := true + +# 64 bit mediadrmserver +TARGET_ENABLE_MEDIADRM_64 := true + +# Puts odex files on system_other, as well as causing dex files not to get +# stripped from APKs. +BOARD_USES_SYSTEM_OTHER_ODEX := true + +# Audio: must using XML format for Treblized devices +USE_XML_AUDIO_POLICY_CONF := 1 + +# Bluetooth defines +# TODO(b/123695868): Remove the need for this +BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := build/make/target/board/mainline_arm64/bluetooth + +BOARD_AVB_ENABLE := true +BOARD_AVB_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) + +BOARD_CHARGER_ENABLE_SUSPEND := true + +# Enable system property split for Treble +BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED := true + +# Include stats logging code in LMKD +TARGET_LMKD_STATS_LOG := true diff --git a/make/target/board/BoardConfigModuleCommon.mk b/make/target/board/BoardConfigModuleCommon.mk new file mode 100644 index 0000000..24c01a5 --- /dev/null +++ b/make/target/board/BoardConfigModuleCommon.mk @@ -0,0 +1,6 @@ +# BoardConfigModuleCommon.mk +# +# Common compile-time settings for module builds. + +# Required for all module devices. +TARGET_USES_64_BIT_BINDER := true diff --git a/make/target/board/BoardConfigPixelCommon.mk b/make/target/board/BoardConfigPixelCommon.mk new file mode 100644 index 0000000..22521b5 --- /dev/null +++ b/make/target/board/BoardConfigPixelCommon.mk @@ -0,0 +1,19 @@ +# BoardConfigPixelCommon.mk +# +# Common compile-time definitions for Pixel devices. + +# Using sha256 for dm-verity partitions. b/156162446 +# system, system_other, system_ext and product. +BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 +BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 +BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 +BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 +BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 + +# vendor and odm. +BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 +BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 + +# vendor_dlkm and odm_dlkm. +BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 +BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 diff --git a/make/target/board/emulator_arm/AndroidBoard.mk b/make/target/board/emulator_arm/AndroidBoard.mk new file mode 100644 index 0000000..7911f61 --- /dev/null +++ b/make/target/board/emulator_arm/AndroidBoard.mk @@ -0,0 +1 @@ +LOCAL_PATH := $(call my-dir) diff --git a/make/target/board/emulator_arm/BoardConfig.mk b/make/target/board/emulator_arm/BoardConfig.mk new file mode 100644 index 0000000..287824f --- /dev/null +++ b/make/target/board/emulator_arm/BoardConfig.mk @@ -0,0 +1,37 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# arm emulator specific definitions +TARGET_ARCH := arm +TARGET_ARCH_VARIANT := armv7-a-neon +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := armeabi-v7a +TARGET_CPU_ABI2 := armeabi + +include build/make/target/board/BoardConfigGsiCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/emulator_arm/device.mk b/make/target/board/emulator_arm/device.mk new file mode 100644 index 0000000..af023eb --- /dev/null +++ b/make/target/board/emulator_arm/device.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. diff --git a/make/target/board/emulator_arm/system_ext.prop b/make/target/board/emulator_arm/system_ext.prop new file mode 100644 index 0000000..64829f3 --- /dev/null +++ b/make/target/board/emulator_arm/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib/libreference-ril.so diff --git a/make/target/board/emulator_arm64/BoardConfig.mk b/make/target/board/emulator_arm64/BoardConfig.mk new file mode 100644 index 0000000..963e558 --- /dev/null +++ b/make/target/board/emulator_arm64/BoardConfig.mk @@ -0,0 +1,69 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# arm64 emulator specific definitions +TARGET_ARCH := arm64 +TARGET_ARCH_VARIANT := armv8-a +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := arm64-v8a + +ifneq ($(TARGET_BUILD_APPS)$(filter cts sdk,$(MAKECMDGOALS)),) +# DO NOT USE +# DO NOT USE +# +# This architecture / CPU variant must NOT be used for any 64 bit +# platform builds. It is the lowest common denominator required +# to build an unbundled application or cts for all supported 32 and 64 bit +# platforms. +# +# If you're building a 64 bit platform (and not an application) the +# ARM-v8 specification allows you to assume all the features available in an +# armv7-a-neon CPU. You should set the following as 2nd arch/cpu variant: +# +# TARGET_2ND_ARCH_VARIANT := armv8-a +# TARGET_2ND_CPU_VARIANT := generic +# +# DO NOT USE +# DO NOT USE +TARGET_2ND_ARCH_VARIANT := armv7-a-neon +# DO NOT USE +# DO NOT USE +TARGET_2ND_CPU_VARIANT := generic +# DO NOT USE +# DO NOT USE +else +TARGET_2ND_ARCH_VARIANT := armv8-a +TARGET_2ND_CPU_VARIANT := generic +endif + +include build/make/target/board/BoardConfigGsiCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +TARGET_NO_KERNEL := false +BOARD_USES_RECOVERY_AS_BOOT := true + +BOARD_BOOTIMAGE_PARTITION_SIZE := 0x02000000 +BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/emulator_arm64/device.mk b/make/target/board/emulator_arm64/device.mk new file mode 100644 index 0000000..dc84192 --- /dev/null +++ b/make/target/board/emulator_arm64/device.mk @@ -0,0 +1,28 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. + +# Cuttlefish has GKI kernel prebuilts, so use those for the GKI boot.img. +ifeq ($(TARGET_PREBUILT_KERNEL),) + LOCAL_KERNEL := kernel/prebuilts/5.4/arm64/kernel-5.4-lz4 +else + LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL) +endif + +PRODUCT_COPY_FILES += \ + $(LOCAL_KERNEL):kernel diff --git a/make/target/board/emulator_arm64/system_ext.prop b/make/target/board/emulator_arm64/system_ext.prop new file mode 100644 index 0000000..2f8f803 --- /dev/null +++ b/make/target/board/emulator_arm64/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for emulator arm64 sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/emulator_x86/BoardConfig.mk b/make/target/board/emulator_x86/BoardConfig.mk new file mode 100644 index 0000000..8f79166 --- /dev/null +++ b/make/target/board/emulator_x86/BoardConfig.mk @@ -0,0 +1,40 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86 emulator specific definitions +TARGET_CPU_ABI := x86 +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 + +TARGET_PRELINK_MODULE := false + +include build/make/target/board/BoardConfigGsiCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +# Resize to 4G to accommodate ASAN and CTS +BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/emulator_x86/device.mk b/make/target/board/emulator_x86/device.mk new file mode 100644 index 0000000..8a9d8da --- /dev/null +++ b/make/target/board/emulator_x86/device.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. + +ifdef NET_ETH0_STARTONBOOT + PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1 +endif + +# Ensure we package the BIOS files too. +PRODUCT_HOST_PACKAGES += \ + bios.bin \ + vgabios-cirrus.bin \ diff --git a/make/target/board/emulator_x86/system_ext.prop b/make/target/board/emulator_x86/system_ext.prop new file mode 100644 index 0000000..64829f3 --- /dev/null +++ b/make/target/board/emulator_x86/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib/libreference-ril.so diff --git a/make/target/board/emulator_x86_64/BoardConfig.mk b/make/target/board/emulator_x86_64/BoardConfig.mk new file mode 100755 index 0000000..b9cbd8a --- /dev/null +++ b/make/target/board/emulator_x86_64/BoardConfig.mk @@ -0,0 +1,42 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86_64 emulator specific definitions +TARGET_CPU_ABI := x86_64 +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 + +TARGET_2ND_CPU_ABI := x86 +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 + +TARGET_PRELINK_MODULE := false +include build/make/target/board/BoardConfigGsiCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/emulator_x86_64/device.mk b/make/target/board/emulator_x86_64/device.mk new file mode 100755 index 0000000..8a9d8da --- /dev/null +++ b/make/target/board/emulator_x86_64/device.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. + +ifdef NET_ETH0_STARTONBOOT + PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1 +endif + +# Ensure we package the BIOS files too. +PRODUCT_HOST_PACKAGES += \ + bios.bin \ + vgabios-cirrus.bin \ diff --git a/make/target/board/emulator_x86_64/system_ext.prop b/make/target/board/emulator_x86_64/system_ext.prop new file mode 100644 index 0000000..ed9d173 --- /dev/null +++ b/make/target/board/emulator_x86_64/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/emulator_x86_64_arm64/BoardConfig.mk b/make/target/board/emulator_x86_64_arm64/BoardConfig.mk new file mode 100755 index 0000000..26b61a6 --- /dev/null +++ b/make/target/board/emulator_x86_64_arm64/BoardConfig.mk @@ -0,0 +1,59 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86_64 emulator specific definitions +TARGET_CPU_ABI := x86_64 +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 + +TARGET_2ND_CPU_ABI := x86 +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 + +TARGET_NATIVE_BRIDGE_ARCH := arm64 +TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv8-a +TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_ABI := arm64-v8a + +TARGET_NATIVE_BRIDGE_2ND_ARCH := arm +TARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT := armv7-a-neon +TARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_2ND_ABI := armeabi-v7a armeabi + +BUILD_BROKEN_DUP_RULES := true + +TARGET_PRELINK_MODULE := false + +include build/make/target/board/BoardConfigMainlineCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +# the settings differ from BoardConfigMainlineCommon.mk +BOARD_USES_SYSTEM_OTHER_ODEX := + +# Resize to 4G to accommodate ASAN and CTS +BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/emulator_x86_64_arm64/device.mk b/make/target/board/emulator_x86_64_arm64/device.mk new file mode 100755 index 0000000..af023eb --- /dev/null +++ b/make/target/board/emulator_x86_64_arm64/device.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. diff --git a/make/target/board/emulator_x86_64_arm64/system_ext.prop b/make/target/board/emulator_x86_64_arm64/system_ext.prop new file mode 100644 index 0000000..ed9d173 --- /dev/null +++ b/make/target/board/emulator_x86_64_arm64/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/emulator_x86_arm/BoardConfig.mk b/make/target/board/emulator_x86_arm/BoardConfig.mk new file mode 100644 index 0000000..21fdbc8 --- /dev/null +++ b/make/target/board/emulator_x86_arm/BoardConfig.mk @@ -0,0 +1,52 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86 emulator specific definitions +TARGET_CPU_ABI := x86 +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 + +TARGET_NATIVE_BRIDGE_ARCH := arm +TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon +TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi + +BUILD_BROKEN_DUP_RULES := true + +# +# The inclusion order below is important. +# The settings in latter makefiles overwrite those in the former. +# +include build/make/target/board/BoardConfigMainlineCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +# the settings differ from BoardConfigMainlineCommon.mk +BOARD_USES_SYSTEM_OTHER_ODEX := + +# Resize to 4G to accommodate ASAN and CTS +BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/emulator_x86_arm/device.mk b/make/target/board/emulator_x86_arm/device.mk new file mode 100644 index 0000000..af023eb --- /dev/null +++ b/make/target/board/emulator_x86_arm/device.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. diff --git a/make/target/board/emulator_x86_arm/system_ext.prop b/make/target/board/emulator_x86_arm/system_ext.prop new file mode 100644 index 0000000..64829f3 --- /dev/null +++ b/make/target/board/emulator_x86_arm/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib/libreference-ril.so diff --git a/make/target/board/generic/AndroidBoard.mk b/make/target/board/generic/AndroidBoard.mk new file mode 100644 index 0000000..7911f61 --- /dev/null +++ b/make/target/board/generic/AndroidBoard.mk @@ -0,0 +1 @@ +LOCAL_PATH := $(call my-dir) diff --git a/make/target/board/generic/BoardConfig.mk b/make/target/board/generic/BoardConfig.mk new file mode 100644 index 0000000..87c16da --- /dev/null +++ b/make/target/board/generic/BoardConfig.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# arm emulator specific definitions +TARGET_ARCH := arm + +# Note: Before P, we built the platform images for ARMv7-A _without_ NEON. +# Note: Before Q, we built the CTS and SDK images for ARMv7-A _without_ NEON. +# Note: Before Q, we built unbundled apps for ARMv7-A _without_ NEON. +# +# Starting from Pi, System image of aosp_arm products is the new GSI +# for real devices newly launched for Pi. These devices are usualy not +# as performant as the mainstream 64-bit devices and the performance +# provided by NEON is important for them to pass related CTS tests. +TARGET_ARCH_VARIANT := armv7-a-neon +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := armeabi-v7a +TARGET_CPU_ABI2 := armeabi + +include build/make/target/board/BoardConfigGsiCommon.mk + +ifndef BUILDING_GSI +include build/make/target/board/BoardConfigEmuCommon.mk + +BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" +endif diff --git a/make/target/board/generic/README.txt b/make/target/board/generic/README.txt new file mode 100644 index 0000000..ddac68e --- /dev/null +++ b/make/target/board/generic/README.txt @@ -0,0 +1,9 @@ +The "generic" product defines a non-hardware-specific target +without a kernel or bootloader. + +It can be used to build the entire user-level system, and +will work with the emulator, though sound will not work +(see the "emulator" product for that). + +It is not a product "base class"; no other products inherit +from it or use it in any way. diff --git a/make/target/board/generic/device.mk b/make/target/board/generic/device.mk new file mode 100644 index 0000000..76242c9 --- /dev/null +++ b/make/target/board/generic/device.mk @@ -0,0 +1,18 @@ +# +# 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. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. diff --git a/make/target/board/generic/system_ext.prop b/make/target/board/generic/system_ext.prop new file mode 100644 index 0000000..ad8e5b8 --- /dev/null +++ b/make/target/board/generic/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib/libreference-ril.so diff --git a/make/target/board/generic_64bitonly_x86_64/BoardConfig.mk b/make/target/board/generic_64bitonly_x86_64/BoardConfig.mk new file mode 100644 index 0000000..a240eab --- /dev/null +++ b/make/target/board/generic_64bitonly_x86_64/BoardConfig.mk @@ -0,0 +1,50 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86_64 emulator specific definitions +TARGET_CPU_ABI := x86_64 +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 + +# Keep the following for 32-bit native code support +# There are a few native services still on 32-bit modes, e.g. media & audio. +# Remove them in S. +TARGET_2ND_CPU_ABI := x86 +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 + +TARGET_PRELINK_MODULE := false + +include build/make/target/board/BoardConfigGsiCommon.mk + +ifndef BUILDING_GSI +include build/make/target/board/BoardConfigEmuCommon.mk + +BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" + +endif # !BUILDING_GSI diff --git a/make/target/board/generic_64bitonly_x86_64/README.txt b/make/target/board/generic_64bitonly_x86_64/README.txt new file mode 100644 index 0000000..dc7efd3 --- /dev/null +++ b/make/target/board/generic_64bitonly_x86_64/README.txt @@ -0,0 +1,7 @@ +The "generic_x86_64_app" product defines a non-hardware-specific IA target +without a kernel or bootloader. + +It can be used to build the entire user-level system, and +will work with the IA version of the emulator, + +This supports 64-bit apps only. diff --git a/make/target/board/generic_64bitonly_x86_64/device.mk b/make/target/board/generic_64bitonly_x86_64/device.mk new file mode 100644 index 0000000..bb49057 --- /dev/null +++ b/make/target/board/generic_64bitonly_x86_64/device.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ifdef NET_ETH0_STARTONBOOT + PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1 +endif + +# Ensure we package the BIOS files too. +PRODUCT_HOST_PACKAGES += \ + bios.bin \ + vgabios-cirrus.bin \ diff --git a/make/target/board/generic_64bitonly_x86_64/system.prop b/make/target/board/generic_64bitonly_x86_64/system.prop new file mode 100644 index 0000000..ed9d173 --- /dev/null +++ b/make/target/board/generic_64bitonly_x86_64/system.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/generic_arm64/BoardConfig.mk b/make/target/board/generic_arm64/BoardConfig.mk new file mode 100644 index 0000000..45ed3da --- /dev/null +++ b/make/target/board/generic_arm64/BoardConfig.mk @@ -0,0 +1,67 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# arm64 emulator specific definitions +TARGET_ARCH := arm64 +TARGET_ARCH_VARIANT := armv8-a +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := arm64-v8a + +TARGET_2ND_ARCH := arm +TARGET_2ND_CPU_ABI := armeabi-v7a +TARGET_2ND_CPU_ABI2 := armeabi + +ifneq ($(TARGET_BUILD_APPS)$(filter cts sdk,$(MAKECMDGOALS)),) +# DO NOT USE +# DO NOT USE +# +# This architecture / CPU variant must NOT be used for any 64 bit +# platform builds. It is the lowest common denominator required +# to build an unbundled application or cts for all supported 32 and 64 bit +# platforms. +# +# If you're building a 64 bit platform (and not an application) the +# ARM-v8 specification allows you to assume all the features available in an +# armv7-a-neon CPU. You should set the following as 2nd arch/cpu variant: +# +# TARGET_2ND_ARCH_VARIANT := armv8-a +# TARGET_2ND_CPU_VARIANT := generic +# +# DO NOT USE +# DO NOT USE +TARGET_2ND_ARCH_VARIANT := armv7-a-neon +# DO NOT USE +# DO NOT USE +TARGET_2ND_CPU_VARIANT := generic +# DO NOT USE +# DO NOT USE +else +TARGET_2ND_ARCH_VARIANT := armv8-a +TARGET_2ND_CPU_VARIANT := generic +endif + +include build/make/target/board/BoardConfigGsiCommon.mk + +# Some vendors still haven't cleaned up all device specific directories under +# root! + +# TODO(b/111434759, b/111287060) SoC specific hacks +BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp +BOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist +BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt:/firmware + +# TODO(b/36764215): remove this setting when the generic system image +# no longer has QCOM-specific directories under /. +BOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy diff --git a/make/target/board/generic_arm64/README.txt b/make/target/board/generic_arm64/README.txt new file mode 100644 index 0000000..8711a14 --- /dev/null +++ b/make/target/board/generic_arm64/README.txt @@ -0,0 +1,7 @@ +The "generic_arm64" product defines a non-hardware-specific arm64 target +without a bootloader. + +It is also the target to build the generic kernel image (GKI). + +It is not a product "base class"; no other products inherit +from it or use it in any way. diff --git a/make/target/board/generic_arm64/device.mk b/make/target/board/generic_arm64/device.mk new file mode 100644 index 0000000..598bef1 --- /dev/null +++ b/make/target/board/generic_arm64/device.mk @@ -0,0 +1,15 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/make/target/board/generic_arm64/sepolicy/OWNERS b/make/target/board/generic_arm64/sepolicy/OWNERS new file mode 100644 index 0000000..6dc2b86 --- /dev/null +++ b/make/target/board/generic_arm64/sepolicy/OWNERS @@ -0,0 +1 @@ +include platform/system/sepolicy:/OWNERS diff --git a/make/target/board/generic_arm64/sepolicy/file.te b/make/target/board/generic_arm64/sepolicy/file.te new file mode 100644 index 0000000..7adfdfa --- /dev/null +++ b/make/target/board/generic_arm64/sepolicy/file.te @@ -0,0 +1,6 @@ +# TODO(b/36764215): remove this file when the generic system image +# no longer has these directories +type persist_file, file_type; + +# Default type for anything under /firmware. +type firmware_file, fs_type, contextmount_type; diff --git a/make/target/board/generic_arm64/sepolicy/file_contexts b/make/target/board/generic_arm64/sepolicy/file_contexts new file mode 100644 index 0000000..0a80559 --- /dev/null +++ b/make/target/board/generic_arm64/sepolicy/file_contexts @@ -0,0 +1,12 @@ +# TODO(b/36764215): remove this file when the generic system image +# no longer has these directories. They are specific to QCOM. + +# / +/tombstones u:object_r:rootfs:s0 +/dsp u:object_r:rootfs:s0 + +# /persist +/persist(/.*)? u:object_r:persist_file:s0 + +# files in firmware +/firmware(/.*)? u:object_r:firmware_file:s0 diff --git a/make/target/board/generic_arm64/system_ext.prop b/make/target/board/generic_arm64/system_ext.prop new file mode 100644 index 0000000..5b0183a --- /dev/null +++ b/make/target/board/generic_arm64/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic arm64 sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/generic_x86/BoardConfig.mk b/make/target/board/generic_x86/BoardConfig.mk new file mode 100644 index 0000000..47fd384 --- /dev/null +++ b/make/target/board/generic_x86/BoardConfig.mk @@ -0,0 +1,41 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86 emulator specific definitions +TARGET_CPU_ABI := x86 +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 + +include build/make/target/board/BoardConfigGsiCommon.mk + +ifndef BUILDING_GSI +include build/make/target/board/BoardConfigEmuCommon.mk + +# Resize to 4G to accomodate ASAN and CTS +BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" +endif diff --git a/make/target/board/generic_x86/README.txt b/make/target/board/generic_x86/README.txt new file mode 100644 index 0000000..938d982 --- /dev/null +++ b/make/target/board/generic_x86/README.txt @@ -0,0 +1,8 @@ +The "generic_x86" product defines a non-hardware-specific IA target +without a kernel or bootloader. + +It can be used to build the entire user-level system, and +will work with the IA version of the emulator, + +It is not a product "base class"; no other products inherit +from it or use it in any way. diff --git a/make/target/board/generic_x86/device.mk b/make/target/board/generic_x86/device.mk new file mode 100644 index 0000000..5ad008f --- /dev/null +++ b/make/target/board/generic_x86/device.mk @@ -0,0 +1,27 @@ +# +# 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. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. + +ifdef NET_ETH0_STARTONBOOT + PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1 +endif + +# Ensure we package the BIOS files too. +PRODUCT_HOST_PACKAGES += \ + bios.bin \ + vgabios-cirrus.bin \ diff --git a/make/target/board/generic_x86/system_ext.prop b/make/target/board/generic_x86/system_ext.prop new file mode 100644 index 0000000..64829f3 --- /dev/null +++ b/make/target/board/generic_x86/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib/libreference-ril.so diff --git a/make/target/board/generic_x86_64/BoardConfig.mk b/make/target/board/generic_x86_64/BoardConfig.mk new file mode 100755 index 0000000..93694f2 --- /dev/null +++ b/make/target/board/generic_x86_64/BoardConfig.mk @@ -0,0 +1,45 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86_64 emulator specific definitions +TARGET_CPU_ABI := x86_64 +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 + +TARGET_2ND_CPU_ABI := x86 +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 + +include build/make/target/board/BoardConfigGsiCommon.mk + +ifndef BUILDING_GSI +include build/make/target/board/BoardConfigEmuCommon.mk + +BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" + +endif # !BUILDING_GSI diff --git a/make/target/board/generic_x86_64/README.txt b/make/target/board/generic_x86_64/README.txt new file mode 100644 index 0000000..8e515c4 --- /dev/null +++ b/make/target/board/generic_x86_64/README.txt @@ -0,0 +1,7 @@ +The "generic_x86_64" product defines a non-hardware-specific x86_64 target +without a bootloader. + +It is also the target to build the generic kernel image (GKI). + +It is not a product "base class"; no other products inherit +from it or use it in any way. diff --git a/make/target/board/generic_x86_64/device.mk b/make/target/board/generic_x86_64/device.mk new file mode 100755 index 0000000..fa1eb67 --- /dev/null +++ b/make/target/board/generic_x86_64/device.mk @@ -0,0 +1,15 @@ +# +# 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. +# diff --git a/make/target/board/generic_x86_64/system_ext.prop b/make/target/board/generic_x86_64/system_ext.prop new file mode 100644 index 0000000..ed9d173 --- /dev/null +++ b/make/target/board/generic_x86_64/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/generic_x86_64_arm64/BoardConfig.mk b/make/target/board/generic_x86_64_arm64/BoardConfig.mk new file mode 100755 index 0000000..f528294 --- /dev/null +++ b/make/target/board/generic_x86_64_arm64/BoardConfig.mk @@ -0,0 +1,59 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86_64 emulator specific definitions +TARGET_CPU_ABI := x86_64 +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 + +TARGET_2ND_CPU_ABI := x86 +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 + +TARGET_NATIVE_BRIDGE_ARCH := arm64 +TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv8-a +TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_ABI := arm64-v8a + +TARGET_NATIVE_BRIDGE_2ND_ARCH := arm +TARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT := armv7-a-neon +TARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_2ND_ABI := armeabi-v7a armeabi + +BUILD_BROKEN_DUP_RULES := true + +TARGET_PRELINK_MODULE := false + +include build/make/target/board/BoardConfigMainlineCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +# the settings differ from BoardConfigMainlineCommon.mk +BOARD_USES_SYSTEM_OTHER_ODEX := + +# Resize to 4G to accommodate ASAN and CTS +BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/generic_x86_64_arm64/README.txt b/make/target/board/generic_x86_64_arm64/README.txt new file mode 100644 index 0000000..48ee319 --- /dev/null +++ b/make/target/board/generic_x86_64_arm64/README.txt @@ -0,0 +1,10 @@ +The "generic_x86_64" product defines a non-hardware-specific IA target +without a kernel or bootloader. + +It can be used to build the entire user-level system, and +will work with the IA version of the emulator, + +It is not a product "base class"; no other products inherit +from it or use it in any way. + +Third party arm64 to x86_64 translator has to be installed as well diff --git a/make/target/board/generic_x86_64_arm64/device.mk b/make/target/board/generic_x86_64_arm64/device.mk new file mode 100755 index 0000000..76242c9 --- /dev/null +++ b/make/target/board/generic_x86_64_arm64/device.mk @@ -0,0 +1,18 @@ +# +# 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. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. diff --git a/make/target/board/generic_x86_64_arm64/system_ext.prop b/make/target/board/generic_x86_64_arm64/system_ext.prop new file mode 100644 index 0000000..ed9d173 --- /dev/null +++ b/make/target/board/generic_x86_64_arm64/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib64/libreference-ril.so diff --git a/make/target/board/generic_x86_arm/BoardConfig.mk b/make/target/board/generic_x86_arm/BoardConfig.mk new file mode 100644 index 0000000..f6589b0 --- /dev/null +++ b/make/target/board/generic_x86_arm/BoardConfig.mk @@ -0,0 +1,52 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# x86 emulator specific definitions +TARGET_CPU_ABI := x86 +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 + +TARGET_NATIVE_BRIDGE_ARCH := arm +TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon +TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi + +BUILD_BROKEN_DUP_RULES := true + +# +# The inclusion order below is important. +# The settings in latter makefiles overwrite those in the former. +# +include build/make/target/board/BoardConfigMainlineCommon.mk +include build/make/target/board/BoardConfigEmuCommon.mk + +# the settings differ from BoardConfigMainlineCommon.mk +BOARD_USES_SYSTEM_OTHER_ODEX := + +# Resize to 4G to accomodate ASAN and CTS +BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 + +BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 + +# Wifi. +BOARD_WLAN_DEVICE := emulator +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated +WPA_SUPPLICANT_VERSION := VER_0_8_X +WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" +WIFI_DRIVER_FW_PATH_STA := "/dev/null" +WIFI_DRIVER_FW_PATH_AP := "/dev/null" diff --git a/make/target/board/generic_x86_arm/README.txt b/make/target/board/generic_x86_arm/README.txt new file mode 100644 index 0000000..05f7ca2 --- /dev/null +++ b/make/target/board/generic_x86_arm/README.txt @@ -0,0 +1,10 @@ +The "generic_x86_arm" product defines a non-hardware-specific IA target +without a kernel or bootloader. + +It can be used to build the entire user-level system, and +will work with the IA version of the emulator, + +It is not a product "base class"; no other products inherit +from it or use it in any way. + +Third party arm to x86 translator has to be installed as well diff --git a/make/target/board/generic_x86_arm/device.mk b/make/target/board/generic_x86_arm/device.mk new file mode 100644 index 0000000..76242c9 --- /dev/null +++ b/make/target/board/generic_x86_arm/device.mk @@ -0,0 +1,18 @@ +# +# 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. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. diff --git a/make/target/board/generic_x86_arm/system_ext.prop b/make/target/board/generic_x86_arm/system_ext.prop new file mode 100644 index 0000000..64829f3 --- /dev/null +++ b/make/target/board/generic_x86_arm/system_ext.prop @@ -0,0 +1,5 @@ +# +# system.prop for generic sdk +# + +rild.libpath=/vendor/lib/libreference-ril.so diff --git a/make/target/board/go_defaults.prop b/make/target/board/go_defaults.prop new file mode 100644 index 0000000..93071cd --- /dev/null +++ b/make/target/board/go_defaults.prop @@ -0,0 +1,15 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/make/target/board/go_defaults_512.prop b/make/target/board/go_defaults_512.prop new file mode 100644 index 0000000..a8eea9c --- /dev/null +++ b/make/target/board/go_defaults_512.prop @@ -0,0 +1,23 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# 512MB specific properties. + +# lmkd can kill more now. +ro.lmk.medium=700 + +# madvise random in ART to reduce page cache thrashing. +dalvik.vm.madvise-random=true diff --git a/make/target/board/go_defaults_common.prop b/make/target/board/go_defaults_common.prop new file mode 100644 index 0000000..ec2eb63 --- /dev/null +++ b/make/target/board/go_defaults_common.prop @@ -0,0 +1,41 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Sets Android Go recommended default values for propreties. + +# Set lowram options +ro.lmk.critical_upgrade=true +ro.lmk.upgrade_pressure=40 +ro.lmk.downgrade_pressure=60 +ro.lmk.kill_heaviest_task=false + +# set threshold to filter unused apps +pm.dexopt.downgrade_after_inactive_days=10 + +# set the compiler filter for shared apks to quicken. +# Rationale: speed has a lot of dex code expansion, it uses more ram and space +# compared to quicken. Using quicken for shared APKs on Go devices may save RAM. +# Note that this is a trade-off: here we trade clean pages for dirty pages, +# extra cpu and battery. That's because the quicken files will be jit-ed in all +# the processes that load of shared apk and the code cache is not shared. +# Some notable apps that will be affected by this are gms and chrome. +# b/65591595. +pm.dexopt.shared=quicken + +# Default heap sizes. Allow up to 256m for large heaps to make sure a single app +# doesn't take all of the RAM. +dalvik.vm.heapgrowthlimit=128m +dalvik.vm.heapsize=256m diff --git a/make/target/board/gsi_arm64/BoardConfig.mk b/make/target/board/gsi_arm64/BoardConfig.mk new file mode 100644 index 0000000..db6f3f0 --- /dev/null +++ b/make/target/board/gsi_arm64/BoardConfig.mk @@ -0,0 +1,37 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include build/make/target/board/BoardConfigGsiCommon.mk + +TARGET_ARCH := arm64 +TARGET_ARCH_VARIANT := armv8-a +TARGET_CPU_ABI := arm64-v8a +TARGET_CPU_ABI2 := +TARGET_CPU_VARIANT := generic + +TARGET_2ND_ARCH := arm +TARGET_2ND_ARCH_VARIANT := armv8-a +TARGET_2ND_CPU_ABI := armeabi-v7a +TARGET_2ND_CPU_ABI2 := armeabi +TARGET_2ND_CPU_VARIANT := generic + +# TODO(b/111434759, b/111287060) SoC specific hacks +BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp +BOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist +BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt:/firmware + +# TODO(b/36764215): remove this setting when the generic system image +# no longer has QCOM-specific directories under /. +BOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy diff --git a/make/target/board/gsi_system_ext.prop b/make/target/board/gsi_system_ext.prop new file mode 100644 index 0000000..780aadc --- /dev/null +++ b/make/target/board/gsi_system_ext.prop @@ -0,0 +1,14 @@ +# GSI always generate dex pre-opt in system image +ro.cp_system_other_odex=0 + +# GSI always disables adb authentication +ro.adb.secure=0 + +# GSI disables non-AOSP nnapi extensions on product partition +ro.nnapi.extensions.deny_on_product=true + +# TODO(b/120679683): disable RescueParty before all problem apps solved +persist.sys.disable_rescue=true + +# TODO(b/78105955): disable privapp_permissions checking before the bug solved +ro.control_privapp_permissions=disable diff --git a/make/target/board/gsi_system_ext_user.prop b/make/target/board/gsi_system_ext_user.prop new file mode 100644 index 0000000..217bd01 --- /dev/null +++ b/make/target/board/gsi_system_ext_user.prop @@ -0,0 +1,11 @@ +# GSI always generate dex pre-opt in system image +ro.cp_system_other_odex=0 + +# GSI disables non-AOSP nnapi extensions on product partition +ro.nnapi.extensions.deny_on_product=true + +# TODO(b/120679683): disable RescueParty before all problem apps solved +persist.sys.disable_rescue=true + +# TODO(b/78105955): disable privapp_permissions checking before the bug solved +ro.control_privapp_permissions=disable diff --git a/make/target/board/mainline_arm64/BoardConfig.mk b/make/target/board/mainline_arm64/BoardConfig.mk new file mode 100644 index 0000000..a09960f --- /dev/null +++ b/make/target/board/mainline_arm64/BoardConfig.mk @@ -0,0 +1,42 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +TARGET_ARCH := arm64 +TARGET_ARCH_VARIANT := armv8-a +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := arm64-v8a + +TARGET_2ND_ARCH := arm +TARGET_2ND_ARCH_VARIANT := armv8-a +TARGET_2ND_CPU_ABI := armeabi-v7a +TARGET_2ND_CPU_ABI2 := armeabi +TARGET_2ND_CPU_VARIANT := generic + +include build/make/target/board/BoardConfigMainlineCommon.mk + +# TODO(b/143732851): Remove this after replacing /persit with +# /mnt/vendor/persist +BOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist +BOARD_SEPOLICY_DIRS += build/make/target/board/mainline_arm64/sepolicy + +TARGET_NO_KERNEL := true + +# Build generic A/B format system-only OTA. +AB_OTA_UPDATER := true +AB_OTA_PARTITIONS := system + +BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 diff --git a/make/target/board/mainline_arm64/bluetooth/bdroid_buildcfg.h b/make/target/board/mainline_arm64/bluetooth/bdroid_buildcfg.h new file mode 100644 index 0000000..0ea8fc2 --- /dev/null +++ b/make/target/board/mainline_arm64/bluetooth/bdroid_buildcfg.h @@ -0,0 +1,28 @@ +/* + * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BDROID_BUILDCFG_H +#define _BDROID_BUILDCFG_H + +// VSC spec support +#define BLE_VND_INCLUDED TRUE + +#endif diff --git a/make/target/board/mainline_arm64/sepolicy/OWNERS b/make/target/board/mainline_arm64/sepolicy/OWNERS new file mode 100644 index 0000000..6dc2b86 --- /dev/null +++ b/make/target/board/mainline_arm64/sepolicy/OWNERS @@ -0,0 +1 @@ +include platform/system/sepolicy:/OWNERS diff --git a/make/target/board/mainline_arm64/sepolicy/file.te b/make/target/board/mainline_arm64/sepolicy/file.te new file mode 100644 index 0000000..36baabd --- /dev/null +++ b/make/target/board/mainline_arm64/sepolicy/file.te @@ -0,0 +1,3 @@ +# TODO(b/143732851): remove this file when the mainline system image +# no longer need these SoC specific directory +type persist_file, file_type; diff --git a/make/target/board/mainline_arm64/sepolicy/file_contexts b/make/target/board/mainline_arm64/sepolicy/file_contexts new file mode 100644 index 0000000..4d02edc --- /dev/null +++ b/make/target/board/mainline_arm64/sepolicy/file_contexts @@ -0,0 +1,5 @@ +# TODO(b/143732851): remove this file when the mainline system image +# no longer need these SoC specific directory + +# /persist +/persist(/.*)? u:object_r:persist_file:s0 diff --git a/make/target/board/mainline_sdk/BoardConfig.mk b/make/target/board/mainline_sdk/BoardConfig.mk new file mode 100644 index 0000000..84f8b2d --- /dev/null +++ b/make/target/board/mainline_sdk/BoardConfig.mk @@ -0,0 +1,20 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +TARGET_ARCH_SUITE := mainline_sdk + +HOST_CROSS_OS := linux_bionic +HOST_CROSS_ARCH := x86_64 +HOST_CROSS_2ND_ARCH := diff --git a/make/target/board/mainline_sdk/README.md b/make/target/board/mainline_sdk/README.md new file mode 100644 index 0000000..714f797 --- /dev/null +++ b/make/target/board/mainline_sdk/README.md @@ -0,0 +1,2 @@ +This device is suitable for a soong-only build that builds for all the architectures +needed for mainline modules sdk prebuilts. diff --git a/make/target/board/mainline_x86/BoardConfig.mk b/make/target/board/mainline_x86/BoardConfig.mk new file mode 100644 index 0000000..4dda058 --- /dev/null +++ b/make/target/board/mainline_x86/BoardConfig.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 +TARGET_CPU_ABI := x86 + +include build/make/target/board/BoardConfigMainlineCommon.mk + +TARGET_NO_KERNEL := true + +# Build generic A/B format system-only OTA. +AB_OTA_UPDATER := true +AB_OTA_PARTITIONS := system + +BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 diff --git a/make/target/board/mainline_x86_64/BoardConfig.mk b/make/target/board/mainline_x86_64/BoardConfig.mk new file mode 100644 index 0000000..118fda6 --- /dev/null +++ b/make/target/board/mainline_x86_64/BoardConfig.mk @@ -0,0 +1,35 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 +TARGET_CPU_ABI := x86_64 + +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 +TARGET_2ND_CPU_ABI := x86 + +include build/make/target/board/BoardConfigMainlineCommon.mk + +TARGET_NO_KERNEL := true + +# Build generic A/B format system-only OTA. +AB_OTA_UPDATER := true +AB_OTA_PARTITIONS := system + +BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 diff --git a/make/target/board/mainline_x86_arm/BoardConfig.mk b/make/target/board/mainline_x86_arm/BoardConfig.mk new file mode 100644 index 0000000..d775a77 --- /dev/null +++ b/make/target/board/mainline_x86_arm/BoardConfig.mk @@ -0,0 +1,36 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 +TARGET_CPU_ABI := x86 + +TARGET_NATIVE_BRIDGE_ARCH := arm +TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon +TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic +TARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi + +include build/make/target/board/BoardConfigMainlineCommon.mk + +TARGET_NO_KERNEL := true + +# Build generic A/B format system-only OTA. +AB_OTA_UPDATER := true +AB_OTA_PARTITIONS := system + +BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 diff --git a/make/target/board/module_arm/BoardConfig.mk b/make/target/board/module_arm/BoardConfig.mk new file mode 100644 index 0000000..3f35c06 --- /dev/null +++ b/make/target/board/module_arm/BoardConfig.mk @@ -0,0 +1,22 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include build/make/target/board/BoardConfigModuleCommon.mk + +TARGET_ARCH := arm +TARGET_ARCH_VARIANT := armv7-a-neon +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := armeabi-v7a +TARGET_CPU_ABI2 := armeabi diff --git a/make/target/board/module_arm/README.md b/make/target/board/module_arm/README.md new file mode 100644 index 0000000..b893573 --- /dev/null +++ b/make/target/board/module_arm/README.md @@ -0,0 +1,2 @@ +This device is suitable for an unbundled module targeted specifically to an arm +device. diff --git a/make/target/board/module_arm64/BoardConfig.mk b/make/target/board/module_arm64/BoardConfig.mk new file mode 100644 index 0000000..3700056 --- /dev/null +++ b/make/target/board/module_arm64/BoardConfig.mk @@ -0,0 +1,27 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include build/make/target/board/BoardConfigModuleCommon.mk + +TARGET_ARCH := arm64 +TARGET_ARCH_VARIANT := armv8-a +TARGET_CPU_VARIANT := generic +TARGET_CPU_ABI := arm64-v8a + +TARGET_2ND_ARCH := arm +TARGET_2ND_ARCH_VARIANT := armv8-a +TARGET_2ND_CPU_ABI := armeabi-v7a +TARGET_2ND_CPU_ABI2 := armeabi +TARGET_2ND_CPU_VARIANT := generic diff --git a/make/target/board/module_arm64/README.md b/make/target/board/module_arm64/README.md new file mode 100644 index 0000000..cb36fbf --- /dev/null +++ b/make/target/board/module_arm64/README.md @@ -0,0 +1,3 @@ +This device is suitable for an unbundled module targeted specifically to an +arm64 device. 32 bit binaries built with this product will not be suitable for a +32-bit arm device. diff --git a/make/target/board/module_x86/BoardConfig.mk b/make/target/board/module_x86/BoardConfig.mk new file mode 100644 index 0000000..a93ac97 --- /dev/null +++ b/make/target/board/module_x86/BoardConfig.mk @@ -0,0 +1,20 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include build/make/target/board/BoardConfigModuleCommon.mk + +TARGET_CPU_ABI := x86 +TARGET_ARCH := x86 +TARGET_ARCH_VARIANT := x86 diff --git a/make/target/board/module_x86/README.md b/make/target/board/module_x86/README.md new file mode 100644 index 0000000..10866b7 --- /dev/null +++ b/make/target/board/module_x86/README.md @@ -0,0 +1,2 @@ +This device is suitable for an unbundled module targeted specifically to an +x86 device. diff --git a/make/target/board/module_x86_64/BoardConfig.mk b/make/target/board/module_x86_64/BoardConfig.mk new file mode 100644 index 0000000..1ed3be0 --- /dev/null +++ b/make/target/board/module_x86_64/BoardConfig.mk @@ -0,0 +1,24 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include build/make/target/board/BoardConfigModuleCommon.mk + +TARGET_CPU_ABI := x86_64 +TARGET_ARCH := x86_64 +TARGET_ARCH_VARIANT := x86_64 + +TARGET_2ND_CPU_ABI := x86 +TARGET_2ND_ARCH := x86 +TARGET_2ND_ARCH_VARIANT := x86_64 diff --git a/make/target/board/module_x86_64/README.md b/make/target/board/module_x86_64/README.md new file mode 100644 index 0000000..3377baa --- /dev/null +++ b/make/target/board/module_x86_64/README.md @@ -0,0 +1,3 @@ +This device is suitable for an unbundled module targeted specifically to an +x86_64 device. 32 bit binaries built with this product will not be suitable for +a 32-bit x86 device. diff --git a/make/target/board/ndk/BoardConfig.mk b/make/target/board/ndk/BoardConfig.mk new file mode 100644 index 0000000..da8b5f3 --- /dev/null +++ b/make/target/board/ndk/BoardConfig.mk @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +TARGET_ARCH_SUITE := ndk +TARGET_USES_64_BIT_BINDER := true + +MALLOC_SVELTE := true + +USE_SAFESTACK := false diff --git a/make/target/board/ndk/README.md b/make/target/board/ndk/README.md new file mode 100644 index 0000000..d8f3a16 --- /dev/null +++ b/make/target/board/ndk/README.md @@ -0,0 +1,2 @@ +This device is suitable for a soong-only build that builds for all the architectures +needed for the ndk. diff --git a/make/target/product/AndroidProducts.mk b/make/target/product/AndroidProducts.mk new file mode 100644 index 0000000..ee702e5 --- /dev/null +++ b/make/target/product/AndroidProducts.mk @@ -0,0 +1,87 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# This file should set PRODUCT_MAKEFILES to a list of product makefiles +# to expose to the build system. LOCAL_DIR will already be set to +# the directory containing this file. +# PRODUCT_MAKEFILES is set up in AndroidProducts.mks. +# Format of PRODUCT_MAKEFILES: +# : +# If the is the same as the base file name (without dir +# and the .mk suffix) of the product makefile, ":" can be +# omitted. +# +# This file may not rely on the value of any variable other than +# LOCAL_DIR; do not use any conditionals, and do not look up the +# value of any variable that isn't set in this file or in a file that +# it includes. +# + +# Unbundled apps will be built with the most generic product config. +ifneq ($(TARGET_BUILD_APPS),) +PRODUCT_MAKEFILES := \ + $(LOCAL_DIR)/aosp_arm64.mk \ + $(LOCAL_DIR)/aosp_arm.mk \ + $(LOCAL_DIR)/aosp_x86_64.mk \ + $(LOCAL_DIR)/aosp_x86.mk \ + $(LOCAL_DIR)/full.mk \ + $(LOCAL_DIR)/full_x86.mk \ + +else +PRODUCT_MAKEFILES := \ + $(LOCAL_DIR)/aosp_64bitonly_x86_64.mk \ + $(LOCAL_DIR)/aosp_arm64.mk \ + $(LOCAL_DIR)/aosp_arm.mk \ + $(LOCAL_DIR)/aosp_x86_64.mk \ + $(LOCAL_DIR)/aosp_x86_arm.mk \ + $(LOCAL_DIR)/aosp_x86.mk \ + $(LOCAL_DIR)/full.mk \ + $(LOCAL_DIR)/full_x86.mk \ + $(LOCAL_DIR)/generic.mk \ + $(LOCAL_DIR)/generic_system_arm64.mk \ + $(LOCAL_DIR)/generic_system_x86.mk \ + $(LOCAL_DIR)/generic_system_x86_64.mk \ + $(LOCAL_DIR)/generic_system_x86_arm.mk \ + $(LOCAL_DIR)/generic_x86.mk \ + $(LOCAL_DIR)/mainline_system_arm64.mk \ + $(LOCAL_DIR)/mainline_system_x86.mk \ + $(LOCAL_DIR)/mainline_system_x86_64.mk \ + $(LOCAL_DIR)/mainline_system_x86_arm.mk \ + $(LOCAL_DIR)/ndk.mk \ + $(LOCAL_DIR)/sdk_arm64.mk \ + $(LOCAL_DIR)/sdk.mk \ + $(LOCAL_DIR)/sdk_phone_arm64.mk \ + $(LOCAL_DIR)/sdk_phone_armv7.mk \ + $(LOCAL_DIR)/sdk_phone_x86_64.mk \ + $(LOCAL_DIR)/sdk_phone_x86.mk \ + $(LOCAL_DIR)/sdk_x86_64.mk \ + $(LOCAL_DIR)/sdk_x86.mk \ + +endif + +PRODUCT_MAKEFILES += \ + $(LOCAL_DIR)/mainline_sdk.mk \ + $(LOCAL_DIR)/module_arm.mk \ + $(LOCAL_DIR)/module_arm64.mk \ + $(LOCAL_DIR)/module_x86.mk \ + $(LOCAL_DIR)/module_x86_64.mk \ + +COMMON_LUNCH_CHOICES := \ + aosp_arm64-eng \ + aosp_arm-eng \ + aosp_x86_64-eng \ + aosp_x86-eng \ diff --git a/make/target/product/OWNERS b/make/target/product/OWNERS new file mode 100644 index 0000000..30b1af6 --- /dev/null +++ b/make/target/product/OWNERS @@ -0,0 +1,10 @@ +per-file runtime_libart.mk = calin@google.com, mast@google.com, ngeoffray@google.com, oth@google.com, rpl@google.com, vmarko@google.com + +# GSI +per-file gsi_release.mk = file:/target/product/gsi/OWNERS +per-file developer_gsi_keys.mk = file:/target/product/gsi/OWNERS + +# Android Go +per-file go_defaults.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com +per-file go_defaults_512.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com +per-file go_defaults_common.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com diff --git a/make/target/product/aosp_64bitonly_x86_64.mk b/make/target/product/aosp_64bitonly_x86_64.mk new file mode 100644 index 0000000..4de4e0c --- /dev/null +++ b/make/target/product/aosp_64bitonly_x86_64.mk @@ -0,0 +1,72 @@ +# +# Copyright 2020 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# The system image of aosp_x86_64_app-userdebug is a GSI for the devices with: +# - x86 64 bits user space +# - 64 bits binder interface +# - system-as-root +# - VNDK enforcement +# - compatible property override enabled + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# GSI for system/product & support 64-bit apps only +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk) + +# Enable mainline checking for excat this product name +ifeq (aosp_64bitonly_x86_64,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/x86_64-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk) + +# +# Special settings for GSI releasing +# +ifeq (aosp_64bitonly_x86_64,$(TARGET_PRODUCT)) +$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) +endif + +PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ + root/init.zygote64.rc + +# This build configuration supports 64-bit apps only +PRODUCT_NAME := aosp_64bitonly_x86_64 +PRODUCT_DEVICE := generic_64bitonly_x86_64 +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on x86_64 App diff --git a/make/target/product/aosp_arm.mk b/make/target/product/aosp_arm.mk new file mode 100644 index 0000000..90acc17 --- /dev/null +++ b/make/target/product/aosp_arm.mk @@ -0,0 +1,67 @@ +# +# Copyright 2017 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# The system image of aosp_arm-userdebug is a GSI for the devices with: +# - ARM 32 bits user space +# - 64 bits binder interface +# - system-as-root +# - VNDK enforcement +# - compatible property override enabled + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking for excat this product name +ifeq (aosp_arm,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/arm32-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk) + +# +# Special settings for GSI releasing +# +ifeq (aosp_arm,$(TARGET_PRODUCT)) +$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) +endif + + +PRODUCT_NAME := aosp_arm +PRODUCT_DEVICE := generic +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on ARM32 diff --git a/make/target/product/aosp_arm64.mk b/make/target/product/aosp_arm64.mk new file mode 100644 index 0000000..01897b7 --- /dev/null +++ b/make/target/product/aosp_arm64.mk @@ -0,0 +1,69 @@ +# +# Copyright (C) 2017 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The system image of aosp_arm64-userdebug is a GSI for the devices with: +# - ARM 64 bits user space +# - 64 bits binder interface +# - system-as-root +# - VNDK enforcement +# - compatible property override enabled + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking for excat this product name +ifeq (aosp_arm64,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor or vendor_boot image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk) + +# +# Special settings for GSI releasing +# +ifeq (aosp_arm64,$(TARGET_PRODUCT)) +$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) +endif + + +PRODUCT_NAME := aosp_arm64 +PRODUCT_DEVICE := generic_arm64 +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on ARM64 diff --git a/make/target/product/aosp_base.mk b/make/target/product/aosp_base.mk new file mode 100644 index 0000000..075c1d3 --- /dev/null +++ b/make/target/product/aosp_base.mk @@ -0,0 +1,16 @@ +# +# Copyright 2013 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk) diff --git a/make/target/product/aosp_base_telephony.mk b/make/target/product/aosp_base_telephony.mk new file mode 100644 index 0000000..0d4e333 --- /dev/null +++ b/make/target/product/aosp_base_telephony.mk @@ -0,0 +1,19 @@ +# +# Copyright 2013 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk) + +PRODUCT_PACKAGES += \ + messaging diff --git a/make/target/product/aosp_product.mk b/make/target/product/aosp_product.mk new file mode 100644 index 0000000..e396ad1 --- /dev/null +++ b/make/target/product/aosp_product.mk @@ -0,0 +1,40 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Includes all AOSP product packages +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk) + +# Default AOSP sounds +$(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk) + +# Additional settings used in all AOSP builds +PRODUCT_PRODUCT_PROPERTIES += \ + ro.config.ringtone?=Ring_Synth_04.ogg \ + ro.config.notification_sound?=pixiedust.ogg \ + ro.com.android.dataroaming?=true \ + +# More AOSP packages +PRODUCT_PACKAGES += \ + messaging \ + PhotoTable \ + preinstalled-packages-platform-aosp-product.xml \ + WallpaperPicker \ + +# Telephony: +# Provide a APN configuration to GSI product +PRODUCT_COPY_FILES += \ + device/sample/etc/apns-full-conf.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/apns-conf.xml diff --git a/make/target/product/aosp_x86.mk b/make/target/product/aosp_x86.mk new file mode 100644 index 0000000..7db2c0f --- /dev/null +++ b/make/target/product/aosp_x86.mk @@ -0,0 +1,66 @@ +# +# Copyright 2013 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# The system image of aosp_x86-userdebug is a GSI for the devices with: +# - x86 32 bits user space +# - 64 bits binder interface +# - system-as-root +# - VNDK enforcement +# - compatible property override enabled + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking for excat this product name +ifeq (aosp_x86,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/x86-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk) + + +# +# Special settings for GSI releasing +# +ifeq (aosp_x86,$(TARGET_PRODUCT)) +$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) +endif + + +PRODUCT_NAME := aosp_x86 +PRODUCT_DEVICE := generic_x86 +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on x86 diff --git a/make/target/product/aosp_x86_64.mk b/make/target/product/aosp_x86_64.mk new file mode 100644 index 0000000..b3cfae4 --- /dev/null +++ b/make/target/product/aosp_x86_64.mk @@ -0,0 +1,72 @@ +# +# Copyright 2013 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# The system image of aosp_x86_64-userdebug is a GSI for the devices with: +# - x86 64 bits user space +# - 64 bits binder interface +# - system-as-root +# - VNDK enforcement +# - compatible property override enabled + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking for excat this product name +ifeq (aosp_x86_64,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/x86_64-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk) + +# +# Special settings for GSI releasing +# +ifeq (aosp_x86_64,$(TARGET_PRODUCT)) +$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) +endif + + +PRODUCT_NAME := aosp_x86_64 +PRODUCT_DEVICE := generic_x86_64 +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on x86_64 diff --git a/make/target/product/aosp_x86_arm.mk b/make/target/product/aosp_x86_arm.mk new file mode 100644 index 0000000..f96e068 --- /dev/null +++ b/make/target/product/aosp_x86_arm.mk @@ -0,0 +1,56 @@ +# +# Copyright 2016 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking +ifeq (aosp_x86_arm,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# TODO (b/138382074): remove following setting after enable product/system_ext +PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ + system/product/% \ + system/system_ext/% + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/x86-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_arm/device.mk) + + +PRODUCT_NAME := aosp_x86_arm +PRODUCT_DEVICE := generic_x86_arm +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on IA Emulator diff --git a/make/target/product/base.mk b/make/target/product/base.mk new file mode 100644 index 0000000..d3250d4 --- /dev/null +++ b/make/target/product/base.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2018 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile is suitable to inherit by products that don't need to be split +# up by partition. +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk) diff --git a/make/target/product/base_product.mk b/make/target/product/base_product.mk new file mode 100644 index 0000000..5446064 --- /dev/null +++ b/make/target/product/base_product.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Base modules and settings for the product partition. +PRODUCT_PACKAGES += \ + fs_config_dirs_product \ + fs_config_files_product \ + group_product \ + ModuleMetadata \ + passwd_product \ + product_compatibility_matrix.xml \ + product_manifest.xml \ + selinux_policy_product \ diff --git a/make/target/product/base_system.mk b/make/target/product/base_system.mk new file mode 100644 index 0000000..51c175d --- /dev/null +++ b/make/target/product/base_system.mk @@ -0,0 +1,419 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Base modules and settings for the system partition. +PRODUCT_PACKAGES += \ + abx \ + adbd_system_api \ + am \ + android.hidl.allocator@1.0-service \ + android.hidl.base-V1.0-java \ + android.hidl.manager-V1.0-java \ + android.hidl.memory@1.0-impl \ + android.hidl.memory@1.0-impl.vendor \ + android.system.suspend@1.0-service \ + android.test.base \ + android.test.mock \ + android.test.runner \ + apexd \ + appops \ + app_process \ + appwidget \ + atrace \ + audioserver \ + BackupRestoreConfirmation \ + bcc \ + blank_screen \ + blkid \ + bmgr \ + bootanimation \ + bootstat \ + boringssl_self_test \ + bpfloader \ + bu \ + bugreport \ + bugreportz \ + cgroups.json \ + charger \ + cmd \ + com.android.adbd \ + com.android.adservices \ + com.android.appsearch \ + com.android.btservices \ + com.android.conscrypt \ + com.android.extservices \ + com.android.i18n \ + com.android.ipsec \ + com.android.location.provider \ + com.android.media \ + com.android.media.swcodec \ + com.android.mediaprovider \ + com.android.ondevicepersonalization \ + com.android.os.statsd \ + com.android.permission \ + com.android.resolv \ + com.android.neuralnetworks \ + com.android.scheduling \ + com.android.sdkext \ + com.android.tethering \ + com.android.tzdata \ + com.android.uwb \ + com.android.wifi \ + ContactsProvider \ + content \ + CtsShimPrebuilt \ + CtsShimPrivPrebuilt \ + debuggerd\ + device_config \ + dmctl \ + dnsmasq \ + dmesgd \ + DownloadProvider \ + dpm \ + dump.erofs \ + dumpstate \ + dumpsys \ + DynamicSystemInstallationService \ + e2fsck \ + ExtShared \ + flags_health_check \ + framework-graphics \ + framework-minus-apex \ + framework-res \ + framework-sysconfig.xml \ + fsck.erofs \ + fsck_msdos \ + fsverity-release-cert-der \ + fs_config_files_system \ + fs_config_dirs_system \ + group_system \ + gsid \ + gsi_tool \ + heapprofd \ + heapprofd_client \ + gatekeeperd \ + gpuservice \ + hid \ + hwservicemanager \ + idmap2 \ + idmap2d \ + ime \ + ims-common \ + incident \ + incidentd \ + incident_helper \ + incident-helper-cmd \ + init.environ.rc \ + init_system \ + input \ + installd \ + IntentResolver \ + ip \ + iptables \ + ip-up-vpn \ + javax.obex \ + keystore2 \ + credstore \ + ld.mc \ + libaaudio \ + libalarm_jni \ + libamidi \ + libandroid \ + libandroidfw \ + libandroid_runtime \ + libandroid_servers \ + libartpalette-system \ + libaudioeffect_jni \ + libbinder \ + libbinder_ndk \ + libbinder_rpc_unstable \ + libc.bootstrap \ + libcamera2ndk \ + libcutils \ + libdl.bootstrap \ + libdl_android.bootstrap \ + libdrmframework \ + libdrmframework_jni \ + libEGL \ + libETC1 \ + libfdtrack \ + libFFTEm \ + libfilterfw \ + libgatekeeper \ + libGLESv1_CM \ + libGLESv2 \ + libGLESv3 \ + libgui \ + libhardware \ + libhardware_legacy \ + libincident \ + libinput \ + libinputflinger \ + libiprouteutil \ + libjnigraphics \ + libjpeg \ + liblog \ + libm.bootstrap \ + libmdnssd \ + libmedia \ + libmedia_jni \ + libmediandk \ + libmtp \ + libnetd_client \ + libnetlink \ + libnetutils \ + libneuralnetworks_packageinfo \ + libOpenMAXAL \ + libOpenSLES \ + libpdfium \ + libpower \ + libpowermanager \ + libradio_metadata \ + librtp_jni \ + libsensorservice \ + libsfplugin_ccodec \ + libskia \ + libsonic \ + libsonivox \ + libsoundpool \ + libspeexresampler \ + libsqlite \ + libstagefright \ + libstagefright_foundation \ + libstagefright_omx \ + libstdc++ \ + libsysutils \ + libui \ + libusbhost \ + libutils \ + libvulkan \ + libwilhelm \ + linker \ + linkerconfig \ + llkd \ + lmkd \ + LocalTransport \ + locksettings \ + logcat \ + logd \ + lpdump \ + lshal \ + mdnsd \ + mediacodec.policy \ + mediaextractor \ + mediametrics \ + media_profiles_V1_0.dtd \ + MediaProviderLegacy \ + mediaserver \ + mke2fs \ + mkfs.erofs \ + monkey \ + mtpd \ + ndc \ + netd \ + NetworkStack \ + odsign \ + org.apache.http.legacy \ + otacerts \ + PackageInstaller \ + passwd_system \ + perfetto \ + ping \ + ping6 \ + platform.xml \ + pm \ + pppd \ + preinstalled-packages-platform.xml \ + privapp-permissions-platform.xml \ + prng_seeder \ + racoon \ + recovery-persist \ + resize2fs \ + rss_hwm_reset \ + run-as \ + sanitizer.libraries.txt \ + schedtest \ + screencap \ + sdcard \ + secdiscard \ + SecureElement \ + selinux_policy_system \ + sensorservice \ + service \ + servicemanager \ + services \ + settings \ + SettingsProvider \ + sgdisk \ + Shell \ + shell_and_utilities_system \ + sm \ + snapshotctl \ + snapuserd \ + SoundPicker \ + storaged \ + surfaceflinger \ + svc \ + task_profiles.json \ + tc \ + telecom \ + telephony-common \ + tombstoned \ + traced \ + traced_probes \ + tune2fs \ + tzdatacheck \ + uiautomator \ + uinput \ + uncrypt \ + usbd \ + vdc \ + viewcompiler \ + voip-common \ + vold \ + WallpaperBackup \ + watchdogd \ + wificond \ + wifi.rc \ + wm \ + +# VINTF data for system image +PRODUCT_PACKAGES += \ + system_manifest.xml \ + system_compatibility_matrix.xml \ + +# HWASAN runtime for SANITIZE_TARGET=hwaddress builds +ifneq (,$(filter hwaddress,$(SANITIZE_TARGET))) + PRODUCT_PACKAGES += \ + libclang_rt.hwasan.bootstrap +endif + +# Jacoco agent JARS to be built and installed, if any. +ifeq ($(EMMA_INSTRUMENT),true) + ifneq ($(EMMA_INSTRUMENT_STATIC),true) + # For instrumented build, if Jacoco is not being included statically + # in instrumented packages then include Jacoco classes in the product + # packages. + PRODUCT_PACKAGES += jacocoagent + ifneq ($(EMMA_INSTRUMENT_FRAMEWORK),true) + # For instrumented build, if Jacoco is not being included statically + # in instrumented packages and has not already been included in the + # bootclasspath via ART_APEX_JARS then include Jacoco classes into the + # bootclasspath. + PRODUCT_BOOT_JARS += jacocoagent + endif # EMMA_INSTRUMENT_FRAMEWORK + endif # EMMA_INSTRUMENT_STATIC +endif # EMMA_INSTRUMENT + +# Host tools to install +PRODUCT_HOST_PACKAGES += \ + BugReport \ + adb \ + art-tools \ + atest \ + bcc \ + bit \ + dump.erofs \ + e2fsck \ + fastboot \ + flags_health_check \ + fsck.erofs \ + icu-data_host_i18n_apex \ + icu_tzdata.dat_host_tzdata_apex \ + idmap2 \ + incident_report \ + ld.mc \ + lpdump \ + minigzip \ + mke2fs \ + mkfs.erofs \ + resize2fs \ + sgdisk \ + sqlite3 \ + tinyplay \ + tune2fs \ + tzdatacheck \ + unwind_info \ + unwind_reg_info \ + unwind_symbols \ + viewcompiler \ + tzdata_host \ + tzdata_host_tzdata_apex \ + tzlookup.xml_host_tzdata_apex \ + tz_version_host \ + tz_version_host_tzdata_apex \ + + +PRODUCT_COPY_FILES += \ + system/core/rootdir/init.usb.rc:system/etc/init/hw/init.usb.rc \ + system/core/rootdir/init.usb.configfs.rc:system/etc/init/hw/init.usb.configfs.rc \ + system/core/rootdir/etc/hosts:system/etc/hosts + +PRODUCT_COPY_FILES += system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc +PRODUCT_COPY_FILES += system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc +PRODUCT_VENDOR_PROPERTIES += ro.zygote?=zygote32 + +PRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0 +PRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1 + +# Packages included only for eng or userdebug builds, previously debug tagged +PRODUCT_PACKAGES_DEBUG := \ + adb_keys \ + arping \ + dmuserd \ + idlcli \ + init-debug.rc \ + iotop \ + iperf3 \ + iw \ + logpersist.start \ + logtagd.rc \ + procrank \ + profcollectd \ + profcollectctl \ + remount \ + servicedispatcher \ + showmap \ + sqlite3 \ + ss \ + start_with_lockagent \ + strace \ + su \ + sanitizer-status \ + tracepath \ + tracepath6 \ + traceroute6 \ + unwind_info \ + unwind_reg_info \ + unwind_symbols \ + +# The set of packages whose code can be loaded by the system server. +PRODUCT_SYSTEM_SERVER_APPS += \ + SettingsProvider \ + WallpaperBackup + +PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE := \ + libdumpcoverage + +PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\ + frameworks/base/config/preloaded-classes:system/etc/preloaded-classes) + +# Note: it is acceptable to not have a dirty-image-objects file. In that case, the special bin +# for known dirty objects in the image will be empty. +PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\ + frameworks/base/config/dirty-image-objects:system/etc/dirty-image-objects) + +$(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk) diff --git a/make/target/product/base_system_ext.mk b/make/target/product/base_system_ext.mk new file mode 100644 index 0000000..852d7ca --- /dev/null +++ b/make/target/product/base_system_ext.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Base modules and settings for the system_ext partition. +PRODUCT_PACKAGES += \ + fs_config_dirs_system_ext \ + fs_config_files_system_ext \ + group_system_ext \ + passwd_system_ext \ + selinux_policy_system_ext \ + system_ext_manifest.xml \ diff --git a/make/target/product/base_vendor.mk b/make/target/product/base_vendor.mk new file mode 100644 index 0000000..5004b85 --- /dev/null +++ b/make/target/product/base_vendor.mk @@ -0,0 +1,86 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Base modules and settings for recovery. +PRODUCT_PACKAGES += \ + adbd.recovery \ + android.hardware.health@2.0-impl-default.recovery \ + cgroups.recovery.json \ + charger.recovery \ + init_second_stage.recovery \ + ld.config.recovery.txt \ + linker.recovery \ + otacerts.recovery \ + recovery \ + servicemanager.recovery \ + shell_and_utilities_recovery \ + watchdogd.recovery \ + +# These had been pulled in via init_second_stage.recovery, but may not be needed. +PRODUCT_HOST_PACKAGES += \ + e2fsdroid \ + mke2fs \ + sload_f2fs \ + make_f2fs \ + +PRODUCT_HOST_PACKAGES += \ + icu-data_host_i18n_apex + +# Base modules and settings for the vendor partition. +PRODUCT_PACKAGES += \ + android.hardware.cas@1.2-service \ + android.hardware.media.omx@1.0-service \ + boringssl_self_test_vendor \ + dumpsys_vendor \ + fs_config_files_nonsystem \ + fs_config_dirs_nonsystem \ + gralloc.default \ + group_odm \ + group_vendor \ + init_vendor \ + libbundlewrapper \ + libclearkeycasplugin \ + libdownmix \ + libdrmclearkeyplugin \ + libdynproc \ + libeffectproxy \ + libeffects \ + libhapticgenerator \ + libldnhncr \ + libreference-ril \ + libreverbwrapper \ + libril \ + libvisualizer \ + passwd_odm \ + passwd_vendor \ + selinux_policy_nonsystem \ + shell_and_utilities_vendor \ + +# Base module when shipping api level is less than or equal to 29 +PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29 += \ + android.hardware.configstore@1.1-service \ + vndservice \ + vndservicemanager \ + +# VINTF data for vendor image +PRODUCT_PACKAGES += \ + vendor_compatibility_matrix.xml \ + +# Base modules and settings for the debug ramdisk, which is then packed +# into a boot-debug.img and a vendor_boot-debug.img. +PRODUCT_PACKAGES += \ + adb_debug.prop \ + userdebug_plat_sepolicy.cil diff --git a/make/target/product/cfi-common.mk b/make/target/product/cfi-common.mk new file mode 100644 index 0000000..559963c --- /dev/null +++ b/make/target/product/cfi-common.mk @@ -0,0 +1,43 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a set of common components to enable CFI for (across +# compatible product configs) +PRODUCT_CFI_INCLUDE_PATHS := \ + device/generic/goldfish/wifi/wpa_supplicant_8_lib \ + device/google/cuttlefish/guest/libs/wpa_supplicant_8_lib \ + external/tinyxml2 \ + external/wpa_supplicant_8 \ + frameworks/av/camera \ + frameworks/av/media \ + frameworks/av/services \ + frameworks/minikin \ + hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib \ + hardware/synaptics/wlan/synadhd/wpa_supplicant_8_lib \ + hardware/interfaces/nfc \ + hardware/qcom/wlan/qcwcn/wpa_supplicant_8_lib \ + hardware/qcom/wlan/legacy/qcwcn/wpa_supplicant_8_lib \ + hardware/qcom/wlan/wcn6740/qcwcn/wpa_supplicant_8_lib \ + hardware/interfaces/keymaster \ + hardware/interfaces/security \ + packages/modules/Bluetooth/system \ + system/chre \ + system/core/libnetutils \ + system/libziparchive \ + system/gatekeeper \ + system/keymaster \ + system/nfc \ + system/security \ diff --git a/make/target/product/core_64_bit.mk b/make/target/product/core_64_bit.mk new file mode 100644 index 0000000..b9d22a6 --- /dev/null +++ b/make/target/product/core_64_bit.mk @@ -0,0 +1,37 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit from this product for devices that support 64-bit apps using: +# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +# The inheritance for this must come before the inheritance chain that leads +# to core_minimal.mk + +# For now this will allow 64-bit apps, but still compile all apps with JNI +# for 32-bit only. + +# Copy the 64-bit primary, 32-bit secondary zygote startup script +PRODUCT_COPY_FILES += system/core/rootdir/init.zygote64_32.rc:system/etc/init/hw/init.zygote64_32.rc + +# Set the zygote property to select the 64-bit primary, 32-bit secondary script +# This line must be parsed before the one in core_minimal.mk +ifeq ($(ZYGOTE_FORCE_64),true) +PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64 +else +PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64_32 +endif + +TARGET_SUPPORTS_32_BIT_APPS := true +TARGET_SUPPORTS_64_BIT_APPS := true diff --git a/make/target/product/core_64_bit_only.mk b/make/target/product/core_64_bit_only.mk new file mode 100644 index 0000000..061728f --- /dev/null +++ b/make/target/product/core_64_bit_only.mk @@ -0,0 +1,33 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit from this product for devices that support only 64-bit apps using: +# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) +# The inheritance for this must come before the inheritance chain that leads +# to core_minimal.mk. + +# Copy the 64-bit zygote startup script +PRODUCT_COPY_FILES += system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc + +# Set the zygote property to select the 64-bit script. +# This line must be parsed before the one in core_minimal.mk +PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64 +# A 64-bit-only platform does not have dex2oat32, so make sure dex2oat64 is +# used for dexopt. +PRODUCT_VENDOR_PROPERTIES += dalvik.vm.dex2oat64.enabled=true + +TARGET_SUPPORTS_32_BIT_APPS := false +TARGET_SUPPORTS_64_BIT_APPS := true diff --git a/make/target/product/core_minimal.mk b/make/target/product/core_minimal.mk new file mode 100644 index 0000000..6fb1173 --- /dev/null +++ b/make/target/product/core_minimal.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This product is the base of a generic media-capable device, which +# means most android products, but excludes wearables. +# +# Note: Do not add any contents directly to this file. Choose either +# media_ depending on partition also consider base_.mk or +# handheld_.mk. + +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk) + +PRODUCT_BRAND := generic +PRODUCT_DEVICE := generic +PRODUCT_NAME := core diff --git a/make/target/product/core_no_zygote.mk b/make/target/product/core_no_zygote.mk new file mode 100644 index 0000000..205a897 --- /dev/null +++ b/make/target/product/core_no_zygote.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit from this product for devices that do not include a zygote using: +# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) +# The inheritance for this must come before the inheritance chain that leads +# to core_minimal.mk. + +# Copy the no-zygote startup script +PRODUCT_COPY_FILES += system/core/rootdir/init.no_zygote.rc:system/etc/init/hw/init.no_zygote.rc + +# Set the zygote property to select the no-zygote script. +# This line must be parsed before the one in core_minimal.mk +PRODUCT_VENDOR_PROPERTIES += ro.zygote=no_zygote + +TARGET_SUPPORTS_32_BIT_APPS := false +TARGET_SUPPORTS_64_BIT_APPS := false diff --git a/make/target/product/default_art_config.mk b/make/target/product/default_art_config.mk new file mode 100644 index 0000000..e2bb9d5 --- /dev/null +++ b/make/target/product/default_art_config.mk @@ -0,0 +1,110 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This file contains product config for the ART module that is common for +# platform and unbundled builds. + +ifeq ($(ART_APEX_JARS),) + $(error ART_APEX_JARS is empty; cannot initialize PRODUCT_BOOT_JARS variable) +endif + +# Order of the jars on BOOTCLASSPATH follows: +# 1. ART APEX jars +# 2. System jars +# 3. System_ext jars +# 4. Non-updatable APEX jars +# 5. Updatable APEX jars +# +# ART APEX jars (1) are defined in ART_APEX_JARS. System and system_ext boot jars are defined below +# in PRODUCT_BOOT_JARS. All other non-art APEX boot jars are part of the PRODUCT_APEX_BOOT_JARS. +# +# The actual runtime ordering matching above is determined by derive_classpath service at runtime. +# See packages/modules/SdkExtensions/README.md for more details. + +# The order of PRODUCT_BOOT_JARS matters for runtime class lookup performance. +PRODUCT_BOOT_JARS := \ + $(ART_APEX_JARS) + +# /system and /system_ext boot jars. +PRODUCT_BOOT_JARS += \ + framework-minus-apex \ + framework-graphics \ + ext \ + telephony-common \ + voip-common \ + ims-common + +# APEX boot jars. Keep the list sorted by module names and then library names. +# Note: core-icu4j is moved back to PRODUCT_BOOT_JARS in product_config.mk at a later stage. +# Note: For modules available in Q, DO NOT add new entries here. +PRODUCT_APEX_BOOT_JARS := \ + com.android.adservices:framework-adservices \ + com.android.adservices:framework-sdksandbox \ + com.android.appsearch:framework-appsearch \ + com.android.btservices:framework-bluetooth \ + com.android.conscrypt:conscrypt \ + com.android.i18n:core-icu4j \ + com.android.ipsec:android.net.ipsec.ike \ + com.android.media:updatable-media \ + com.android.mediaprovider:framework-mediaprovider \ + com.android.ondevicepersonalization:framework-ondevicepersonalization \ + com.android.os.statsd:framework-statsd \ + com.android.permission:framework-permission \ + com.android.permission:framework-permission-s \ + com.android.scheduling:framework-scheduling \ + com.android.sdkext:framework-sdkextensions \ + com.android.tethering:framework-connectivity \ + com.android.tethering:framework-connectivity-t \ + com.android.tethering:framework-tethering \ + com.android.uwb:framework-uwb \ + com.android.wifi:framework-wifi \ + +# List of system_server classpath jars delivered via apex. +# Keep the list sorted by module names and then library names. +# Note: For modules available in Q, DO NOT add new entries here. +PRODUCT_APEX_SYSTEM_SERVER_JARS := \ + com.android.adservices:service-adservices \ + com.android.adservices:service-sdksandbox \ + com.android.appsearch:service-appsearch \ + com.android.art:service-art \ + com.android.media:service-media-s \ + com.android.permission:service-permission \ + +PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += art/build/boot/boot-image-profile.txt + +# List of jars on the platform that system_server loads dynamically using separate classloaders. +# Keep the list sorted library names. +PRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \ + +# List of jars delivered via apex that system_server loads dynamically using separate classloaders. +# Keep the list sorted by module names and then library names. +# Note: For modules available in Q, DO NOT add new entries here. +PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS := \ + com.android.btservices:service-bluetooth \ + com.android.os.statsd:service-statsd \ + com.android.scheduling:service-scheduling \ + com.android.tethering:service-connectivity \ + com.android.uwb:service-uwb \ + com.android.wifi:service-wifi \ + +# Minimal configuration for running dex2oat (default argument values). +# PRODUCT_USES_DEFAULT_ART_CONFIG must be true to enable boot image compilation. +PRODUCT_USES_DEFAULT_ART_CONFIG := true +PRODUCT_SYSTEM_PROPERTIES += \ + dalvik.vm.image-dex2oat-Xms=64m \ + dalvik.vm.image-dex2oat-Xmx=64m \ + dalvik.vm.dex2oat-Xms=64m \ + dalvik.vm.dex2oat-Xmx=512m \ diff --git a/make/target/product/developer_gsi_keys.mk b/make/target/product/developer_gsi_keys.mk new file mode 100644 index 0000000..a7e3d62 --- /dev/null +++ b/make/target/product/developer_gsi_keys.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2019 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Device makers who are willing to support booting the public Developer-GSI +# in locked state can add the following line into a device.mk to inherit this +# makefile. This file will then install the up-to-date GSI public keys into +# the first-stage ramdisk to pass verified boot. +# +# In device///device.mk: +# $(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk) +# +# Currently, the developer GSI images can be downloaded from the following URL: +# https://developer.android.com/topic/generic-system-image/releases +# +PRODUCT_PACKAGES += \ + q-developer-gsi.avbpubkey \ + r-developer-gsi.avbpubkey \ + s-developer-gsi.avbpubkey \ diff --git a/make/target/product/empty-preloaded-classes b/make/target/product/empty-preloaded-classes new file mode 100644 index 0000000..c2ff1e9 --- /dev/null +++ b/make/target/product/empty-preloaded-classes @@ -0,0 +1 @@ +# Empty preloaded-classes file for automated testing. diff --git a/make/target/product/empty-profile b/make/target/product/empty-profile new file mode 100644 index 0000000..c2ff1e9 --- /dev/null +++ b/make/target/product/empty-profile @@ -0,0 +1 @@ +# Empty preloaded-classes file for automated testing. diff --git a/make/target/product/emulated_storage.mk b/make/target/product/emulated_storage.mk new file mode 100644 index 0000000..7d380d9 --- /dev/null +++ b/make/target/product/emulated_storage.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2020 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_QUOTA_PROJID := 1 +PRODUCT_VENDOR_PROPERTIES += external_storage.projid.enabled=1 + +PRODUCT_FS_CASEFOLD := 1 +PRODUCT_VENDOR_PROPERTIES += external_storage.casefold.enabled=1 + +PRODUCT_VENDOR_PROPERTIES += external_storage.sdcardfs.enabled=0 diff --git a/make/target/product/emulator.mk b/make/target/product/emulator.mk new file mode 100644 index 0000000..36da1f7 --- /dev/null +++ b/make/target/product/emulator.mk @@ -0,0 +1,60 @@ +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# This file is included by other product makefiles to add all the +# emulator-related modules to PRODUCT_PACKAGES. +# + +# Device modules +PRODUCT_PACKAGES += \ + CarrierConfig \ + +# need this for gles libraries to load properly +# after moving to /vendor/lib/ +PRODUCT_PACKAGES += \ + vndk-sp + +# WiFi: system side +PRODUCT_PACKAGES += \ + ip \ + iw \ + wificond \ + + +PRODUCT_PACKAGE_OVERLAYS := device/generic/goldfish/overlay + +PRODUCT_CHARACTERISTICS := emulator + +PRODUCT_FULL_TREBLE_OVERRIDE := true + +# goldfish vendor partition configurations +$(call inherit-product-if-exists, device/generic/goldfish/vendor.mk) + +#watchdog tiggers reboot because location service is not +#responding, disble it for now. +#still keep it on internal master as it is still working +#once it is fixed in aosp, remove this block of comment. +#PRODUCT_VENDOR_PROPERTIES += \ +#config.disable_location=true + +# enable Google-specific location features, +# like NetworkLocationProvider and LocationCollector +PRODUCT_SYSTEM_EXT_PROPERTIES += \ + ro.com.google.locationfeatures=1 + +# disable setupwizard +PRODUCT_SYSTEM_EXT_PROPERTIES += \ + ro.setupwizard.mode=DISABLED diff --git a/make/target/product/emulator_system.mk b/make/target/product/emulator_system.mk new file mode 100644 index 0000000..b7e7cfa --- /dev/null +++ b/make/target/product/emulator_system.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This file lists emulator experimental modules added to PRODUCT_PACKAGES, +# only included by targets sdk_phone_x86/64 and sdk_gphone_x86/64 + +PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \ + system/lib/libemulator_multidisplay_jni.so \ + system/lib64/libemulator_multidisplay_jni.so \ + system/priv-app/MultiDisplayProvider/MultiDisplayProvider.apk \ + +PRODUCT_PACKAGES += MultiDisplayProvider diff --git a/make/target/product/emulator_vendor.mk b/make/target/product/emulator_vendor.mk new file mode 100644 index 0000000..f71b275 --- /dev/null +++ b/make/target/product/emulator_vendor.mk @@ -0,0 +1,52 @@ +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# This file is included by other product makefiles to add all the +# emulator-related modules to PRODUCT_PACKAGES. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk) + +# need this for gles libraries to load properly +# after moving to /vendor/lib/ +PRODUCT_PACKAGES += \ + vndk-sp + +DEVICE_PACKAGE_OVERLAYS := device/generic/goldfish/overlay + +PRODUCT_CHARACTERISTICS := emulator + +PRODUCT_FULL_TREBLE_OVERRIDE := true + +# goldfish vendor partition configurations +$(call inherit-product-if-exists, device/generic/goldfish/vendor.mk) + +#watchdog tiggers reboot because location service is not +#responding, disble it for now. +#still keep it on internal main (master) as it is still working +#once it is fixed in aosp, remove this block of comment. +#PRODUCT_VENDOR_PROPERTIES += \ +#config.disable_location=true + +# enable Google-specific location features, +# like NetworkLocationProvider and LocationCollector +PRODUCT_SYSTEM_EXT_PROPERTIES += \ + ro.com.google.locationfeatures=1 + +# disable setupwizard +PRODUCT_SYSTEM_EXT_PROPERTIES += \ + ro.setupwizard.mode?=DISABLED diff --git a/make/target/product/full.mk b/make/target/product/full.mk new file mode 100644 index 0000000..adb54ab --- /dev/null +++ b/make/target/product/full.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +$(call inherit-product-if-exists, device/generic/goldfish/arm32-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic/device.mk) + +# Enable dynamic partition size +PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true + +# Overrides +PRODUCT_NAME := full +PRODUCT_DEVICE := generic +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on ARM Emulator diff --git a/make/target/product/full_base.mk b/make/target/product/full_base.mk new file mode 100644 index 0000000..39c66da --- /dev/null +++ b/make/target/product/full_base.mk @@ -0,0 +1,57 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build of the emulator, but all those aspects can be overridden +# in inherited configurations. + +PRODUCT_PACKAGES := \ + libfwdlockengine \ + WAPPushManager + +PRODUCT_PACKAGES += \ + LiveWallpapersPicker \ + PhotoTable \ + preinstalled-packages-platform-full-base.xml + +# Net: +# Vendors can use the platform-provided network configuration utilities (ip, +# iptable, etc.) to configure the Linux networking stack, but these utilities +# do not yet include a HIDL interface wrapper. This is a solution on +# Android O. +PRODUCT_PACKAGES += \ + netutils-wrapper-1.0 + +# Additional settings used in all AOSP builds +PRODUCT_VENDOR_PROPERTIES := \ + ro.config.ringtone?=Ring_Synth_04.ogg \ + ro.config.notification_sound?=pixiedust.ogg + +# Put en_US first in the list, so make it default. +PRODUCT_LOCALES := en_US + +# Get some sounds +$(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk) + +# Get a list of languages. +$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_full.mk) + +# Get everything else from the parent package +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk) + +# Add adb keys to debuggable AOSP builds (if they exist) +$(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk) diff --git a/make/target/product/full_base_telephony.mk b/make/target/product/full_base_telephony.mk new file mode 100644 index 0000000..d8a54cd --- /dev/null +++ b/make/target/product/full_base_telephony.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +PRODUCT_VENDOR_PROPERTIES := \ + keyguard.no_require_sim?=true \ + ro.com.android.dataroaming?=true + +PRODUCT_COPY_FILES := \ + device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml \ + frameworks/native/data/etc/handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml + +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk) diff --git a/make/target/product/full_x86.mk b/make/target/product/full_x86.mk new file mode 100644 index 0000000..2f40c03 --- /dev/null +++ b/make/target/product/full_x86.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# If running on an emulator or some other device that has a LAN connection +# that isn't a wifi connection. This will instruct init.rc to enable the +# network connection so that you can use it with ADB + +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk) + +ifdef NET_ETH0_STARTONBOOT + PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1 +endif + +# Ensure we package the BIOS files too. +PRODUCT_HOST_PACKAGES += \ + bios.bin \ + vgabios-cirrus.bin \ + +# Enable dynamic partition size +PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true + +# Overrides +PRODUCT_NAME := full_x86 +PRODUCT_DEVICE := generic_x86 +PRODUCT_BRAND := Android +PRODUCT_MODEL := AOSP on IA Emulator diff --git a/make/target/product/generic.mk b/make/target/product/generic.mk new file mode 100644 index 0000000..fb5b727 --- /dev/null +++ b/make/target/product/generic.mk @@ -0,0 +1,38 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu +PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps. + +# This is a generic phone product that isn't specialized for a specific device. +# It includes the base Android platform. + +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk) + +# Overrides +PRODUCT_BRAND := generic +PRODUCT_DEVICE := generic +PRODUCT_NAME := generic + +allowed_list := product_manifest.xml + +# TODO(b/182105280): When ART prebuilts are used in this product, Soong doesn't +# produce any Android.mk entries for them. Exclude them until that problem is +# fixed. +allowed_list += com.android.art com.android.art.debug + +$(call enforce-product-packages-exist,$(allowed_list)) diff --git a/make/target/product/generic_no_telephony.mk b/make/target/product/generic_no_telephony.mk new file mode 100644 index 0000000..eb12872 --- /dev/null +++ b/make/target/product/generic_no_telephony.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This product is a generic phone or tablet, that doesn't have telephony. +# +# Note: Do not add any contents directly to this file. Choose either +# handheld_system or handheld_vendor depending on partition (also consider +# base_.mk or media_.mk. + +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk) + +PRODUCT_BRAND := generic +PRODUCT_DEVICE := generic +PRODUCT_NAME := generic_no_telephony diff --git a/make/target/product/generic_ramdisk.mk b/make/target/product/generic_ramdisk.mk new file mode 100644 index 0000000..60fe0ce --- /dev/null +++ b/make/target/product/generic_ramdisk.mk @@ -0,0 +1,41 @@ +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile installs contents of the generic ramdisk. +# Inherit from this makefile to declare that this product uses generic ramdisk. +# This makefile checks that other makefiles must not install things to the +# ramdisk. + +# Ramdisk +PRODUCT_PACKAGES += \ + init_first_stage \ + snapuserd.ramdisk \ + +# Debug ramdisk +PRODUCT_PACKAGES += \ + adb_debug.prop \ + userdebug_plat_sepolicy.cil \ + +_my_paths := \ + $(TARGET_COPY_OUT_RAMDISK)/ \ + $(TARGET_COPY_OUT_DEBUG_RAMDISK)/ \ + system/usr/share/zoneinfo/tz_version \ + system/usr/share/zoneinfo/tzdata \ + +# We use the "relaxed" version here because tzdata / tz_version is only produced +# by this makefile on a subset of devices. +# TODO: remove this +$(call require-artifacts-in-path-relaxed, $(_my_paths), ) diff --git a/make/target/product/generic_system.mk b/make/target/product/generic_system.mk new file mode 100644 index 0000000..1a639ef --- /dev/null +++ b/make/target/product/generic_system.mk @@ -0,0 +1,136 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile is the basis of a generic system image for a handheld device. +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) +# Add adb keys to debuggable AOSP builds (if they exist) +$(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk) + +# Enable updating of APEXes +$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk) + +# Shared java libs +PRODUCT_PACKAGES += \ + com.android.nfc_extras \ + +# Applications +PRODUCT_PACKAGES += \ + LiveWallpapersPicker \ + PartnerBookmarksProvider \ + Stk \ + Tag \ + +# OTA support +PRODUCT_PACKAGES += \ + recovery-refresh \ + update_engine \ + update_verifier \ + +# Wrapped net utils for /vendor access. +PRODUCT_PACKAGES += netutils-wrapper-1.0 + +# Charger images +PRODUCT_PACKAGES += charger_res_images + +# system_other support +PRODUCT_PACKAGES += \ + cppreopts.sh \ + otapreopt_script \ + +# For ringtones that rely on forward lock encryption +PRODUCT_PACKAGES += libfwdlockengine + +# System libraries commonly depended on by things on the system_ext or product partitions. +# These lists will be pruned periodically. +PRODUCT_PACKAGES += \ + android.hardware.biometrics.fingerprint@2.1 \ + android.hardware.radio@1.0 \ + android.hardware.radio@1.1 \ + android.hardware.radio@1.2 \ + android.hardware.radio@1.3 \ + android.hardware.radio@1.4 \ + android.hardware.radio.config@1.0 \ + android.hardware.radio.deprecated@1.0 \ + android.hardware.secure_element@1.0 \ + android.hardware.wifi@1.0 \ + libaudio-resampler \ + libaudiohal \ + libdrm \ + liblogwrap \ + liblz4 \ + libminui \ + libnl \ + libprotobuf-cpp-full \ + +# These libraries are empty and have been combined into libhidlbase, but are still depended +# on by things off /system. +# TODO(b/135686713): remove these +PRODUCT_PACKAGES += \ + libhidltransport \ + libhwbinder \ + +PRODUCT_PACKAGES_DEBUG += \ + avbctl \ + bootctl \ + tinycap \ + tinyhostless \ + tinymix \ + tinypcminfo \ + tinyplay \ + update_engine_client \ + +PRODUCT_HOST_PACKAGES += \ + tinyplay + +# Enable configurable audio policy +PRODUCT_PACKAGES += \ + libaudiopolicyengineconfigurable \ + libpolicy-subsystem + +# Include all zygote init scripts. "ro.zygote" will select one of them. +PRODUCT_COPY_FILES += \ + system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc \ + system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc \ + system/core/rootdir/init.zygote64_32.rc:system/etc/init/hw/init.zygote64_32.rc \ + +# Enable dynamic partition size +PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true + +PRODUCT_ENFORCE_RRO_TARGETS := * + +PRODUCT_NAME := generic_system +PRODUCT_BRAND := generic + +# Define /system partition-specific product properties to identify that /system +# partition is generic_system. +PRODUCT_SYSTEM_NAME := mainline +PRODUCT_SYSTEM_BRAND := Android +PRODUCT_SYSTEM_MANUFACTURER := Android +PRODUCT_SYSTEM_MODEL := mainline +PRODUCT_SYSTEM_DEVICE := generic + +_base_mk_allowed_list := + +_my_allowed_list := $(_base_mk_allowed_list) + +# For mainline, system.img should be mounted at /, so we include ROOT here. +_my_paths := \ + $(TARGET_COPY_OUT_ROOT)/ \ + $(TARGET_COPY_OUT_SYSTEM)/ \ + +$(call require-artifacts-in-path, $(_my_paths), $(_my_allowed_list)) diff --git a/make/target/product/generic_system_arm64.mk b/make/target/product/generic_system_arm64.mk new file mode 100644 index 0000000..0fc7803 --- /dev/null +++ b/make/target/product/generic_system_arm64.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) +$(call enforce-product-packages-exist,) + +# Enable mainline checking +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true + +PRODUCT_BUILD_CACHE_IMAGE := false +PRODUCT_BUILD_ODM_IMAGE := false +PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false +PRODUCT_BUILD_ODM_DLKM_IMAGE := false +PRODUCT_BUILD_PRODUCT_IMAGE := false +PRODUCT_BUILD_RAMDISK_IMAGE := false +PRODUCT_BUILD_SYSTEM_IMAGE := true +PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false +PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false +PRODUCT_BUILD_USERDATA_IMAGE := false +PRODUCT_BUILD_VENDOR_IMAGE := false + +PRODUCT_SHIPPING_API_LEVEL := 29 + +PRODUCT_RESTRICT_VENDOR_FILES := all + +PRODUCT_NAME := generic_system_arm64 +PRODUCT_DEVICE := mainline_arm64 +PRODUCT_BRAND := generic diff --git a/make/target/product/generic_system_x86.mk b/make/target/product/generic_system_x86.mk new file mode 100644 index 0000000..21555d4 --- /dev/null +++ b/make/target/product/generic_system_x86.mk @@ -0,0 +1,44 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) +$(call enforce-product-packages-exist,) + +# Enable mainline checking +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true + +PRODUCT_BUILD_CACHE_IMAGE := false +PRODUCT_BUILD_ODM_IMAGE := false +PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false +PRODUCT_BUILD_ODM_DLKM_IMAGE := false +PRODUCT_BUILD_PRODUCT_IMAGE := false +PRODUCT_BUILD_RAMDISK_IMAGE := false +PRODUCT_BUILD_SYSTEM_IMAGE := true +PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false +PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false +PRODUCT_BUILD_USERDATA_IMAGE := false +PRODUCT_BUILD_VENDOR_IMAGE := false + +PRODUCT_SHIPPING_API_LEVEL := 29 + +PRODUCT_RESTRICT_VENDOR_FILES := all + +PRODUCT_NAME := generic_system_x86 +PRODUCT_DEVICE := mainline_x86 +PRODUCT_BRAND := generic diff --git a/make/target/product/generic_system_x86_64.mk b/make/target/product/generic_system_x86_64.mk new file mode 100644 index 0000000..1ca9678 --- /dev/null +++ b/make/target/product/generic_system_x86_64.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) +$(call enforce-product-packages-exist,) + +# Enable mainline checking +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true + +PRODUCT_BUILD_CACHE_IMAGE := false +PRODUCT_BUILD_ODM_IMAGE := false +PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false +PRODUCT_BUILD_ODM_DLKM_IMAGE := false +PRODUCT_BUILD_PRODUCT_IMAGE := false +PRODUCT_BUILD_RAMDISK_IMAGE := false +PRODUCT_BUILD_SYSTEM_IMAGE := true +PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false +PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false +PRODUCT_BUILD_USERDATA_IMAGE := false +PRODUCT_BUILD_VENDOR_IMAGE := false + +PRODUCT_SHIPPING_API_LEVEL := 29 + +PRODUCT_RESTRICT_VENDOR_FILES := all + +PRODUCT_NAME := generic_system_x86_64 +PRODUCT_DEVICE := mainline_x86_64 +PRODUCT_BRAND := generic diff --git a/make/target/product/generic_system_x86_arm.mk b/make/target/product/generic_system_x86_arm.mk new file mode 100644 index 0000000..fe78f3b --- /dev/null +++ b/make/target/product/generic_system_x86_arm.mk @@ -0,0 +1,44 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) +$(call enforce-product-packages-exist,) + +# Enable mainline checking +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true + +PRODUCT_BUILD_CACHE_IMAGE := false +PRODUCT_BUILD_ODM_IMAGE := false +PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false +PRODUCT_BUILD_ODM_DLKM_IMAGE := false +PRODUCT_BUILD_PRODUCT_IMAGE := false +PRODUCT_BUILD_RAMDISK_IMAGE := false +PRODUCT_BUILD_SYSTEM_IMAGE := true +PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false +PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false +PRODUCT_BUILD_USERDATA_IMAGE := false +PRODUCT_BUILD_VENDOR_IMAGE := false + +PRODUCT_SHIPPING_API_LEVEL := 29 + +PRODUCT_RESTRICT_VENDOR_FILES := all + +PRODUCT_NAME := generic_system_x86_arm +PRODUCT_DEVICE := mainline_x86_arm +PRODUCT_BRAND := generic diff --git a/make/target/product/generic_x86.mk b/make/target/product/generic_x86.mk new file mode 100644 index 0000000..eeb8216 --- /dev/null +++ b/make/target/product/generic_x86.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a generic phone product that isn't specialized for a specific device. +# It includes the base Android platform. + +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk) + +# Overrides +PRODUCT_BRAND := generic_x86 +PRODUCT_DEVICE := generic_x86 +PRODUCT_NAME := generic_x86 diff --git a/make/target/product/go_defaults.mk b/make/target/product/go_defaults.mk new file mode 100644 index 0000000..b717486 --- /dev/null +++ b/make/target/product/go_defaults.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit common Android Go defaults. +$(call inherit-product, build/make/target/product/go_defaults_common.mk) + +# Add the system properties. +TARGET_SYSTEM_PROP += \ + build/make/target/board/go_defaults.prop diff --git a/make/target/product/go_defaults_512.mk b/make/target/product/go_defaults_512.mk new file mode 100644 index 0000000..70d067e --- /dev/null +++ b/make/target/product/go_defaults_512.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit common Android Go defaults. +$(call inherit-product, build/make/target/product/go_defaults_common.mk) + +# Add the system properties. +TARGET_SYSTEM_PROP += \ + build/make/target/board/go_defaults_512.prop diff --git a/make/target/product/go_defaults_common.mk b/make/target/product/go_defaults_common.mk new file mode 100644 index 0000000..51a1ef6 --- /dev/null +++ b/make/target/product/go_defaults_common.mk @@ -0,0 +1,58 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Sets Android Go recommended default product options.. + + +# Set lowram options and enable traced by default +PRODUCT_VENDOR_PROPERTIES += \ + ro.config.low_ram=true \ + +# Speed profile services and wifi-service to reduce RAM and storage. +PRODUCT_SYSTEM_SERVER_COMPILER_FILTER := speed-profile + +# Always preopt extracted APKs to prevent extracting out of the APK for gms +# modules. +PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK := true + +# Use a profile based boot image for this device. Note that this is currently a +# generic profile and not Android Go optimized. +PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE := true +PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION := frameworks/base/config/boot-image-profile.txt + +# Do not generate libartd. +PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false + +# Strip the local variable table and the local variable type table to reduce +# the size of the system image. This has no bearing on stack traces, but will +# leave less information available via JDWP. +PRODUCT_MINIMIZE_JAVA_DEBUG_INFO := true + +# Disable Scudo outside of eng builds to save RAM. +ifneq (,$(filter eng, $(TARGET_BUILD_VARIANT))) + PRODUCT_DISABLE_SCUDO := true +endif + +# Add the system properties. +TARGET_SYSTEM_PROP += \ + build/make/target/board/go_defaults_common.prop + +# use the go specific handheld_core_hardware.xml from frameworks +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/go_handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml + +# Dedupe VNDK libraries with identical core variants. +TARGET_VNDK_USE_CORE_VARIANT := true diff --git a/make/target/product/gsi/28.txt b/make/target/product/gsi/28.txt new file mode 100644 index 0000000..712e91c --- /dev/null +++ b/make/target/product/gsi/28.txt @@ -0,0 +1,252 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libhidltransport.so +VNDK-SP: libhwbinder.so +VNDK-SP: libhwbinder_noltopgo.so +VNDK-SP: libion.so +VNDK-SP: liblzma.so +VNDK-SP: libunwind.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.frameworks.displayservice@1.0.so +VNDK-core: android.frameworks.schedulerservice@1.0.so +VNDK-core: android.frameworks.sensorservice@1.0.so +VNDK-core: android.frameworks.vr.composer@1.0.so +VNDK-core: android.hardware.audio.common-util.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.audio.common@2.0-util.so +VNDK-core: android.hardware.audio.common@4.0.so +VNDK-core: android.hardware.audio.common@4.0-util.so +VNDK-core: android.hardware.audio.effect@2.0.so +VNDK-core: android.hardware.audio.effect@4.0.so +VNDK-core: android.hardware.audio@2.0.so +VNDK-core: android.hardware.audio@4.0.so +VNDK-core: android.hardware.authsecret@1.0.so +VNDK-core: android.hardware.automotive.audiocontrol@1.0.so +VNDK-core: android.hardware.automotive.evs@1.0.so +VNDK-core: android.hardware.automotive.vehicle@2.0.so +VNDK-core: android.hardware.biometrics.fingerprint@2.1.so +VNDK-core: android.hardware.bluetooth.a2dp@1.0.so +VNDK-core: android.hardware.bluetooth@1.0.so +VNDK-core: android.hardware.boot@1.0.so +VNDK-core: android.hardware.broadcastradio@1.0.so +VNDK-core: android.hardware.broadcastradio@1.1.so +VNDK-core: android.hardware.broadcastradio@2.0.so +VNDK-core: android.hardware.camera.common@1.0.so +VNDK-core: android.hardware.camera.device@1.0.so +VNDK-core: android.hardware.camera.device@3.2.so +VNDK-core: android.hardware.camera.device@3.3.so +VNDK-core: android.hardware.camera.device@3.4.so +VNDK-core: android.hardware.camera.metadata@3.2.so +VNDK-core: android.hardware.camera.metadata@3.3.so +VNDK-core: android.hardware.camera.provider@2.4.so +VNDK-core: android.hardware.cas.native@1.0.so +VNDK-core: android.hardware.cas@1.0.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.confirmationui@1.0.so +VNDK-core: android.hardware.contexthub@1.0.so +VNDK-core: android.hardware.drm@1.0.so +VNDK-core: android.hardware.drm@1.1.so +VNDK-core: android.hardware.dumpstate@1.0.so +VNDK-core: android.hardware.gatekeeper@1.0.so +VNDK-core: android.hardware.gnss@1.0.so +VNDK-core: android.hardware.gnss@1.1.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.composer@2.1.so +VNDK-core: android.hardware.graphics.composer@2.2.so +VNDK-core: android.hardware.health@1.0.so +VNDK-core: android.hardware.health@2.0.so +VNDK-core: android.hardware.ir@1.0.so +VNDK-core: android.hardware.keymaster@3.0.so +VNDK-core: android.hardware.keymaster@4.0.so +VNDK-core: android.hardware.light@2.0.so +VNDK-core: android.hardware.media.bufferpool@1.0.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.neuralnetworks@1.0.so +VNDK-core: android.hardware.neuralnetworks@1.1.so +VNDK-core: android.hardware.nfc@1.0.so +VNDK-core: android.hardware.nfc@1.1.so +VNDK-core: android.hardware.oemlock@1.0.so +VNDK-core: android.hardware.power@1.0.so +VNDK-core: android.hardware.power@1.1.so +VNDK-core: android.hardware.power@1.2.so +VNDK-core: android.hardware.radio.config@1.0.so +VNDK-core: android.hardware.radio.deprecated@1.0.so +VNDK-core: android.hardware.radio@1.0.so +VNDK-core: android.hardware.radio@1.1.so +VNDK-core: android.hardware.radio@1.2.so +VNDK-core: android.hardware.secure_element@1.0.so +VNDK-core: android.hardware.sensors@1.0.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.1.so +VNDK-core: android.hardware.tetheroffload.config@1.0.so +VNDK-core: android.hardware.tetheroffload.control@1.0.so +VNDK-core: android.hardware.thermal@1.0.so +VNDK-core: android.hardware.thermal@1.1.so +VNDK-core: android.hardware.tv.cec@1.0.so +VNDK-core: android.hardware.tv.input@1.0.so +VNDK-core: android.hardware.usb.gadget@1.0.so +VNDK-core: android.hardware.usb@1.0.so +VNDK-core: android.hardware.usb@1.1.so +VNDK-core: android.hardware.vibrator@1.0.so +VNDK-core: android.hardware.vibrator@1.1.so +VNDK-core: android.hardware.vibrator@1.2.so +VNDK-core: android.hardware.vr@1.0.so +VNDK-core: android.hardware.weaver@1.0.so +VNDK-core: android.hardware.wifi.hostapd@1.0.so +VNDK-core: android.hardware.wifi.offload@1.0.so +VNDK-core: android.hardware.wifi.supplicant@1.0.so +VNDK-core: android.hardware.wifi.supplicant@1.1.so +VNDK-core: android.hardware.wifi@1.0.so +VNDK-core: android.hardware.wifi@1.1.so +VNDK-core: android.hardware.wifi@1.2.so +VNDK-core: android.hidl.allocator@1.0.so +VNDK-core: android.hidl.memory.block@1.0.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.system.net.netd@1.0.so +VNDK-core: android.system.net.netd@1.1.so +VNDK-core: android.system.wifi.keystore@1.0.so +VNDK-core: libadf.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libhidlcache.so +VNDK-core: libjpeg.so +VNDK-core: libkeymaster_messages.so +VNDK-core: libkeymaster_portable.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libopus.so +VNDK-core: libpagemap.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libprotobuf-cpp-full.so +VNDK-core: libprotobuf-cpp-lite.so +VNDK-core: libpuresoftkeymasterdevice.so +VNDK-core: libradio_metadata.so +VNDK-core: libselinux.so +VNDK-core: libsoftkeymasterdevice.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_amrnb_common.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_enc_common.so +VNDK-core: libstagefright_flacdec.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_soft_aacdec.so +VNDK-core: libstagefright_soft_aacenc.so +VNDK-core: libstagefright_soft_amrdec.so +VNDK-core: libstagefright_soft_amrnbenc.so +VNDK-core: libstagefright_soft_amrwbenc.so +VNDK-core: libstagefright_soft_avcdec.so +VNDK-core: libstagefright_soft_avcenc.so +VNDK-core: libstagefright_soft_flacdec.so +VNDK-core: libstagefright_soft_flacenc.so +VNDK-core: libstagefright_soft_g711dec.so +VNDK-core: libstagefright_soft_gsmdec.so +VNDK-core: libstagefright_soft_hevcdec.so +VNDK-core: libstagefright_soft_mp3dec.so +VNDK-core: libstagefright_soft_mpeg2dec.so +VNDK-core: libstagefright_soft_mpeg4dec.so +VNDK-core: libstagefright_soft_mpeg4enc.so +VNDK-core: libstagefright_soft_opusdec.so +VNDK-core: libstagefright_soft_rawdec.so +VNDK-core: libstagefright_soft_vorbisdec.so +VNDK-core: libstagefright_soft_vpxdec.so +VNDK-core: libstagefright_soft_vpxenc.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsuspend.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libvixl-arm.so +VNDK-core: libvixl-arm64.so +VNDK-core: libvorbisidec.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so +VNDK-private: libunwind.so diff --git a/make/target/product/gsi/29.txt b/make/target/product/gsi/29.txt new file mode 100644 index 0000000..14faba5 --- /dev/null +++ b/make/target/product/gsi/29.txt @@ -0,0 +1,273 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libc.so +LLNDK: libcgrouprc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.common@1.2.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.graphics.mapper@3.0.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: android.hidl.safe_union@1.0.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libbinderthreadstate.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libhidltransport.so +VNDK-SP: libhwbinder.so +VNDK-SP: libhwbinder_noltopgo.so +VNDK-SP: libion.so +VNDK-SP: libjsoncpp.so +VNDK-SP: liblzma.so +VNDK-SP: libprocessgroup.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.frameworks.cameraservice.common@2.0.so +VNDK-core: android.frameworks.cameraservice.device@2.0.so +VNDK-core: android.frameworks.cameraservice.service@2.0.so +VNDK-core: android.frameworks.displayservice@1.0.so +VNDK-core: android.frameworks.schedulerservice@1.0.so +VNDK-core: android.frameworks.sensorservice@1.0.so +VNDK-core: android.frameworks.stats@1.0.so +VNDK-core: android.frameworks.vr.composer@1.0.so +VNDK-core: android.hardware.atrace@1.0.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.audio.common@4.0.so +VNDK-core: android.hardware.audio.common@5.0.so +VNDK-core: android.hardware.audio.effect@2.0.so +VNDK-core: android.hardware.audio.effect@4.0.so +VNDK-core: android.hardware.audio.effect@5.0.so +VNDK-core: android.hardware.audio@2.0.so +VNDK-core: android.hardware.audio@4.0.so +VNDK-core: android.hardware.audio@5.0.so +VNDK-core: android.hardware.authsecret@1.0.so +VNDK-core: android.hardware.automotive.audiocontrol@1.0.so +VNDK-core: android.hardware.automotive.evs@1.0.so +VNDK-core: android.hardware.automotive.vehicle@2.0.so +VNDK-core: android.hardware.biometrics.face@1.0.so +VNDK-core: android.hardware.biometrics.fingerprint@2.1.so +VNDK-core: android.hardware.bluetooth.a2dp@1.0.so +VNDK-core: android.hardware.bluetooth.audio@2.0.so +VNDK-core: android.hardware.bluetooth@1.0.so +VNDK-core: android.hardware.boot@1.0.so +VNDK-core: android.hardware.broadcastradio@1.0.so +VNDK-core: android.hardware.broadcastradio@1.1.so +VNDK-core: android.hardware.broadcastradio@2.0.so +VNDK-core: android.hardware.camera.common@1.0.so +VNDK-core: android.hardware.camera.device@1.0.so +VNDK-core: android.hardware.camera.device@3.2.so +VNDK-core: android.hardware.camera.device@3.3.so +VNDK-core: android.hardware.camera.device@3.4.so +VNDK-core: android.hardware.camera.device@3.5.so +VNDK-core: android.hardware.camera.metadata@3.2.so +VNDK-core: android.hardware.camera.metadata@3.3.so +VNDK-core: android.hardware.camera.metadata@3.4.so +VNDK-core: android.hardware.camera.provider@2.4.so +VNDK-core: android.hardware.camera.provider@2.5.so +VNDK-core: android.hardware.cas.native@1.0.so +VNDK-core: android.hardware.cas@1.0.so +VNDK-core: android.hardware.cas@1.1.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.confirmationui@1.0.so +VNDK-core: android.hardware.contexthub@1.0.so +VNDK-core: android.hardware.drm@1.0.so +VNDK-core: android.hardware.drm@1.1.so +VNDK-core: android.hardware.drm@1.2.so +VNDK-core: android.hardware.dumpstate@1.0.so +VNDK-core: android.hardware.fastboot@1.0.so +VNDK-core: android.hardware.gatekeeper@1.0.so +VNDK-core: android.hardware.gnss.measurement_corrections@1.0.so +VNDK-core: android.hardware.gnss.visibility_control@1.0.so +VNDK-core: android.hardware.gnss@1.0.so +VNDK-core: android.hardware.gnss@1.1.so +VNDK-core: android.hardware.gnss@2.0.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.allocator@3.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.bufferqueue@2.0.so +VNDK-core: android.hardware.graphics.composer@2.1.so +VNDK-core: android.hardware.graphics.composer@2.2.so +VNDK-core: android.hardware.graphics.composer@2.3.so +VNDK-core: android.hardware.health.storage@1.0.so +VNDK-core: android.hardware.health@1.0.so +VNDK-core: android.hardware.health@2.0.so +VNDK-core: android.hardware.input.classifier@1.0.so +VNDK-core: android.hardware.input.common@1.0.so +VNDK-core: android.hardware.ir@1.0.so +VNDK-core: android.hardware.keymaster@3.0.so +VNDK-core: android.hardware.keymaster@4.0.so +VNDK-core: android.hardware.light@2.0.so +VNDK-core: android.hardware.media.bufferpool@1.0.so +VNDK-core: android.hardware.media.bufferpool@2.0.so +VNDK-core: android.hardware.media.c2@1.0.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.neuralnetworks@1.0.so +VNDK-core: android.hardware.neuralnetworks@1.1.so +VNDK-core: android.hardware.neuralnetworks@1.2.so +VNDK-core: android.hardware.nfc@1.0.so +VNDK-core: android.hardware.nfc@1.1.so +VNDK-core: android.hardware.nfc@1.2.so +VNDK-core: android.hardware.oemlock@1.0.so +VNDK-core: android.hardware.power.stats@1.0.so +VNDK-core: android.hardware.power@1.0.so +VNDK-core: android.hardware.power@1.1.so +VNDK-core: android.hardware.power@1.2.so +VNDK-core: android.hardware.power@1.3.so +VNDK-core: android.hardware.radio.config@1.0.so +VNDK-core: android.hardware.radio.config@1.1.so +VNDK-core: android.hardware.radio.config@1.2.so +VNDK-core: android.hardware.radio.deprecated@1.0.so +VNDK-core: android.hardware.radio@1.0.so +VNDK-core: android.hardware.radio@1.1.so +VNDK-core: android.hardware.radio@1.2.so +VNDK-core: android.hardware.radio@1.3.so +VNDK-core: android.hardware.radio@1.4.so +VNDK-core: android.hardware.secure_element@1.0.so +VNDK-core: android.hardware.secure_element@1.1.so +VNDK-core: android.hardware.sensors@1.0.so +VNDK-core: android.hardware.sensors@2.0.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.1.so +VNDK-core: android.hardware.soundtrigger@2.2.so +VNDK-core: android.hardware.tetheroffload.config@1.0.so +VNDK-core: android.hardware.tetheroffload.control@1.0.so +VNDK-core: android.hardware.thermal@1.0.so +VNDK-core: android.hardware.thermal@1.1.so +VNDK-core: android.hardware.thermal@2.0.so +VNDK-core: android.hardware.tv.cec@1.0.so +VNDK-core: android.hardware.tv.cec@2.0.so +VNDK-core: android.hardware.tv.input@1.0.so +VNDK-core: android.hardware.usb.gadget@1.0.so +VNDK-core: android.hardware.usb@1.0.so +VNDK-core: android.hardware.usb@1.1.so +VNDK-core: android.hardware.usb@1.2.so +VNDK-core: android.hardware.vibrator@1.0.so +VNDK-core: android.hardware.vibrator@1.1.so +VNDK-core: android.hardware.vibrator@1.2.so +VNDK-core: android.hardware.vibrator@1.3.so +VNDK-core: android.hardware.vr@1.0.so +VNDK-core: android.hardware.weaver@1.0.so +VNDK-core: android.hardware.wifi.hostapd@1.0.so +VNDK-core: android.hardware.wifi.hostapd@1.1.so +VNDK-core: android.hardware.wifi.offload@1.0.so +VNDK-core: android.hardware.wifi.supplicant@1.0.so +VNDK-core: android.hardware.wifi.supplicant@1.1.so +VNDK-core: android.hardware.wifi.supplicant@1.2.so +VNDK-core: android.hardware.wifi@1.0.so +VNDK-core: android.hardware.wifi@1.1.so +VNDK-core: android.hardware.wifi@1.2.so +VNDK-core: android.hardware.wifi@1.3.so +VNDK-core: android.hidl.allocator@1.0.so +VNDK-core: android.hidl.memory.block@1.0.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.system.net.netd@1.0.so +VNDK-core: android.system.net.netd@1.1.so +VNDK-core: android.system.suspend@1.0.so +VNDK-core: android.system.wifi.keystore@1.0.so +VNDK-core: libadf.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcodec2.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libhidlcache.so +VNDK-core: libjpeg.so +VNDK-core: libkeymaster_messages.so +VNDK-core: libkeymaster_portable.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libprotobuf-cpp-full.so +VNDK-core: libprotobuf-cpp-lite.so +VNDK-core: libpuresoftkeymasterdevice.so +VNDK-core: libradio_metadata.so +VNDK-core: libselinux.so +VNDK-core: libsoftkeymasterdevice.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_bufferpool@2.0.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libbinderthreadstate.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so diff --git a/make/target/product/gsi/30.txt b/make/target/product/gsi/30.txt new file mode 100644 index 0000000..0589517 --- /dev/null +++ b/make/target/product/gsi/30.txt @@ -0,0 +1,309 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libbinder_ndk.so +LLNDK: libc.so +LLNDK: libcgrouprc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libselinux.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.common-V1-ndk_platform.so +VNDK-SP: android.hardware.graphics.common-V1-ndk_platform.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.common@1.2.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.graphics.mapper@3.0.so +VNDK-SP: android.hardware.graphics.mapper@4.0.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.safe_union@1.0.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libgralloctypes.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libion.so +VNDK-SP: libjsoncpp.so +VNDK-SP: liblzma.so +VNDK-SP: libprocessgroup.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.frameworks.automotive.display@1.0.so +VNDK-core: android.frameworks.cameraservice.common@2.0.so +VNDK-core: android.frameworks.cameraservice.device@2.0.so +VNDK-core: android.frameworks.cameraservice.service@2.0.so +VNDK-core: android.frameworks.cameraservice.service@2.1.so +VNDK-core: android.frameworks.displayservice@1.0.so +VNDK-core: android.frameworks.schedulerservice@1.0.so +VNDK-core: android.frameworks.sensorservice@1.0.so +VNDK-core: android.frameworks.stats@1.0.so +VNDK-core: android.hardware.atrace@1.0.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.audio.common@4.0.so +VNDK-core: android.hardware.audio.common@5.0.so +VNDK-core: android.hardware.audio.common@6.0.so +VNDK-core: android.hardware.audio.effect@2.0.so +VNDK-core: android.hardware.audio.effect@4.0.so +VNDK-core: android.hardware.audio.effect@5.0.so +VNDK-core: android.hardware.audio.effect@6.0.so +VNDK-core: android.hardware.audio@2.0.so +VNDK-core: android.hardware.audio@4.0.so +VNDK-core: android.hardware.audio@5.0.so +VNDK-core: android.hardware.audio@6.0.so +VNDK-core: android.hardware.authsecret@1.0.so +VNDK-core: android.hardware.automotive.audiocontrol@1.0.so +VNDK-core: android.hardware.automotive.audiocontrol@2.0.so +VNDK-core: android.hardware.automotive.can@1.0.so +VNDK-core: android.hardware.automotive.evs@1.0.so +VNDK-core: android.hardware.automotive.evs@1.1.so +VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so +VNDK-core: android.hardware.automotive.sv@1.0.so +VNDK-core: android.hardware.automotive.vehicle@2.0.so +VNDK-core: android.hardware.biometrics.face@1.0.so +VNDK-core: android.hardware.biometrics.fingerprint@2.1.so +VNDK-core: android.hardware.biometrics.fingerprint@2.2.so +VNDK-core: android.hardware.bluetooth.a2dp@1.0.so +VNDK-core: android.hardware.bluetooth.audio@2.0.so +VNDK-core: android.hardware.bluetooth@1.0.so +VNDK-core: android.hardware.bluetooth@1.1.so +VNDK-core: android.hardware.boot@1.0.so +VNDK-core: android.hardware.boot@1.1.so +VNDK-core: android.hardware.broadcastradio@1.0.so +VNDK-core: android.hardware.broadcastradio@1.1.so +VNDK-core: android.hardware.broadcastradio@2.0.so +VNDK-core: android.hardware.camera.common@1.0.so +VNDK-core: android.hardware.camera.device@1.0.so +VNDK-core: android.hardware.camera.device@3.2.so +VNDK-core: android.hardware.camera.device@3.3.so +VNDK-core: android.hardware.camera.device@3.4.so +VNDK-core: android.hardware.camera.device@3.5.so +VNDK-core: android.hardware.camera.device@3.6.so +VNDK-core: android.hardware.camera.metadata@3.2.so +VNDK-core: android.hardware.camera.metadata@3.3.so +VNDK-core: android.hardware.camera.metadata@3.4.so +VNDK-core: android.hardware.camera.metadata@3.5.so +VNDK-core: android.hardware.camera.provider@2.4.so +VNDK-core: android.hardware.camera.provider@2.5.so +VNDK-core: android.hardware.camera.provider@2.6.so +VNDK-core: android.hardware.cas.native@1.0.so +VNDK-core: android.hardware.cas@1.0.so +VNDK-core: android.hardware.cas@1.1.so +VNDK-core: android.hardware.cas@1.2.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.confirmationui@1.0.so +VNDK-core: android.hardware.contexthub@1.0.so +VNDK-core: android.hardware.contexthub@1.1.so +VNDK-core: android.hardware.drm@1.0.so +VNDK-core: android.hardware.drm@1.1.so +VNDK-core: android.hardware.drm@1.2.so +VNDK-core: android.hardware.drm@1.3.so +VNDK-core: android.hardware.dumpstate@1.0.so +VNDK-core: android.hardware.dumpstate@1.1.so +VNDK-core: android.hardware.fastboot@1.0.so +VNDK-core: android.hardware.gatekeeper@1.0.so +VNDK-core: android.hardware.gnss.measurement_corrections@1.0.so +VNDK-core: android.hardware.gnss.measurement_corrections@1.1.so +VNDK-core: android.hardware.gnss.visibility_control@1.0.so +VNDK-core: android.hardware.gnss@1.0.so +VNDK-core: android.hardware.gnss@1.1.so +VNDK-core: android.hardware.gnss@2.0.so +VNDK-core: android.hardware.gnss@2.1.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.allocator@3.0.so +VNDK-core: android.hardware.graphics.allocator@4.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.bufferqueue@2.0.so +VNDK-core: android.hardware.graphics.composer@2.1.so +VNDK-core: android.hardware.graphics.composer@2.2.so +VNDK-core: android.hardware.graphics.composer@2.3.so +VNDK-core: android.hardware.graphics.composer@2.4.so +VNDK-core: android.hardware.health.storage@1.0.so +VNDK-core: android.hardware.health@1.0.so +VNDK-core: android.hardware.health@2.0.so +VNDK-core: android.hardware.health@2.1.so +VNDK-core: android.hardware.identity-V2-ndk_platform.so +VNDK-core: android.hardware.input.classifier@1.0.so +VNDK-core: android.hardware.input.common@1.0.so +VNDK-core: android.hardware.ir@1.0.so +VNDK-core: android.hardware.keymaster-V2-ndk_platform.so +VNDK-core: android.hardware.keymaster@3.0.so +VNDK-core: android.hardware.keymaster@4.0.so +VNDK-core: android.hardware.keymaster@4.1.so +VNDK-core: android.hardware.light-V1-ndk_platform.so +VNDK-core: android.hardware.light@2.0.so +VNDK-core: android.hardware.media.bufferpool@1.0.so +VNDK-core: android.hardware.media.bufferpool@2.0.so +VNDK-core: android.hardware.media.c2@1.0.so +VNDK-core: android.hardware.media.c2@1.1.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.neuralnetworks@1.0.so +VNDK-core: android.hardware.neuralnetworks@1.1.so +VNDK-core: android.hardware.neuralnetworks@1.2.so +VNDK-core: android.hardware.neuralnetworks@1.3.so +VNDK-core: android.hardware.nfc@1.0.so +VNDK-core: android.hardware.nfc@1.1.so +VNDK-core: android.hardware.nfc@1.2.so +VNDK-core: android.hardware.oemlock@1.0.so +VNDK-core: android.hardware.power-V1-ndk_platform.so +VNDK-core: android.hardware.power.stats@1.0.so +VNDK-core: android.hardware.power@1.0.so +VNDK-core: android.hardware.power@1.1.so +VNDK-core: android.hardware.power@1.2.so +VNDK-core: android.hardware.power@1.3.so +VNDK-core: android.hardware.radio.config@1.0.so +VNDK-core: android.hardware.radio.config@1.1.so +VNDK-core: android.hardware.radio.config@1.2.so +VNDK-core: android.hardware.radio.deprecated@1.0.so +VNDK-core: android.hardware.radio@1.0.so +VNDK-core: android.hardware.radio@1.1.so +VNDK-core: android.hardware.radio@1.2.so +VNDK-core: android.hardware.radio@1.3.so +VNDK-core: android.hardware.radio@1.4.so +VNDK-core: android.hardware.radio@1.5.so +VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so +VNDK-core: android.hardware.secure_element@1.0.so +VNDK-core: android.hardware.secure_element@1.1.so +VNDK-core: android.hardware.secure_element@1.2.so +VNDK-core: android.hardware.sensors@1.0.so +VNDK-core: android.hardware.sensors@2.0.so +VNDK-core: android.hardware.sensors@2.1.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.soundtrigger@2.1.so +VNDK-core: android.hardware.soundtrigger@2.2.so +VNDK-core: android.hardware.soundtrigger@2.3.so +VNDK-core: android.hardware.tetheroffload.config@1.0.so +VNDK-core: android.hardware.tetheroffload.control@1.0.so +VNDK-core: android.hardware.thermal@1.0.so +VNDK-core: android.hardware.thermal@1.1.so +VNDK-core: android.hardware.thermal@2.0.so +VNDK-core: android.hardware.tv.cec@1.0.so +VNDK-core: android.hardware.tv.cec@2.0.so +VNDK-core: android.hardware.tv.input@1.0.so +VNDK-core: android.hardware.tv.tuner@1.0.so +VNDK-core: android.hardware.usb.gadget@1.0.so +VNDK-core: android.hardware.usb.gadget@1.1.so +VNDK-core: android.hardware.usb@1.0.so +VNDK-core: android.hardware.usb@1.1.so +VNDK-core: android.hardware.usb@1.2.so +VNDK-core: android.hardware.vibrator-V1-ndk_platform.so +VNDK-core: android.hardware.vibrator@1.0.so +VNDK-core: android.hardware.vibrator@1.1.so +VNDK-core: android.hardware.vibrator@1.2.so +VNDK-core: android.hardware.vibrator@1.3.so +VNDK-core: android.hardware.vr@1.0.so +VNDK-core: android.hardware.weaver@1.0.so +VNDK-core: android.hardware.wifi.hostapd@1.0.so +VNDK-core: android.hardware.wifi.hostapd@1.1.so +VNDK-core: android.hardware.wifi.hostapd@1.2.so +VNDK-core: android.hardware.wifi.offload@1.0.so +VNDK-core: android.hardware.wifi.supplicant@1.0.so +VNDK-core: android.hardware.wifi.supplicant@1.1.so +VNDK-core: android.hardware.wifi.supplicant@1.2.so +VNDK-core: android.hardware.wifi.supplicant@1.3.so +VNDK-core: android.hardware.wifi@1.0.so +VNDK-core: android.hardware.wifi@1.1.so +VNDK-core: android.hardware.wifi@1.2.so +VNDK-core: android.hardware.wifi@1.3.so +VNDK-core: android.hardware.wifi@1.4.so +VNDK-core: android.hidl.allocator@1.0.so +VNDK-core: android.hidl.memory.block@1.0.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.system.net.netd@1.0.so +VNDK-core: android.system.net.netd@1.1.so +VNDK-core: android.system.suspend@1.0.so +VNDK-core: android.system.wifi.keystore@1.0.so +VNDK-core: libadf.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libbufferqueueconverter.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcodec2.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libjpeg.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libradio_metadata.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_bufferpool@2.0.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so diff --git a/make/target/product/gsi/31.txt b/make/target/product/gsi/31.txt new file mode 100644 index 0000000..971ec92 --- /dev/null +++ b/make/target/product/gsi/31.txt @@ -0,0 +1,223 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libbinder_ndk.so +LLNDK: libc.so +LLNDK: libcgrouprc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libselinux.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.common-V2-ndk_platform.so +VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so +VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.common@1.2.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.graphics.mapper@3.0.so +VNDK-SP: android.hardware.graphics.mapper@4.0.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.safe_union@1.0.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libdmabufheap.so +VNDK-SP: libgralloctypes.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libion.so +VNDK-SP: libjsoncpp.so +VNDK-SP: liblzma.so +VNDK-SP: libprocessgroup.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.authsecret-V1-ndk_platform.so +VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.gnss-V1-ndk_platform.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.allocator@3.0.so +VNDK-core: android.hardware.graphics.allocator@4.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.bufferqueue@2.0.so +VNDK-core: android.hardware.health.storage-V1-ndk_platform.so +VNDK-core: android.hardware.identity-V3-ndk_platform.so +VNDK-core: android.hardware.keymaster-V3-ndk_platform.so +VNDK-core: android.hardware.light-V1-ndk_platform.so +VNDK-core: android.hardware.media.bufferpool@2.0.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack-V1-ndk_platform.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.oemlock-V1-ndk_platform.so +VNDK-core: android.hardware.power-V2-ndk_platform.so +VNDK-core: android.hardware.power.stats-V1-ndk_platform.so +VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so +VNDK-core: android.hardware.security.keymint-V1-ndk_platform.so +VNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so +VNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.vibrator-V2-ndk_platform.so +VNDK-core: android.hardware.weaver-V1-ndk_platform.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.system.keystore2-V1-ndk_platform.so +VNDK-core: android.system.suspend@1.0.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libbufferqueueconverter.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcodec2.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libjpeg.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libradio_metadata.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_bufferpool@2.0.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so +VNDK-product: android.hardware.audio.common@2.0.so +VNDK-product: android.hardware.configstore@1.0.so +VNDK-product: android.hardware.configstore@1.1.so +VNDK-product: android.hardware.graphics.allocator@2.0.so +VNDK-product: android.hardware.graphics.allocator@3.0.so +VNDK-product: android.hardware.graphics.allocator@4.0.so +VNDK-product: android.hardware.graphics.bufferqueue@1.0.so +VNDK-product: android.hardware.graphics.bufferqueue@2.0.so +VNDK-product: android.hardware.graphics.common@1.0.so +VNDK-product: android.hardware.graphics.common@1.1.so +VNDK-product: android.hardware.graphics.common@1.2.so +VNDK-product: android.hardware.graphics.mapper@2.0.so +VNDK-product: android.hardware.graphics.mapper@2.1.so +VNDK-product: android.hardware.graphics.mapper@3.0.so +VNDK-product: android.hardware.graphics.mapper@4.0.so +VNDK-product: android.hardware.media.bufferpool@2.0.so +VNDK-product: android.hardware.media.omx@1.0.so +VNDK-product: android.hardware.media@1.0.so +VNDK-product: android.hardware.memtrack@1.0.so +VNDK-product: android.hardware.renderscript@1.0.so +VNDK-product: android.hardware.soundtrigger@2.0.so +VNDK-product: android.hidl.memory.token@1.0.so +VNDK-product: android.hidl.memory@1.0.so +VNDK-product: android.hidl.safe_union@1.0.so +VNDK-product: android.hidl.token@1.0.so +VNDK-product: android.system.suspend@1.0.so +VNDK-product: libaudioutils.so +VNDK-product: libbacktrace.so +VNDK-product: libbase.so +VNDK-product: libc++.so +VNDK-product: libcamera_metadata.so +VNDK-product: libcap.so +VNDK-product: libcompiler_rt.so +VNDK-product: libcrypto.so +VNDK-product: libcurl.so +VNDK-product: libcutils.so +VNDK-product: libevent.so +VNDK-product: libexpat.so +VNDK-product: libfmq.so +VNDK-product: libhidlbase.so +VNDK-product: libhidlmemory.so +VNDK-product: libion.so +VNDK-product: libjpeg.so +VNDK-product: libjsoncpp.so +VNDK-product: libldacBT_abr.so +VNDK-product: libldacBT_enc.so +VNDK-product: liblz4.so +VNDK-product: liblzma.so +VNDK-product: libminijail.so +VNDK-product: libnl.so +VNDK-product: libpcre2.so +VNDK-product: libpiex.so +VNDK-product: libpng.so +VNDK-product: libprocessgroup.so +VNDK-product: libprocinfo.so +VNDK-product: libspeexresampler.so +VNDK-product: libssl.so +VNDK-product: libtinyalsa.so +VNDK-product: libtinyxml2.so +VNDK-product: libunwindstack.so +VNDK-product: libutils.so +VNDK-product: libutilscallstack.so +VNDK-product: libwifi-system-iface.so +VNDK-product: libxml2.so +VNDK-product: libyuv.so +VNDK-product: libz.so +VNDK-product: libziparchive.so diff --git a/make/target/product/gsi/32.txt b/make/target/product/gsi/32.txt new file mode 100644 index 0000000..971ec92 --- /dev/null +++ b/make/target/product/gsi/32.txt @@ -0,0 +1,223 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libbinder_ndk.so +LLNDK: libc.so +LLNDK: libcgrouprc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libselinux.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.common-V2-ndk_platform.so +VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so +VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.common@1.2.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.graphics.mapper@3.0.so +VNDK-SP: android.hardware.graphics.mapper@4.0.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.safe_union@1.0.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libdmabufheap.so +VNDK-SP: libgralloctypes.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libion.so +VNDK-SP: libjsoncpp.so +VNDK-SP: liblzma.so +VNDK-SP: libprocessgroup.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.authsecret-V1-ndk_platform.so +VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.gnss-V1-ndk_platform.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.allocator@3.0.so +VNDK-core: android.hardware.graphics.allocator@4.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.bufferqueue@2.0.so +VNDK-core: android.hardware.health.storage-V1-ndk_platform.so +VNDK-core: android.hardware.identity-V3-ndk_platform.so +VNDK-core: android.hardware.keymaster-V3-ndk_platform.so +VNDK-core: android.hardware.light-V1-ndk_platform.so +VNDK-core: android.hardware.media.bufferpool@2.0.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack-V1-ndk_platform.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.oemlock-V1-ndk_platform.so +VNDK-core: android.hardware.power-V2-ndk_platform.so +VNDK-core: android.hardware.power.stats-V1-ndk_platform.so +VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so +VNDK-core: android.hardware.security.keymint-V1-ndk_platform.so +VNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so +VNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.vibrator-V2-ndk_platform.so +VNDK-core: android.hardware.weaver-V1-ndk_platform.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.system.keystore2-V1-ndk_platform.so +VNDK-core: android.system.suspend@1.0.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libbufferqueueconverter.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcodec2.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libjpeg.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libradio_metadata.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_bufferpool@2.0.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so +VNDK-product: android.hardware.audio.common@2.0.so +VNDK-product: android.hardware.configstore@1.0.so +VNDK-product: android.hardware.configstore@1.1.so +VNDK-product: android.hardware.graphics.allocator@2.0.so +VNDK-product: android.hardware.graphics.allocator@3.0.so +VNDK-product: android.hardware.graphics.allocator@4.0.so +VNDK-product: android.hardware.graphics.bufferqueue@1.0.so +VNDK-product: android.hardware.graphics.bufferqueue@2.0.so +VNDK-product: android.hardware.graphics.common@1.0.so +VNDK-product: android.hardware.graphics.common@1.1.so +VNDK-product: android.hardware.graphics.common@1.2.so +VNDK-product: android.hardware.graphics.mapper@2.0.so +VNDK-product: android.hardware.graphics.mapper@2.1.so +VNDK-product: android.hardware.graphics.mapper@3.0.so +VNDK-product: android.hardware.graphics.mapper@4.0.so +VNDK-product: android.hardware.media.bufferpool@2.0.so +VNDK-product: android.hardware.media.omx@1.0.so +VNDK-product: android.hardware.media@1.0.so +VNDK-product: android.hardware.memtrack@1.0.so +VNDK-product: android.hardware.renderscript@1.0.so +VNDK-product: android.hardware.soundtrigger@2.0.so +VNDK-product: android.hidl.memory.token@1.0.so +VNDK-product: android.hidl.memory@1.0.so +VNDK-product: android.hidl.safe_union@1.0.so +VNDK-product: android.hidl.token@1.0.so +VNDK-product: android.system.suspend@1.0.so +VNDK-product: libaudioutils.so +VNDK-product: libbacktrace.so +VNDK-product: libbase.so +VNDK-product: libc++.so +VNDK-product: libcamera_metadata.so +VNDK-product: libcap.so +VNDK-product: libcompiler_rt.so +VNDK-product: libcrypto.so +VNDK-product: libcurl.so +VNDK-product: libcutils.so +VNDK-product: libevent.so +VNDK-product: libexpat.so +VNDK-product: libfmq.so +VNDK-product: libhidlbase.so +VNDK-product: libhidlmemory.so +VNDK-product: libion.so +VNDK-product: libjpeg.so +VNDK-product: libjsoncpp.so +VNDK-product: libldacBT_abr.so +VNDK-product: libldacBT_enc.so +VNDK-product: liblz4.so +VNDK-product: liblzma.so +VNDK-product: libminijail.so +VNDK-product: libnl.so +VNDK-product: libpcre2.so +VNDK-product: libpiex.so +VNDK-product: libpng.so +VNDK-product: libprocessgroup.so +VNDK-product: libprocinfo.so +VNDK-product: libspeexresampler.so +VNDK-product: libssl.so +VNDK-product: libtinyalsa.so +VNDK-product: libtinyxml2.so +VNDK-product: libunwindstack.so +VNDK-product: libutils.so +VNDK-product: libutilscallstack.so +VNDK-product: libwifi-system-iface.so +VNDK-product: libxml2.so +VNDK-product: libyuv.so +VNDK-product: libz.so +VNDK-product: libziparchive.so diff --git a/make/target/product/gsi/33.txt b/make/target/product/gsi/33.txt new file mode 100644 index 0000000..03a143d --- /dev/null +++ b/make/target/product/gsi/33.txt @@ -0,0 +1,254 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libbinder_ndk.so +LLNDK: libc.so +LLNDK: libcgrouprc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libselinux.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.common-V2-ndk.so +VNDK-SP: android.hardware.common.fmq-V1-ndk.so +VNDK-SP: android.hardware.graphics.allocator-V1-ndk.so +VNDK-SP: android.hardware.graphics.common-V3-ndk.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.common@1.2.so +VNDK-SP: android.hardware.graphics.composer3-V1-ndk.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.graphics.mapper@3.0.so +VNDK-SP: android.hardware.graphics.mapper@4.0.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.safe_union@1.0.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libdmabufheap.so +VNDK-SP: libgralloctypes.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libion.so +VNDK-SP: libjsoncpp.so +VNDK-SP: liblzma.so +VNDK-SP: libprocessgroup.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.hardware.audio.common-V1-ndk.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.authsecret-V1-ndk.so +VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so +VNDK-core: android.hardware.bluetooth.audio-V2-ndk.so +VNDK-core: android.hardware.camera.common-V1-ndk.so +VNDK-core: android.hardware.camera.device-V1-ndk.so +VNDK-core: android.hardware.camera.metadata-V1-ndk.so +VNDK-core: android.hardware.camera.provider-V1-ndk.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.drm-V1-ndk.so +VNDK-core: android.hardware.dumpstate-V1-ndk.so +VNDK-core: android.hardware.gnss-V2-ndk.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.allocator@3.0.so +VNDK-core: android.hardware.graphics.allocator@4.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.bufferqueue@2.0.so +VNDK-core: android.hardware.health-V1-ndk.so +VNDK-core: android.hardware.health.storage-V1-ndk.so +VNDK-core: android.hardware.identity-V4-ndk.so +VNDK-core: android.hardware.ir-V1-ndk.so +VNDK-core: android.hardware.keymaster-V3-ndk.so +VNDK-core: android.hardware.light-V2-ndk.so +VNDK-core: android.hardware.media.bufferpool@2.0.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack-V1-ndk.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.nfc-V1-ndk.so +VNDK-core: android.hardware.oemlock-V1-ndk.so +VNDK-core: android.hardware.power-V3-ndk.so +VNDK-core: android.hardware.power.stats-V1-ndk.so +VNDK-core: android.hardware.radio-V1-ndk.so +VNDK-core: android.hardware.radio.config-V1-ndk.so +VNDK-core: android.hardware.radio.data-V1-ndk.so +VNDK-core: android.hardware.radio.messaging-V1-ndk.so +VNDK-core: android.hardware.radio.modem-V1-ndk.so +VNDK-core: android.hardware.radio.network-V1-ndk.so +VNDK-core: android.hardware.radio.sim-V1-ndk.so +VNDK-core: android.hardware.radio.voice-V1-ndk.so +VNDK-core: android.hardware.rebootescrow-V1-ndk.so +VNDK-core: android.hardware.security.dice-V1-ndk.so +VNDK-core: android.hardware.security.keymint-V2-ndk.so +VNDK-core: android.hardware.security.secureclock-V1-ndk.so +VNDK-core: android.hardware.security.sharedsecret-V1-ndk.so +VNDK-core: android.hardware.sensors-V1-ndk.so +VNDK-core: android.hardware.soundtrigger3-V1-ndk.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.usb-V1-ndk.so +VNDK-core: android.hardware.uwb-V1-ndk.so +VNDK-core: android.hardware.vibrator-V2-ndk.so +VNDK-core: android.hardware.weaver-V1-ndk.so +VNDK-core: android.hardware.wifi.hostapd-V1-ndk.so +VNDK-core: android.hardware.wifi.supplicant-V1-ndk.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.media.audio.common.types-V1-ndk.so +VNDK-core: android.media.soundtrigger.types-V1-ndk.so +VNDK-core: android.system.keystore2-V2-ndk.so +VNDK-core: android.system.suspend-V1-ndk.so +VNDK-core: android.system.suspend@1.0.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libbufferqueueconverter.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcodec2.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libjpeg.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libradio_metadata.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_bufferpool@2.0.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so +VNDK-product: android.hardware.audio.common@2.0.so +VNDK-product: android.hardware.configstore@1.0.so +VNDK-product: android.hardware.configstore@1.1.so +VNDK-product: android.hardware.graphics.allocator@2.0.so +VNDK-product: android.hardware.graphics.allocator@3.0.so +VNDK-product: android.hardware.graphics.allocator@4.0.so +VNDK-product: android.hardware.graphics.bufferqueue@1.0.so +VNDK-product: android.hardware.graphics.bufferqueue@2.0.so +VNDK-product: android.hardware.graphics.common@1.0.so +VNDK-product: android.hardware.graphics.common@1.1.so +VNDK-product: android.hardware.graphics.common@1.2.so +VNDK-product: android.hardware.graphics.mapper@2.0.so +VNDK-product: android.hardware.graphics.mapper@2.1.so +VNDK-product: android.hardware.graphics.mapper@3.0.so +VNDK-product: android.hardware.graphics.mapper@4.0.so +VNDK-product: android.hardware.media.bufferpool@2.0.so +VNDK-product: android.hardware.media.omx@1.0.so +VNDK-product: android.hardware.media@1.0.so +VNDK-product: android.hardware.memtrack@1.0.so +VNDK-product: android.hardware.renderscript@1.0.so +VNDK-product: android.hardware.soundtrigger@2.0.so +VNDK-product: android.hidl.memory.token@1.0.so +VNDK-product: android.hidl.memory@1.0.so +VNDK-product: android.hidl.safe_union@1.0.so +VNDK-product: android.hidl.token@1.0.so +VNDK-product: android.system.suspend@1.0.so +VNDK-product: libaudioutils.so +VNDK-product: libbacktrace.so +VNDK-product: libbase.so +VNDK-product: libc++.so +VNDK-product: libcamera_metadata.so +VNDK-product: libcap.so +VNDK-product: libcompiler_rt.so +VNDK-product: libcrypto.so +VNDK-product: libcurl.so +VNDK-product: libcutils.so +VNDK-product: libevent.so +VNDK-product: libexpat.so +VNDK-product: libfmq.so +VNDK-product: libhidlbase.so +VNDK-product: libhidlmemory.so +VNDK-product: libion.so +VNDK-product: libjpeg.so +VNDK-product: libjsoncpp.so +VNDK-product: libldacBT_abr.so +VNDK-product: libldacBT_enc.so +VNDK-product: liblz4.so +VNDK-product: liblzma.so +VNDK-product: libminijail.so +VNDK-product: libnl.so +VNDK-product: libpcre2.so +VNDK-product: libpiex.so +VNDK-product: libpng.so +VNDK-product: libprocessgroup.so +VNDK-product: libprocinfo.so +VNDK-product: libspeexresampler.so +VNDK-product: libssl.so +VNDK-product: libtinyalsa.so +VNDK-product: libtinyxml2.so +VNDK-product: libunwindstack.so +VNDK-product: libutils.so +VNDK-product: libutilscallstack.so +VNDK-product: libwifi-system-iface.so +VNDK-product: libxml2.so +VNDK-product: libyuv.so +VNDK-product: libz.so +VNDK-product: libziparchive.so diff --git a/make/target/product/gsi/Android.bp b/make/target/product/gsi/Android.bp new file mode 100644 index 0000000..a8af9c4 --- /dev/null +++ b/make/target/product/gsi/Android.bp @@ -0,0 +1,25 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "vndk_lib_lists", + srcs: [ + "*.txt", + ], +} diff --git a/make/target/product/gsi/Android.mk b/make/target/product/gsi/Android.mk new file mode 100644 index 0000000..85e551d --- /dev/null +++ b/make/target/product/gsi/Android.mk @@ -0,0 +1,262 @@ +LOCAL_PATH:= $(call my-dir) + +##################################################################### +# list of vndk libraries from the source code. +INTERNAL_VNDK_LIB_LIST := $(SOONG_VNDK_LIBRARIES_FILE) + +##################################################################### +# This is the up-to-date list of vndk libs. +# TODO(b/62012285): the lib list should be stored somewhere under +# /prebuilts/vndk +ifeq (REL,$(PLATFORM_VERSION_CODENAME)) +LATEST_VNDK_LIB_LIST := $(LOCAL_PATH)/$(PLATFORM_VNDK_VERSION).txt +ifeq ($(wildcard $(LATEST_VNDK_LIB_LIST)),) +$(error $(LATEST_VNDK_LIB_LIST) file not found. Please copy "$(LOCAL_PATH)/current.txt" to "$(LATEST_VNDK_LIB_LIST)" and commit a CL for release branch) +endif +else +LATEST_VNDK_LIB_LIST := $(LOCAL_PATH)/current.txt +endif + +##################################################################### +# Check the generate list against the latest list stored in the +# source tree +.PHONY: check-vndk-list + +# Check if vndk list is changed +droidcore: check-vndk-list + +check-vndk-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-list-timestamp +check-vndk-abi-dump-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-abi-dump-list-timestamp + +ifeq ($(TARGET_IS_64_BIT)|$(TARGET_2ND_ARCH),true|) +# TODO(b/110429754) remove this condition when we support 64-bit-only device +check-vndk-list: ; +else ifeq ($(TARGET_SKIP_CURRENT_VNDK),true) +check-vndk-list: ; +else ifeq ($(BOARD_VNDK_VERSION),) +# b/143233626 do not check vndk-list when vndk libs are not built +check-vndk-list: ; +else +check-vndk-list: $(check-vndk-list-timestamp) +ifneq ($(SKIP_ABI_CHECKS),true) +check-vndk-list: $(check-vndk-abi-dump-list-timestamp) +endif +endif + +_vndk_check_failure_message := " error: VNDK library list has been changed.\n" +ifeq (REL,$(PLATFORM_VERSION_CODENAME)) +_vndk_check_failure_message += " Changing the VNDK library list is not allowed in API locked branches." +else +_vndk_check_failure_message += " Run \`update-vndk-list.sh\` to update $(LATEST_VNDK_LIB_LIST)" +endif + +# The *-ndk_platform.so libraries no longer exist and are removed from the VNDK set. However, they +# can exist if NEED_AIDL_NDK_PLATFORM_BACKEND is set to true for legacy devices. Don't be bothered +# with the extraneous libraries. +ifeq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),true) + _READ_INTERNAL_VNDK_LIB_LIST := sed /ndk_platform.so/d $(INTERNAL_VNDK_LIB_LIST) +else + _READ_INTERNAL_VNDK_LIB_LIST := cat $(INTERNAL_VNDK_LIB_LIST) +endif + +$(check-vndk-list-timestamp): $(INTERNAL_VNDK_LIB_LIST) $(LATEST_VNDK_LIB_LIST) $(HOST_OUT_EXECUTABLES)/update-vndk-list.sh + $(hide) ($(_READ_INTERNAL_VNDK_LIB_LIST) | sort | \ + diff --old-line-format="Removed %L" \ + --new-line-format="Added %L" \ + --unchanged-line-format="" \ + <(cat $(LATEST_VNDK_LIB_LIST) | sort) - \ + || ( echo -e $(_vndk_check_failure_message); exit 1 )) + $(hide) mkdir -p $(dir $@) + $(hide) touch $@ + +##################################################################### +# Script to update the latest VNDK lib list +include $(CLEAR_VARS) +LOCAL_MODULE := update-vndk-list.sh +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_STEM := $(LOCAL_MODULE) +LOCAL_IS_HOST_MODULE := true +include $(BUILD_SYSTEM)/base_rules.mk +$(LOCAL_BUILT_MODULE): PRIVATE_INTERNAL_VNDK_LIB_LIST := $(INTERNAL_VNDK_LIB_LIST) +$(LOCAL_BUILT_MODULE): PRIVATE_LATEST_VNDK_LIB_LIST := $(LATEST_VNDK_LIB_LIST) +$(LOCAL_BUILT_MODULE): + @echo "Generate: $@" + @mkdir -p $(dir $@) + @rm -f $@ + $(hide) echo "#!/bin/bash" > $@ +ifeq (REL,$(PLATFORM_VERSION_CODENAME)) + $(hide) echo "echo Updating VNDK library list is NOT allowed in API locked branches." >> $@; \ + echo "exit 1" >> $@ +else + $(hide) echo "if [ -z \"\$${ANDROID_BUILD_TOP}\" ]; then" >> $@; \ + echo " echo Run lunch or choosecombo first" >> $@; \ + echo " exit 1" >> $@; \ + echo "fi" >> $@; \ + echo "cd \$${ANDROID_BUILD_TOP}" >> $@ +ifeq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),true) + $(hide) echo "sed /ndk_platform.so/d $(PRIVATE_INTERNAL_VNDK_LIB_LIST) > $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@ +else + $(hide) echo "cp $(PRIVATE_INTERNAL_VNDK_LIB_LIST) $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@ +endif + $(hide) echo "echo $(PRIVATE_LATEST_VNDK_LIB_LIST) updated." >> $@ +endif + @chmod a+x $@ + +##################################################################### +# Check that all ABI reference dumps have corresponding +# NDK/VNDK/PLATFORM libraries. + +# $(1): The directory containing ABI dumps. +# Return a list of ABI dump paths ending with .so.lsdump. +define find-abi-dump-paths +$(if $(wildcard $(1)), \ + $(addprefix $(1)/, \ + $(call find-files-in-subdirs,$(1),"*.so.lsdump" -and -type f,.))) +endef + +# $(1): A list of tags. +# $(2): A list of tag:path. +# Return the file names of the ABI dumps that match the tags. +define filter-abi-dump-paths +$(eval tag_patterns := $(foreach tag,$(1),$(tag):%)) +$(notdir $(patsubst $(tag_patterns),%,$(filter $(tag_patterns),$(2)))) +endef + +VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(PLATFORM_VNDK_VERSION) +NDK_ABI_DUMP_DIR := prebuilts/abi-dumps/ndk/$(PLATFORM_VNDK_VERSION) +PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/$(PLATFORM_VNDK_VERSION) +VNDK_ABI_DUMPS := $(call find-abi-dump-paths,$(VNDK_ABI_DUMP_DIR)) +NDK_ABI_DUMPS := $(call find-abi-dump-paths,$(NDK_ABI_DUMP_DIR)) +PLATFORM_ABI_DUMPS := $(call find-abi-dump-paths,$(PLATFORM_ABI_DUMP_DIR)) + +# Check for superfluous lsdump files. Since LSDUMP_PATHS only covers the +# libraries that can be built from source in the current build, and prebuilts of +# Mainline modules may be in use, we also allow the libs in STUB_LIBRARIES for +# NDK and platform ABIs. + +$(check-vndk-abi-dump-list-timestamp): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS) +$(check-vndk-abi-dump-list-timestamp): PRIVATE_STUB_LIBRARIES := $(STUB_LIBRARIES) +$(check-vndk-abi-dump-list-timestamp): + $(eval added_vndk_abi_dumps := $(strip $(sort $(filter-out \ + $(call filter-abi-dump-paths,LLNDK VNDK-SP VNDK-core,$(PRIVATE_LSDUMP_PATHS)), \ + $(notdir $(VNDK_ABI_DUMPS)))))) + $(if $(added_vndk_abi_dumps), \ + echo -e "Found unexpected ABI reference dump files under $(VNDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(VNDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_vndk_abi_dumps)) ')' -delete\` to delete the dump files.") + + $(eval added_ndk_abi_dumps := $(strip $(sort $(filter-out \ + $(call filter-abi-dump-paths,NDK,$(PRIVATE_LSDUMP_PATHS)) \ + $(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)), \ + $(notdir $(NDK_ABI_DUMPS)))))) + $(if $(added_ndk_abi_dumps), \ + echo -e "Found unexpected ABI reference dump files under $(NDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(NDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_ndk_abi_dumps)) ')' -delete\` to delete the dump files.") + + $(eval added_platform_abi_dumps := $(strip $(sort $(filter-out \ + $(call filter-abi-dump-paths,PLATFORM,$(PRIVATE_LSDUMP_PATHS)) \ + $(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)), \ + $(notdir $(PLATFORM_ABI_DUMPS)))))) + $(if $(added_platform_abi_dumps), \ + echo -e "Found unexpected ABI reference dump files under $(PLATFORM_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(PLATFORM_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_platform_abi_dumps)) ')' -delete\` to delete the dump files.") + + $(if $(added_vndk_abi_dumps)$(added_ndk_abi_dumps)$(added_platform_abi_dumps),exit 1) + $(hide) mkdir -p $(dir $@) + $(hide) touch $@ + +##################################################################### +# VNDK package and snapshot. + +ifneq ($(BOARD_VNDK_VERSION),) + +include $(CLEAR_VARS) +LOCAL_MODULE := vndk_package +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +# Filter LLNDK libs moved to APEX to avoid pulling them into /system/LIB +LOCAL_REQUIRED_MODULES := \ + $(filter-out $(LLNDK_MOVED_TO_APEX_LIBRARIES),$(LLNDK_LIBRARIES)) + +ifneq ($(TARGET_SKIP_CURRENT_VNDK),true) +LOCAL_REQUIRED_MODULES += \ + vndkcorevariant.libraries.txt \ + $(addsuffix .vendor,$(VNDK_CORE_LIBRARIES)) \ + $(addsuffix .vendor,$(VNDK_SAMEPROCESS_LIBRARIES)) \ + $(VNDK_USING_CORE_VARIANT_LIBRARIES) \ + com.android.vndk.current +endif +include $(BUILD_PHONY_PACKAGE) + +include $(CLEAR_VARS) +_vndk_versions := +ifeq ($(filter com.android.vndk.current.on_vendor, $(PRODUCT_PACKAGES)),) + _vndk_versions += $(PRODUCT_EXTRA_VNDK_VERSIONS) +endif +ifneq ($(BOARD_VNDK_VERSION),current) + _vndk_versions += $(BOARD_VNDK_VERSION) +endif +LOCAL_MODULE := vndk_apex_snapshot_package +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_REQUIRED_MODULES := $(foreach vndk_ver,$(_vndk_versions),com.android.vndk.v$(vndk_ver)) +include $(BUILD_PHONY_PACKAGE) + +_vndk_versions := + +endif # BOARD_VNDK_VERSION is set + +##################################################################### +# skip_mount.cfg, read by init to skip mounting some partitions when GSI is used. + +include $(CLEAR_VARS) +LOCAL_MODULE := gsi_skip_mount.cfg +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_MODULE_STEM := skip_mount.cfg +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_SYSTEM_EXT_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := init/config + +# Adds a symlink under /system/etc/init/config pointing to /system/system_ext/etc/init/config +# because first-stage init in Android 10.0 will read the skip_mount.cfg from /system/etc/* after +# chroot /system. +# TODO: remove this symlink when no need to support new GSI on Android 10. +# The actual file needs to be under /system/system_ext because it's GSI-specific and does not +# belong to core CSI. +LOCAL_POST_INSTALL_CMD := \ + mkdir -p $(TARGET_OUT)/etc/init; \ + ln -sf /system/system_ext/etc/init/config $(TARGET_OUT)/etc/init/config + +include $(BUILD_PREBUILT) + +##################################################################### +# init.gsi.rc, GSI-specific init script. + +include $(CLEAR_VARS) +LOCAL_MODULE := init.gsi.rc +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_SYSTEM_EXT_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := init + +include $(BUILD_PREBUILT) + + +include $(CLEAR_VARS) +LOCAL_MODULE := init.vndk-nodef.rc +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_SYSTEM_EXT_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := gsi + +include $(BUILD_PREBUILT) diff --git a/make/target/product/gsi/OWNERS b/make/target/product/gsi/OWNERS new file mode 100644 index 0000000..39f97de --- /dev/null +++ b/make/target/product/gsi/OWNERS @@ -0,0 +1,6 @@ +bowgotsai@google.com +jiyong@google.com +justinyun@google.com +smoreland@google.com +szuweilin@google.com +yochiang@google.com diff --git a/make/target/product/gsi/current.txt b/make/target/product/gsi/current.txt new file mode 100644 index 0000000..03a143d --- /dev/null +++ b/make/target/product/gsi/current.txt @@ -0,0 +1,254 @@ +LLNDK: libEGL.so +LLNDK: libGLESv1_CM.so +LLNDK: libGLESv2.so +LLNDK: libGLESv3.so +LLNDK: libRS.so +LLNDK: libandroid_net.so +LLNDK: libbinder_ndk.so +LLNDK: libc.so +LLNDK: libcgrouprc.so +LLNDK: libdl.so +LLNDK: libft2.so +LLNDK: liblog.so +LLNDK: libm.so +LLNDK: libmediandk.so +LLNDK: libnativewindow.so +LLNDK: libneuralnetworks.so +LLNDK: libselinux.so +LLNDK: libsync.so +LLNDK: libvndksupport.so +LLNDK: libvulkan.so +VNDK-SP: android.hardware.common-V2-ndk.so +VNDK-SP: android.hardware.common.fmq-V1-ndk.so +VNDK-SP: android.hardware.graphics.allocator-V1-ndk.so +VNDK-SP: android.hardware.graphics.common-V3-ndk.so +VNDK-SP: android.hardware.graphics.common@1.0.so +VNDK-SP: android.hardware.graphics.common@1.1.so +VNDK-SP: android.hardware.graphics.common@1.2.so +VNDK-SP: android.hardware.graphics.composer3-V1-ndk.so +VNDK-SP: android.hardware.graphics.mapper@2.0.so +VNDK-SP: android.hardware.graphics.mapper@2.1.so +VNDK-SP: android.hardware.graphics.mapper@3.0.so +VNDK-SP: android.hardware.graphics.mapper@4.0.so +VNDK-SP: android.hardware.renderscript@1.0.so +VNDK-SP: android.hidl.memory.token@1.0.so +VNDK-SP: android.hidl.memory@1.0-impl.so +VNDK-SP: android.hidl.memory@1.0.so +VNDK-SP: android.hidl.safe_union@1.0.so +VNDK-SP: libRSCpuRef.so +VNDK-SP: libRSDriver.so +VNDK-SP: libRS_internal.so +VNDK-SP: libbacktrace.so +VNDK-SP: libbase.so +VNDK-SP: libbcinfo.so +VNDK-SP: libblas.so +VNDK-SP: libc++.so +VNDK-SP: libcompiler_rt.so +VNDK-SP: libcutils.so +VNDK-SP: libdmabufheap.so +VNDK-SP: libgralloctypes.so +VNDK-SP: libhardware.so +VNDK-SP: libhidlbase.so +VNDK-SP: libhidlmemory.so +VNDK-SP: libion.so +VNDK-SP: libjsoncpp.so +VNDK-SP: liblzma.so +VNDK-SP: libprocessgroup.so +VNDK-SP: libunwindstack.so +VNDK-SP: libutils.so +VNDK-SP: libutilscallstack.so +VNDK-SP: libz.so +VNDK-core: android.hardware.audio.common-V1-ndk.so +VNDK-core: android.hardware.audio.common@2.0.so +VNDK-core: android.hardware.authsecret-V1-ndk.so +VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so +VNDK-core: android.hardware.bluetooth.audio-V2-ndk.so +VNDK-core: android.hardware.camera.common-V1-ndk.so +VNDK-core: android.hardware.camera.device-V1-ndk.so +VNDK-core: android.hardware.camera.metadata-V1-ndk.so +VNDK-core: android.hardware.camera.provider-V1-ndk.so +VNDK-core: android.hardware.configstore-utils.so +VNDK-core: android.hardware.configstore@1.0.so +VNDK-core: android.hardware.configstore@1.1.so +VNDK-core: android.hardware.confirmationui-support-lib.so +VNDK-core: android.hardware.drm-V1-ndk.so +VNDK-core: android.hardware.dumpstate-V1-ndk.so +VNDK-core: android.hardware.gnss-V2-ndk.so +VNDK-core: android.hardware.graphics.allocator@2.0.so +VNDK-core: android.hardware.graphics.allocator@3.0.so +VNDK-core: android.hardware.graphics.allocator@4.0.so +VNDK-core: android.hardware.graphics.bufferqueue@1.0.so +VNDK-core: android.hardware.graphics.bufferqueue@2.0.so +VNDK-core: android.hardware.health-V1-ndk.so +VNDK-core: android.hardware.health.storage-V1-ndk.so +VNDK-core: android.hardware.identity-V4-ndk.so +VNDK-core: android.hardware.ir-V1-ndk.so +VNDK-core: android.hardware.keymaster-V3-ndk.so +VNDK-core: android.hardware.light-V2-ndk.so +VNDK-core: android.hardware.media.bufferpool@2.0.so +VNDK-core: android.hardware.media.omx@1.0.so +VNDK-core: android.hardware.media@1.0.so +VNDK-core: android.hardware.memtrack-V1-ndk.so +VNDK-core: android.hardware.memtrack@1.0.so +VNDK-core: android.hardware.nfc-V1-ndk.so +VNDK-core: android.hardware.oemlock-V1-ndk.so +VNDK-core: android.hardware.power-V3-ndk.so +VNDK-core: android.hardware.power.stats-V1-ndk.so +VNDK-core: android.hardware.radio-V1-ndk.so +VNDK-core: android.hardware.radio.config-V1-ndk.so +VNDK-core: android.hardware.radio.data-V1-ndk.so +VNDK-core: android.hardware.radio.messaging-V1-ndk.so +VNDK-core: android.hardware.radio.modem-V1-ndk.so +VNDK-core: android.hardware.radio.network-V1-ndk.so +VNDK-core: android.hardware.radio.sim-V1-ndk.so +VNDK-core: android.hardware.radio.voice-V1-ndk.so +VNDK-core: android.hardware.rebootescrow-V1-ndk.so +VNDK-core: android.hardware.security.dice-V1-ndk.so +VNDK-core: android.hardware.security.keymint-V2-ndk.so +VNDK-core: android.hardware.security.secureclock-V1-ndk.so +VNDK-core: android.hardware.security.sharedsecret-V1-ndk.so +VNDK-core: android.hardware.sensors-V1-ndk.so +VNDK-core: android.hardware.soundtrigger3-V1-ndk.so +VNDK-core: android.hardware.soundtrigger@2.0-core.so +VNDK-core: android.hardware.soundtrigger@2.0.so +VNDK-core: android.hardware.usb-V1-ndk.so +VNDK-core: android.hardware.uwb-V1-ndk.so +VNDK-core: android.hardware.vibrator-V2-ndk.so +VNDK-core: android.hardware.weaver-V1-ndk.so +VNDK-core: android.hardware.wifi.hostapd-V1-ndk.so +VNDK-core: android.hardware.wifi.supplicant-V1-ndk.so +VNDK-core: android.hidl.token@1.0-utils.so +VNDK-core: android.hidl.token@1.0.so +VNDK-core: android.media.audio.common.types-V1-ndk.so +VNDK-core: android.media.soundtrigger.types-V1-ndk.so +VNDK-core: android.system.keystore2-V2-ndk.so +VNDK-core: android.system.suspend-V1-ndk.so +VNDK-core: android.system.suspend@1.0.so +VNDK-core: libaudioroute.so +VNDK-core: libaudioutils.so +VNDK-core: libbinder.so +VNDK-core: libbufferqueueconverter.so +VNDK-core: libcamera_metadata.so +VNDK-core: libcap.so +VNDK-core: libcn-cbor.so +VNDK-core: libcodec2.so +VNDK-core: libcrypto.so +VNDK-core: libcrypto_utils.so +VNDK-core: libcurl.so +VNDK-core: libdiskconfig.so +VNDK-core: libdumpstateutil.so +VNDK-core: libevent.so +VNDK-core: libexif.so +VNDK-core: libexpat.so +VNDK-core: libfmq.so +VNDK-core: libgatekeeper.so +VNDK-core: libgui.so +VNDK-core: libhardware_legacy.so +VNDK-core: libhidlallocatorutils.so +VNDK-core: libjpeg.so +VNDK-core: libldacBT_abr.so +VNDK-core: libldacBT_enc.so +VNDK-core: liblz4.so +VNDK-core: libmedia_helper.so +VNDK-core: libmedia_omx.so +VNDK-core: libmemtrack.so +VNDK-core: libminijail.so +VNDK-core: libmkbootimg_abi_check.so +VNDK-core: libnetutils.so +VNDK-core: libnl.so +VNDK-core: libpcre2.so +VNDK-core: libpiex.so +VNDK-core: libpng.so +VNDK-core: libpower.so +VNDK-core: libprocinfo.so +VNDK-core: libradio_metadata.so +VNDK-core: libspeexresampler.so +VNDK-core: libsqlite.so +VNDK-core: libssl.so +VNDK-core: libstagefright_bufferpool@2.0.so +VNDK-core: libstagefright_bufferqueue_helper.so +VNDK-core: libstagefright_foundation.so +VNDK-core: libstagefright_omx.so +VNDK-core: libstagefright_omx_utils.so +VNDK-core: libstagefright_xmlparser.so +VNDK-core: libsysutils.so +VNDK-core: libtinyalsa.so +VNDK-core: libtinyxml2.so +VNDK-core: libui.so +VNDK-core: libusbhost.so +VNDK-core: libwifi-system-iface.so +VNDK-core: libxml2.so +VNDK-core: libyuv.so +VNDK-core: libziparchive.so +VNDK-private: libbacktrace.so +VNDK-private: libblas.so +VNDK-private: libcompiler_rt.so +VNDK-private: libft2.so +VNDK-private: libgui.so +VNDK-product: android.hardware.audio.common@2.0.so +VNDK-product: android.hardware.configstore@1.0.so +VNDK-product: android.hardware.configstore@1.1.so +VNDK-product: android.hardware.graphics.allocator@2.0.so +VNDK-product: android.hardware.graphics.allocator@3.0.so +VNDK-product: android.hardware.graphics.allocator@4.0.so +VNDK-product: android.hardware.graphics.bufferqueue@1.0.so +VNDK-product: android.hardware.graphics.bufferqueue@2.0.so +VNDK-product: android.hardware.graphics.common@1.0.so +VNDK-product: android.hardware.graphics.common@1.1.so +VNDK-product: android.hardware.graphics.common@1.2.so +VNDK-product: android.hardware.graphics.mapper@2.0.so +VNDK-product: android.hardware.graphics.mapper@2.1.so +VNDK-product: android.hardware.graphics.mapper@3.0.so +VNDK-product: android.hardware.graphics.mapper@4.0.so +VNDK-product: android.hardware.media.bufferpool@2.0.so +VNDK-product: android.hardware.media.omx@1.0.so +VNDK-product: android.hardware.media@1.0.so +VNDK-product: android.hardware.memtrack@1.0.so +VNDK-product: android.hardware.renderscript@1.0.so +VNDK-product: android.hardware.soundtrigger@2.0.so +VNDK-product: android.hidl.memory.token@1.0.so +VNDK-product: android.hidl.memory@1.0.so +VNDK-product: android.hidl.safe_union@1.0.so +VNDK-product: android.hidl.token@1.0.so +VNDK-product: android.system.suspend@1.0.so +VNDK-product: libaudioutils.so +VNDK-product: libbacktrace.so +VNDK-product: libbase.so +VNDK-product: libc++.so +VNDK-product: libcamera_metadata.so +VNDK-product: libcap.so +VNDK-product: libcompiler_rt.so +VNDK-product: libcrypto.so +VNDK-product: libcurl.so +VNDK-product: libcutils.so +VNDK-product: libevent.so +VNDK-product: libexpat.so +VNDK-product: libfmq.so +VNDK-product: libhidlbase.so +VNDK-product: libhidlmemory.so +VNDK-product: libion.so +VNDK-product: libjpeg.so +VNDK-product: libjsoncpp.so +VNDK-product: libldacBT_abr.so +VNDK-product: libldacBT_enc.so +VNDK-product: liblz4.so +VNDK-product: liblzma.so +VNDK-product: libminijail.so +VNDK-product: libnl.so +VNDK-product: libpcre2.so +VNDK-product: libpiex.so +VNDK-product: libpng.so +VNDK-product: libprocessgroup.so +VNDK-product: libprocinfo.so +VNDK-product: libspeexresampler.so +VNDK-product: libssl.so +VNDK-product: libtinyalsa.so +VNDK-product: libtinyxml2.so +VNDK-product: libunwindstack.so +VNDK-product: libutils.so +VNDK-product: libutilscallstack.so +VNDK-product: libwifi-system-iface.so +VNDK-product: libxml2.so +VNDK-product: libyuv.so +VNDK-product: libz.so +VNDK-product: libziparchive.so diff --git a/make/target/product/gsi/gsi_skip_mount.cfg b/make/target/product/gsi/gsi_skip_mount.cfg new file mode 100644 index 0000000..28f4349 --- /dev/null +++ b/make/target/product/gsi/gsi_skip_mount.cfg @@ -0,0 +1,9 @@ +# Skip "system" mountpoints. +/oem +/product +/system_ext +# Skip sub-mountpoints of system mountpoints. +/oem/* +/product/* +/system_ext/* +/system/* diff --git a/make/target/product/gsi/init.gsi.rc b/make/target/product/gsi/init.gsi.rc new file mode 100644 index 0000000..69c8e46 --- /dev/null +++ b/make/target/product/gsi/init.gsi.rc @@ -0,0 +1,5 @@ +# +# Android init script for GSI required initialization +# + +import /system/system_ext/etc/gsi/init.vndk-${ro.vndk.version:-nodef}.rc diff --git a/make/target/product/gsi/init.vndk-nodef.rc b/make/target/product/gsi/init.vndk-nodef.rc new file mode 100644 index 0000000..1b141a0 --- /dev/null +++ b/make/target/product/gsi/init.vndk-nodef.rc @@ -0,0 +1,3 @@ +on early-init + # Reboot if BOARD_VNDK_VERSION is not defined + exec - root -- /system/bin/reboot bootloader diff --git a/make/target/product/gsi/testkey_rsa2048.pem b/make/target/product/gsi/testkey_rsa2048.pem new file mode 100644 index 0000000..64de31c --- /dev/null +++ b/make/target/product/gsi/testkey_rsa2048.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA3fDgwU4JKVRHhAfofi/g8daTNplB2mTJCX9fIMy9FnZDXNij +1zijRQ8HKbt3bAGImQvb3GxSV4M5eIdiLDUF7RsUpE7K+s939i/AaTtcuyqimQbJ +QjP9emTsgngHzuKWMg1mwlRZYDfdv62zIQmZcbM9a0CZE36hAYvEBiDB8qT4ob++ +godGAx3rpF2Wi7mhIYDINvkCw8/16Qi9CZgvOUrEolt3mz8Sps41z9j7YAsPbAa8 +fg7dUu61s6NkZEykl4G67loOaf7h+SyP//LpFZ0gV+STZ+EMGofL0SXb8A+hdIYE +QxsnKUYo8e+GaQg92FLxVZqcfyG3AZuMB04R1QIDAQABAoIBAQDGj3/1UaSepjlJ +ZW3an2lH1Cpm2ZxyEGNQLPVluead1vaTdXq3zYM9AKHu8zp3lbOpAVQVk4/jnZJo +Q+9QD6waonTIP3oYBE+WIMirHSHsjctkzw52PV9VBkAWxd5ueIfZheXejGpdy/2H +RJcTQqxWbf7QGr4ZE9xmLq4UsW/zbXwy8qGEp9eMQIIaWBua43FkqmWYLSnVFVJI +Gl8mfVJctLNSZHhS3tKiV8up6NxZlDjO8o7kYVFCkv0xJ9yzQNBc3P2MEmvfZ06D +QnimHBqSxr0M9X6hqP43CnqtCbpsHS8A12Dm4l6fkXfkrAY0UNrEaCSDb8aN7TEc +7bc1MB4NAoGBAPK7xSuvQE9CH05Iy+G6mEQTtNmpfcQosqhi6dF60h4bqlkeGzUu +gF/PKHwwffHAxQSv4V831P3A/IoJFa9IFkg218mYPNfzpj4vJA4aNCDp+SYZAIYm +h6hMOmuByI97wds2yCBGt4mP0eow5B3A1b3UQeqW6LVSuobZ22QVlSk/AoGBAOoS +L82yda9hUa7vuXtqTraf9EGjSXhyjoPqWxa+a1ooI9l24f7mokS5Iof+a/SLfPUj +pwj8eOeOZksjAaWJIdrRb3TaYLaqhDkWQeV5N5XxYbn3+TvVJQyR+OSBfGoEpVP/ +IS6fusvpT3eULJDax10By+gDcoLT5M1FNs4rBIvrAoGBAM8yJP5DHDwLjzl9vjsy +0iLaR3e8zBQTQV2nATvFAXKd3u0vW74rsX0XEdHgesFP8V0s3M4wlGj+wRL66j2y +5QJDfjMg9l7IJlHSX46CI5ks33X7xYy9evLYDs4R/Kct1q5OtsmGU8jisSadETus +jUb61kFvC7krovjVIgbuvWJ1AoGAVikzp4gVgeVU6AwePqu3JcpjYvX0SX4Br9VI +imq1oY49BAOa1PWYratoZp7kpjPiX2osRkaJStNEHExagtCjwaRuXpk0GIlT+p+S +yiGAsJUV4BrDh57B8IqbD6IKZgwnv2+ei0cIv562PdIxRXEDCd1rbZA3SqktA9KC +hgmXttkCgYBPU1lqRpwoHP9lpOBTDa6/Xi6WaDEWrG/tUF/wMlvrZ4hEVMDJRs1d +9JCXBxL/O4TMvpmyVKBZW15iZOcLM3EpiZ00UD+ChcAaFstup+oYKrs8gL9hgyTd +cvWMxGQm13KwSj2CLzEQpPAN5xG14njXaee5ksshxkzBz9z3MVWiiw== +-----END RSA PRIVATE KEY----- diff --git a/make/target/product/gsi_release.mk b/make/target/product/gsi_release.mk new file mode 100644 index 0000000..74501cd --- /dev/null +++ b/make/target/product/gsi_release.mk @@ -0,0 +1,88 @@ +# +# Copyright (C) 2019 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# The makefile contains the special settings for GSI releasing. +# This makefile is used for the build targets which used for releasing GSI. +# +# For example: +# - Released GSI contains skip_mount.cfg to skip mounting prodcut paritition +# - Released GSI contains more VNDK packages to support old version vendors +# - etc. +# + +BUILDING_GSI := true + +# Exclude all files under system/product and system/system_ext +PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ + system/product/% \ + system/system_ext/% + +# GSI should always support up-to-date platform features. +# Keep this value at the latest API level to ensure latest build system +# default configs are applied. +PRODUCT_SHIPPING_API_LEVEL := 31 + +# Enable dynamic partitions to facilitate mixing onto Cuttlefish +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# Enable dynamic partition size +PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true + +# Disable the build-time debugfs restrictions on GSI builds +PRODUCT_SET_DEBUGFS_RESTRICTIONS := false + +# GSI targets should install "unflattened" APEXes in /system +TARGET_FLATTEN_APEX := false + +# GSI targets should install "flattened" APEXes in /system_ext as well +PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES := true + +# The flattened version of com.android.apex.cts.shim.v1 should be explicitly installed +# because the shim apex is prebuilt one and PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES is not +# supported for prebuilt_apex modules yet. +PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_with_prebuilts.flattened + +# GSI specific tasks on boot +PRODUCT_PACKAGES += \ + gsi_skip_mount.cfg \ + init.gsi.rc \ + init.vndk-nodef.rc \ + +# Support additional VNDK snapshots +PRODUCT_EXTRA_VNDK_VERSIONS := \ + 28 \ + 29 \ + 30 \ + 31 \ + 32 \ + +# Do not build non-GSI partition images. +PRODUCT_BUILD_CACHE_IMAGE := false +PRODUCT_BUILD_DEBUG_BOOT_IMAGE := false +PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false +PRODUCT_BUILD_USERDATA_IMAGE := false +PRODUCT_BUILD_VENDOR_IMAGE := false +PRODUCT_BUILD_SUPER_PARTITION := false +PRODUCT_BUILD_SUPER_EMPTY_IMAGE := false +PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true + +# Always build modules from source +MODULE_BUILD_FROM_SOURCE := true + +# Additional settings used in all GSI builds +PRODUCT_PRODUCT_PROPERTIES += \ + ro.crypto.metadata_init_delete_all_keys.enabled=false \ diff --git a/make/target/product/handheld_product.mk b/make/target/product/handheld_product.mk new file mode 100644 index 0000000..2199c57 --- /dev/null +++ b/make/target/product/handheld_product.mk @@ -0,0 +1,40 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the product partition contents for +# a generic phone or tablet device. Only add something here if +# it definitely doesn't belong on other types of devices (if it +# does, use base_product.mk). +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk) + +# /product packages +PRODUCT_PACKAGES += \ + Browser2 \ + Calendar \ + Camera2 \ + Contacts \ + DeskClock \ + Gallery2 \ + LatinIME \ + Music \ + OneTimeInitializer \ + preinstalled-packages-platform-handheld-product.xml \ + QuickSearchBox \ + SettingsIntelligence \ + frameworks-base-overlays + +PRODUCT_PACKAGES_DEBUG += \ + frameworks-base-overlays-debug diff --git a/make/target/product/handheld_system.mk b/make/target/product/handheld_system.mk new file mode 100644 index 0000000..41233b2 --- /dev/null +++ b/make/target/product/handheld_system.mk @@ -0,0 +1,87 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the system partition contents for +# a generic phone or tablet device. Only add something here if +# it definitely doesn't belong on other types of devices (if it +# does, use base_vendor.mk). +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk) +$(call inherit-product-if-exists, frameworks/base/data/fonts/fonts.mk) +$(call inherit-product-if-exists, external/google-fonts/dancing-script/fonts.mk) +$(call inherit-product-if-exists, external/google-fonts/carrois-gothic-sc/fonts.mk) +$(call inherit-product-if-exists, external/google-fonts/coming-soon/fonts.mk) +$(call inherit-product-if-exists, external/google-fonts/cutive-mono/fonts.mk) +$(call inherit-product-if-exists, external/google-fonts/source-sans-pro/fonts.mk) +$(call inherit-product-if-exists, external/noto-fonts/fonts.mk) +$(call inherit-product-if-exists, external/roboto-fonts/fonts.mk) +$(call inherit-product-if-exists, external/hyphenation-patterns/patterns.mk) +$(call inherit-product-if-exists, frameworks/base/data/keyboards/keyboards.mk) +$(call inherit-product-if-exists, frameworks/webview/chromium/chromium.mk) + +PRODUCT_PACKAGES += \ + BasicDreams \ + BlockedNumberProvider \ + BluetoothMidiService \ + BookmarkProvider \ + BuiltInPrintService \ + CalendarProvider \ + cameraserver \ + CameraExtensionsProxy \ + CaptivePortalLogin \ + CertInstaller \ + DocumentsUI \ + DownloadProviderUi \ + EasterEgg \ + ExternalStorageProvider \ + FusedLocation \ + InputDevices \ + KeyChain \ + librs_jni \ + ManagedProvisioning \ + MmsService \ + MtpService \ + MusicFX \ + NfcNci \ + PacProcessor \ + PrintRecommendationService \ + PrintSpooler \ + ProxyHandler \ + screenrecord \ + SecureElement \ + SharedStorageBackup \ + SimAppDialog \ + Telecom \ + TelephonyProvider \ + TeleService \ + Traceur \ + UserDictionaryProvider \ + VpnDialogs \ + vr \ + + +PRODUCT_SYSTEM_SERVER_APPS += \ + FusedLocation \ + InputDevices \ + KeyChain \ + Telecom \ + +PRODUCT_COPY_FILES += \ + frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf + +PRODUCT_VENDOR_PROPERTIES += \ + ro.carrier?=unknown \ + ro.config.notification_sound?=OnTheHunt.ogg \ + ro.config.alarm_alert?=Alarm_Classic.ogg diff --git a/make/target/product/handheld_system_ext.mk b/make/target/product/handheld_system_ext.mk new file mode 100644 index 0000000..d935fbf --- /dev/null +++ b/make/target/product/handheld_system_ext.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the system_ext partition contents for +# a generic phone or tablet device. Only add something here if +# it definitely doesn't belong on other types of devices (if it +# does, use base_system_ext.mk). +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system_ext.mk) + +# /system_ext packages +PRODUCT_PACKAGES += \ + Launcher3QuickStep \ + Provision \ + Settings \ + StorageManager \ + SystemUI \ + WallpaperCropper \ diff --git a/make/target/product/handheld_vendor.mk b/make/target/product/handheld_vendor.mk new file mode 100644 index 0000000..cb7cf74 --- /dev/null +++ b/make/target/product/handheld_vendor.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the non-system partition contents for +# a generic phone or tablet device. Only add something here if +# it definitely doesn't belong on other types of devices (if it +# does, use base_vendor.mk). +$(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk) + +# /vendor packages +PRODUCT_PACKAGES += \ + audio.primary.default \ + local_time.default \ + power.default \ + vibrator.default \ + diff --git a/make/target/product/languages_default.mk b/make/target/product/languages_default.mk new file mode 100644 index 0000000..a13a23c --- /dev/null +++ b/make/target/product/languages_default.mk @@ -0,0 +1,105 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a build configuration that just contains a list of languages, with +# en_US set as the default language. +PRODUCT_LOCALES := \ + en_US \ + af_ZA \ + am_ET \ + ar_EG \ + ar_XB \ + as_IN \ + az_AZ \ + be_BY \ + bg_BG \ + bn_BD \ + bs_BA \ + ca_ES \ + cs_CZ \ + da_DK \ + de_DE \ + el_GR \ + en_AU \ + en_CA \ + en_GB \ + en_IN \ + en_XA \ + es_ES \ + es_US \ + et_EE \ + eu_ES \ + fa_IR \ + fi_FI \ + fr_CA \ + fr_FR \ + gl_ES \ + gu_IN \ + hi_IN \ + hr_HR \ + hu_HU \ + hy_AM \ + in_ID \ + is_IS \ + it_IT \ + iw_IL \ + ja_JP \ + ka_GE \ + kk_KZ \ + km_KH \ + kn_IN \ + ko_KR \ + ky_KG \ + lo_LA \ + lt_LT \ + lv_LV \ + mk_MK \ + ml_IN \ + mn_MN \ + mr_IN \ + ms_MY \ + my_MM \ + nb_NO \ + ne_NP \ + nl_NL \ + or_IN \ + pa_IN \ + pl_PL \ + pt_BR \ + pt_PT \ + ro_RO \ + ru_RU \ + si_LK \ + sk_SK \ + sl_SI \ + sq_AL \ + sr_Latn_RS \ + sr_RS \ + sv_SE \ + sw_TZ \ + ta_IN \ + te_IN \ + th_TH \ + tl_PH \ + tr_TR \ + uk_UA \ + ur_PK \ + uz_UZ \ + vi_VN \ + zh_CN \ + zh_HK \ + zh_TW \ + zu_ZA \ diff --git a/make/target/product/languages_full.mk b/make/target/product/languages_full.mk new file mode 100644 index 0000000..43a40a7 --- /dev/null +++ b/make/target/product/languages_full.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a build configuration that contains the default list of languages, +# as well as the en_XC pseudo-locale, which is useful for localization test +# builds. + +$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) +PRODUCT_LOCALES += en_XC diff --git a/make/target/product/mainline_sdk.mk b/make/target/product/mainline_sdk.mk new file mode 100644 index 0000000..343aed6 --- /dev/null +++ b/make/target/product/mainline_sdk.mk @@ -0,0 +1,18 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_NAME := mainline_sdk +PRODUCT_BRAND := Android +PRODUCT_DEVICE := mainline_sdk diff --git a/make/target/product/mainline_system.mk b/make/target/product/mainline_system.mk new file mode 120000 index 0000000..0b6eaf0 --- /dev/null +++ b/make/target/product/mainline_system.mk @@ -0,0 +1 @@ +generic_system.mk \ No newline at end of file diff --git a/make/target/product/mainline_system_arm64.mk b/make/target/product/mainline_system_arm64.mk new file mode 100644 index 0000000..5fa13ce --- /dev/null +++ b/make/target/product/mainline_system_arm64.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Do not modify this file. It's just alias of generic_system_arm64.mk +# Will be removed when renaming from mainline_system to generic_system +# complete + +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_arm64.mk) + +PRODUCT_NAME := mainline_system_arm64 diff --git a/make/target/product/mainline_system_x86.mk b/make/target/product/mainline_system_x86.mk new file mode 100644 index 0000000..3fb1963 --- /dev/null +++ b/make/target/product/mainline_system_x86.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Do not modify this file. It's just alias of generic_system_x86.mk +# Will be removed when renaming from mainline_system to generic_system +# complete + +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86.mk) + +PRODUCT_NAME := mainline_system_x86 diff --git a/make/target/product/mainline_system_x86_64.mk b/make/target/product/mainline_system_x86_64.mk new file mode 100644 index 0000000..eab99c5 --- /dev/null +++ b/make/target/product/mainline_system_x86_64.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Do not modify this file. It's just alias of generic_system_x86_64.mk +# Will be removed when renaming from mainline_system to generic_system +# complete + +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86_64.mk) + +PRODUCT_NAME := mainline_system_x86_64 diff --git a/make/target/product/mainline_system_x86_arm.mk b/make/target/product/mainline_system_x86_arm.mk new file mode 100644 index 0000000..483fb58 --- /dev/null +++ b/make/target/product/mainline_system_x86_arm.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Do not modify this file. It's just alias of generic_system_x86_arm.mk +# Will be removed when renaming from mainline_system to generic_system +# complete + +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86_arm.mk) + +PRODUCT_NAME := mainline_system_x86_arm diff --git a/make/target/product/media_product.mk b/make/target/product/media_product.mk new file mode 100644 index 0000000..76cb311 --- /dev/null +++ b/make/target/product/media_product.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the product partition contents for +# media-capable devices (non-wearables). Only add something here +# if it definitely doesn't belong on wearables. Otherwise, choose +# base_product.mk. +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk) + +# /product packages +PRODUCT_PACKAGES += \ + webview \ diff --git a/make/target/product/media_system.mk b/make/target/product/media_system.mk new file mode 100644 index 0000000..79bd74a --- /dev/null +++ b/make/target/product/media_system.mk @@ -0,0 +1,78 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the system partition contents for +# media-capable devices (non-wearables). Only add something +# here if it definitely doesn't belong on wearables. Otherwise, +# choose base_system.mk. +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk) + +PRODUCT_PACKAGES += \ + com.android.future.usb.accessory \ + com.android.mediadrm.signer \ + com.android.media.remotedisplay \ + com.android.media.remotedisplay.xml \ + CompanionDeviceManager \ + drmserver \ + fsck.f2fs \ + HTMLViewer \ + libfilterpack_imageproc \ + libwebviewchromium_loader \ + libwebviewchromium_plat_support \ + make_f2fs \ + requestsync \ + StatementService \ + +PRODUCT_HOST_PACKAGES += \ + fsck.f2fs \ + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.software.webview.xml:system/etc/permissions/android.software.webview.xml + +ifneq (REL,$(PLATFORM_VERSION_CODENAME)) +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.software.preview_sdk.xml:system/etc/permissions/android.software.preview_sdk.xml +endif + +# The order here is the same order they end up on the classpath, so it matters. +PRODUCT_SYSTEM_SERVER_JARS := \ + com.android.location.provider \ + services + +PRODUCT_COPY_FILES += \ + system/core/rootdir/etc/public.libraries.android.txt:system/etc/public.libraries.txt + +# Enable boot.oat filtering of compiled classes to reduce boot.oat size. b/28026683 +PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\ + frameworks/base/config/compiled-classes-phone:system/etc/compiled-classes) + +# Enable dirty image object binning to reduce dirty pages in the image. +PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\ + frameworks/base/dirty-image-objects-phone:system/etc/dirty-image-objects) + +# On userdebug builds, collect more tombstones by default. +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +PRODUCT_VENDOR_PROPERTIES += \ + tombstoned.max_tombstone_count?=50 +endif + +PRODUCT_VENDOR_PROPERTIES += \ + ro.logd.size.stats=64K \ + log.tag.stats_log=I + +# Enable CFI for security-sensitive components +$(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk) +$(call inherit-product-if-exists, vendor/google/products/cfi-vendor.mk) diff --git a/make/target/product/media_system_ext.mk b/make/target/product/media_system_ext.mk new file mode 100644 index 0000000..2e20af3 --- /dev/null +++ b/make/target/product/media_system_ext.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the system_ext partition contents for +# media-capable devices (non-wearables). Only add something here +# if it definitely doesn't belong on wearables. Otherwise, choose +# base_system_ext.mk. +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk) + +# /system_ext packages +PRODUCT_PACKAGES += \ + vndk_apex_snapshot_package \ diff --git a/make/target/product/media_vendor.mk b/make/target/product/media_vendor.mk new file mode 100644 index 0000000..ef009ad --- /dev/null +++ b/make/target/product/media_vendor.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This makefile contains the non-system partition contents for +# media-capable devices (non-wearables). Only add something here +# if it definitely doesn't belong on wearables. Otherwise, choose +# base_vendor.mk. +$(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk) + +# /vendor packages +PRODUCT_PACKAGES += \ + libaudiopreprocessing \ diff --git a/make/target/product/module_arm.mk b/make/target/product/module_arm.mk new file mode 100644 index 0000000..d99dce8 --- /dev/null +++ b/make/target/product/module_arm.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) + +PRODUCT_NAME := module_arm +PRODUCT_BRAND := Android +PRODUCT_DEVICE := module_arm diff --git a/make/target/product/module_arm64.mk b/make/target/product/module_arm64.mk new file mode 100644 index 0000000..fc9529c --- /dev/null +++ b/make/target/product/module_arm64.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) + +PRODUCT_NAME := module_arm64 +PRODUCT_BRAND := Android +PRODUCT_DEVICE := module_arm64 diff --git a/make/target/product/module_common.mk b/make/target/product/module_common.mk new file mode 100644 index 0000000..54f3949 --- /dev/null +++ b/make/target/product/module_common.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk) + +# Enables treble, which enabled certain -D compilation flags. In particular, libhidlbase +# uses -DENFORCE_VINTF_MANIFEST. See b/185759877 +PRODUCT_SHIPPING_API_LEVEL := 29 + +# Builds using a module product should build modules from source, even if +# BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE says otherwise. +PRODUCT_MODULE_BUILD_FROM_SOURCE := true diff --git a/make/target/product/module_x86.mk b/make/target/product/module_x86.mk new file mode 100644 index 0000000..b852e7a --- /dev/null +++ b/make/target/product/module_x86.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) + +PRODUCT_NAME := module_x86 +PRODUCT_BRAND := Android +PRODUCT_DEVICE := module_x86 diff --git a/make/target/product/module_x86_64.mk b/make/target/product/module_x86_64.mk new file mode 100644 index 0000000..f6bc1fc --- /dev/null +++ b/make/target/product/module_x86_64.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) + +PRODUCT_NAME := module_x86_64 +PRODUCT_BRAND := Android +PRODUCT_DEVICE := module_x86_64 diff --git a/make/target/product/ndk.mk b/make/target/product/ndk.mk new file mode 100644 index 0000000..1dfd0db --- /dev/null +++ b/make/target/product/ndk.mk @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This device is suitable for soong-only build that builds for all the architectures +# needed for the ndk. It is not going to work for normal `lunch && m` workflows. + +PRODUCT_NAME := ndk +PRODUCT_BRAND := Android +PRODUCT_DEVICE := ndk diff --git a/make/target/product/non_ab_device.mk b/make/target/product/non_ab_device.mk new file mode 100644 index 0000000..6dc4506 --- /dev/null +++ b/make/target/product/non_ab_device.mk @@ -0,0 +1,5 @@ +# Packages to update the recovery partition, which will be installed on +# /vendor. Don't install these unless they're needed. +PRODUCT_PACKAGES += \ + applypatch + diff --git a/make/target/product/product_launched_with_k.mk b/make/target/product/product_launched_with_k.mk new file mode 100644 index 0000000..87faa12 --- /dev/null +++ b/make/target/product/product_launched_with_k.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 19 diff --git a/make/target/product/product_launched_with_l.mk b/make/target/product/product_launched_with_l.mk new file mode 100644 index 0000000..4e79749 --- /dev/null +++ b/make/target/product/product_launched_with_l.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 21 diff --git a/make/target/product/product_launched_with_l_mr1.mk b/make/target/product/product_launched_with_l_mr1.mk new file mode 100644 index 0000000..2086832 --- /dev/null +++ b/make/target/product/product_launched_with_l_mr1.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 22 diff --git a/make/target/product/product_launched_with_m.mk b/make/target/product/product_launched_with_m.mk new file mode 100644 index 0000000..1ba1014 --- /dev/null +++ b/make/target/product/product_launched_with_m.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 23 diff --git a/make/target/product/product_launched_with_n.mk b/make/target/product/product_launched_with_n.mk new file mode 100644 index 0000000..cac29eb --- /dev/null +++ b/make/target/product/product_launched_with_n.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 24 diff --git a/make/target/product/product_launched_with_n_mr1.mk b/make/target/product/product_launched_with_n_mr1.mk new file mode 100644 index 0000000..194a1aa --- /dev/null +++ b/make/target/product/product_launched_with_n_mr1.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 25 diff --git a/make/target/product/product_launched_with_o.mk b/make/target/product/product_launched_with_o.mk new file mode 100644 index 0000000..8e25a2b --- /dev/null +++ b/make/target/product/product_launched_with_o.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 26 diff --git a/make/target/product/product_launched_with_o_mr1.mk b/make/target/product/product_launched_with_o_mr1.mk new file mode 100644 index 0000000..2f3d7e6 --- /dev/null +++ b/make/target/product/product_launched_with_o_mr1.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 27 diff --git a/make/target/product/product_launched_with_p.mk b/make/target/product/product_launched_with_p.mk new file mode 100644 index 0000000..fe7591f --- /dev/null +++ b/make/target/product/product_launched_with_p.mk @@ -0,0 +1,2 @@ +#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. +PRODUCT_SHIPPING_API_LEVEL := 28 \ No newline at end of file diff --git a/make/target/product/profile_boot_common.mk b/make/target/product/profile_boot_common.mk new file mode 100644 index 0000000..fa2e163 --- /dev/null +++ b/make/target/product/profile_boot_common.mk @@ -0,0 +1,30 @@ +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Use an empty profile to make non of the boot image be AOT compiled (for now). +# Note that we could use a previous profile but we will miss the opportunity to +# remove classes that are no longer in use. +# Ideally we would just generate an empty boot.art but we don't have the build +# support to separate the image from the compile code. +PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION := build/make/target/product/empty-profile +DEX_PREOPT_DEFAULT := nostripping + +# Boot image property overrides. +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.profilesystemserver=true \ + dalvik.vm.profilebootclasspath=true + +PRODUCT_DIST_BOOT_AND_SYSTEM_JARS := true diff --git a/make/target/product/runtime_libart.mk b/make/target/product/runtime_libart.mk new file mode 100644 index 0000000..b6560fc --- /dev/null +++ b/make/target/product/runtime_libart.mk @@ -0,0 +1,159 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Provides a functioning ART environment without Android frameworks + +$(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk) + +# Additional mixins to the boot classpath. +PRODUCT_PACKAGES += \ + android.test.base \ + +# Why are we pulling in ext, which is frameworks/base, depending on tagsoup and nist-sip? +PRODUCT_PACKAGES += \ + ext \ + +# Runtime (Bionic) APEX module. +PRODUCT_PACKAGES += com.android.runtime + +# ART APEX module. +# +# Select either release (com.android.art) or debug (com.android.art.debug) +# variant of the ART APEX. By default, "user" build variants contain the release +# module, while the "eng" build variant contain the debug module. However, if +# `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is defined, it overrides the previous +# logic: +# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `false`, the +# build will include the release module (whatever the build +# variant); +# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `true`, the +# build will include the debug module (whatever the build variant). +# +# Note that the ART APEX package includes the minimal boot classpath JARs +# (listed in ART_APEX_JARS), which should no longer be added directly to +# PRODUCT_PACKAGES. + +art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD) +ifneq (false,$(art_target_include_debug_build)) + ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT))) + art_target_include_debug_build := true + endif +endif + +ifeq (true,$(art_target_include_debug_build)) + PRODUCT_PACKAGES += com.android.art.debug + apex_test_module := art-check-debug-apex-gen-fakebin +else + PRODUCT_PACKAGES += com.android.art + apex_test_module := art-check-release-apex-gen-fakebin +endif + +ifeq (true,$(call soong_config_get,art_module,source_build)) + PRODUCT_HOST_PACKAGES += $(apex_test_module) +endif + +art_target_include_debug_build := +apex_test_module := + +# Certificates. +PRODUCT_PACKAGES += \ + cacerts \ + +PRODUCT_PACKAGES += \ + hiddenapi-package-whitelist.xml \ + +ifeq (,$(TARGET_BUILD_UNBUNDLED)) + # Don't depend on the framework boot image profile in unbundled builds where + # frameworks/base may not be present. + # TODO(b/179900989): We may not need this check once we stop using full + # platform products on the thin ART manifest branch. + PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += frameworks/base/boot/boot-image-profile.txt +endif + +# The dalvik.vm.dexopt.thermal-cutoff property must contain one of the values +# listed here: +# +# https://source.android.com/devices/architecture/hidl/thermal-mitigation#thermal-api +# +# If the thermal status of the device reaches or exceeds the value set here +# background dexopt will be terminated and rescheduled using an exponential +# backoff polcy. +# +# The thermal cutoff value is currently set to THERMAL_STATUS_MODERATE. +PRODUCT_SYSTEM_PROPERTIES += \ + dalvik.vm.usejit=true \ + dalvik.vm.usejitprofiles=true \ + dalvik.vm.dexopt.secondary=true \ + dalvik.vm.dexopt.thermal-cutoff=2 \ + dalvik.vm.appimageformat=lz4 + +PRODUCT_SYSTEM_PROPERTIES += \ + ro.dalvik.vm.native.bridge?=0 + +# Different dexopt types for different package update/install times. +# On eng builds, make "boot" reasons only extract for faster turnaround. +ifeq (eng,$(TARGET_BUILD_VARIANT)) + PRODUCT_SYSTEM_PROPERTIES += \ + pm.dexopt.first-boot?=extract \ + pm.dexopt.boot-after-ota?=extract +else + PRODUCT_SYSTEM_PROPERTIES += \ + pm.dexopt.first-boot?=verify \ + pm.dexopt.boot-after-ota?=verify +endif + +# The install filter is speed-profile in order to enable the use of +# profiles from the dex metadata files. Note that if a profile is not provided +# or if it is empty speed-profile is equivalent to (quicken + empty app image). +# Note that `cmdline` is not strictly needed but it simplifies the management +# of compilation reason in the platform (as we have a unified, single path, +# without exceptions). +PRODUCT_SYSTEM_PROPERTIES += \ + pm.dexopt.post-boot?=extract \ + pm.dexopt.install?=speed-profile \ + pm.dexopt.install-fast?=skip \ + pm.dexopt.install-bulk?=speed-profile \ + pm.dexopt.install-bulk-secondary?=verify \ + pm.dexopt.install-bulk-downgraded?=verify \ + pm.dexopt.install-bulk-secondary-downgraded?=extract \ + pm.dexopt.bg-dexopt?=speed-profile \ + pm.dexopt.ab-ota?=speed-profile \ + pm.dexopt.inactive?=verify \ + pm.dexopt.cmdline?=verify \ + pm.dexopt.shared?=speed + +# Enable resolution of startup const strings. +PRODUCT_SYSTEM_PROPERTIES += \ + dalvik.vm.dex2oat-resolve-startup-strings=true + +# Specify default block size of 512K to enable parallel image decompression. +PRODUCT_SYSTEM_PROPERTIES += \ + dalvik.vm.dex2oat-max-image-block-size=524288 + +# Enable minidebuginfo generation unless overridden. +PRODUCT_SYSTEM_PROPERTIES += \ + dalvik.vm.minidebuginfo=true \ + dalvik.vm.dex2oat-minidebuginfo=true + +# Enable Madvising of the whole art, odex and vdex files to MADV_WILLNEED. +# The size specified here is the size limit of how much of the file +# (in bytes) is madvised. +# We madvise the whole .art file to MADV_WILLNEED with UINT_MAX limit. +# For odex and vdex files, we limit madvising to 100MB. +PRODUCT_SYSTEM_PROPERTIES += \ + dalvik.vm.madvise.vdexfile.size=104857600 \ + dalvik.vm.madvise.odexfile.size=104857600 \ + dalvik.vm.madvise.artfile.size=4294967295 diff --git a/make/target/product/sdk.mk b/make/target/product/sdk.mk new file mode 100644 index 0000000..fa7e1ad --- /dev/null +++ b/make/target/product/sdk.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a simple product that uses configures the minimum amount +# needed to build the SDK (without the emulator). + +$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) + +PRODUCT_NAME := sdk +PRODUCT_BRAND := Android +PRODUCT_DEVICE := mainline_x86 diff --git a/make/target/product/sdk_arm64.mk b/make/target/product/sdk_arm64.mk new file mode 100644 index 0000000..8bb38f4 --- /dev/null +++ b/make/target/product/sdk_arm64.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Don't modify this file - It's just an alias! + +$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_phone_arm64.mk) + +PRODUCT_NAME := sdk_arm64 diff --git a/make/target/product/sdk_phone_arm64.mk b/make/target/product/sdk_phone_arm64.mk new file mode 100644 index 0000000..4203d45 --- /dev/null +++ b/make/target/product/sdk_phone_arm64.mk @@ -0,0 +1,67 @@ +# +# 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. +# +QEMU_USE_SYSTEM_EXT_PARTITIONS := true +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# Enable mainline checking for exact this product name +ifeq (sdk_phone_arm64,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor or vendor_boot image +# +$(call inherit-product-if-exists, device/generic/goldfish/arm64-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_arm64/device.mk) + +# keep this apk for sdk targets for now +PRODUCT_PACKAGES += \ + EmulatorSmokeTests + +# Overrides +PRODUCT_BRAND := Android +PRODUCT_NAME := sdk_phone_arm64 +PRODUCT_DEVICE := emulator_arm64 +PRODUCT_MODEL := Android SDK built for arm64 +# Disable checks for SDK product. It lacks some libraries (e.g. +# RadioConfigLib), which makes it impossible to translate their module names to +# library name, so the check fails. +PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true + + diff --git a/make/target/product/sdk_phone_armv7.mk b/make/target/product/sdk_phone_armv7.mk new file mode 100644 index 0000000..6c88b44 --- /dev/null +++ b/make/target/product/sdk_phone_armv7.mk @@ -0,0 +1,65 @@ +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +QEMU_USE_SYSTEM_EXT_PARTITIONS := true +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# Enable mainline checking for exact this product name +ifeq (sdk_phone_armv7,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/arm32-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_arm/device.mk) + +# keep this apk for sdk targets for now +PRODUCT_PACKAGES += \ + EmulatorSmokeTests + + +# Overrides +PRODUCT_BRAND := Android +PRODUCT_NAME := sdk_phone_armv7 +PRODUCT_DEVICE := emulator_arm +PRODUCT_MODEL := Android SDK built for arm +# Disable checks for SDK product. It lacks some libraries (e.g. +# RadioConfigLib), which makes it impossible to translate their module names to +# library name, so the check fails. +PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true diff --git a/make/target/product/sdk_phone_x86.mk b/make/target/product/sdk_phone_x86.mk new file mode 100644 index 0000000..a324e5f --- /dev/null +++ b/make/target/product/sdk_phone_x86.mk @@ -0,0 +1,60 @@ +# +# 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. +# +QEMU_USE_SYSTEM_EXT_PARTITIONS := true +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking for exact this product name +ifeq (sdk_phone_x86,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/x86-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_x86/device.mk) + +# Overrides +PRODUCT_BRAND := Android +PRODUCT_NAME := sdk_phone_x86 +PRODUCT_DEVICE := emulator_x86 +PRODUCT_MODEL := Android SDK built for x86 +# Disable checks for SDK product. It lacks some libraries (e.g. +# RadioConfigLib), which makes it impossible to translate their module names to +# library name, so the check fails. +PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true diff --git a/make/target/product/sdk_phone_x86_64.mk b/make/target/product/sdk_phone_x86_64.mk new file mode 100644 index 0000000..ff9018d --- /dev/null +++ b/make/target/product/sdk_phone_x86_64.mk @@ -0,0 +1,61 @@ +# +# 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. +# +QEMU_USE_SYSTEM_EXT_PARTITIONS := true +PRODUCT_USE_DYNAMIC_PARTITIONS := true + +# This is a build configuration for a full-featured build of the +# Open-Source part of the tree. It's geared toward a US-centric +# build quite specifically for the emulator, and might not be +# entirely appropriate to inherit from for on-device configurations. + +# +# All components inherited here go to system image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) + +# Enable mainline checking for exact this product name +ifeq (sdk_phone_x86_64,$(TARGET_PRODUCT)) +PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed +endif + +# +# All components inherited here go to system_ext image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) + +# +# All components inherited here go to product image +# +$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) + +# +# All components inherited here go to vendor image +# +$(call inherit-product-if-exists, device/generic/goldfish/x86_64-vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_x86_64/device.mk) + +# Overrides +PRODUCT_BRAND := Android +PRODUCT_NAME := sdk_phone_x86_64 +PRODUCT_DEVICE := emulator_x86_64 +PRODUCT_MODEL := Android SDK built for x86_64 +# Disable checks for SDK product. It lacks some libraries (e.g. +# RadioConfigLib), which makes it impossible to translate their module names to +# library name, so the check fails. +PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true diff --git a/make/target/product/sdk_x86.mk b/make/target/product/sdk_x86.mk new file mode 100644 index 0000000..13ee57d --- /dev/null +++ b/make/target/product/sdk_x86.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Don't modify this file - It's just an alias! + +$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_phone_x86.mk) + +PRODUCT_NAME := sdk_x86 diff --git a/make/target/product/sdk_x86_64.mk b/make/target/product/sdk_x86_64.mk new file mode 100644 index 0000000..5f6553e --- /dev/null +++ b/make/target/product/sdk_x86_64.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Don't modify this file - It's just an alias! + +$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_phone_x86_64.mk) + +PRODUCT_NAME := sdk_x86_64 diff --git a/make/target/product/security/Android.bp b/make/target/product/security/Android.bp new file mode 100644 index 0000000..1e26d59 --- /dev/null +++ b/make/target/product/security/Android.bp @@ -0,0 +1,27 @@ +// AOSP test certificate +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_app_certificate { + name: "aosp-testkey", + certificate: "testkey", +} + +// Certificate for CTS tests that rely on UICC hardware conforming to the +// updated CTS UICC card specification introduced in 2021. See +// //cts/tests/tests/carrierapi/Android.bp for more details. +android_app_certificate { + name: "cts-uicc-2021-testkey", + certificate: "cts_uicc_2021", +} + +// Google-owned certificate for CTS testing, since we can't trust arbitrary keys +// on release devices. +prebuilt_etc { + name: "fsverity-release-cert-der", + src: "fsverity-release.x509.der", + sub_dir: "security/fsverity", + filename_from_src: true, +} diff --git a/make/target/product/security/Android.mk b/make/target/product/security/Android.mk new file mode 100644 index 0000000..ad25a92 --- /dev/null +++ b/make/target/product/security/Android.mk @@ -0,0 +1,105 @@ +LOCAL_PATH:= $(call my-dir) + +####################################### +# verity_key (installed to /, i.e. part of system.img) +include $(CLEAR_VARS) + +LOCAL_MODULE := verity_key +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) + +# For devices using a separate ramdisk, we need a copy there to establish the chain of trust. +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) +LOCAL_REQUIRED_MODULES := verity_key_ramdisk +endif + +include $(BUILD_PREBUILT) + +####################################### +# verity_key (installed to ramdisk) +# +# Enabling the target when using system-as-root would cause build failure, as TARGET_RAMDISK_OUT +# points to the same location as TARGET_ROOT_OUT. +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + include $(CLEAR_VARS) + LOCAL_MODULE := verity_key_ramdisk + LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 + LOCAL_LICENSE_CONDITIONS := notice + LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE + LOCAL_MODULE_CLASS := ETC + LOCAL_SRC_FILES := verity_key + LOCAL_MODULE_STEM := verity_key + LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT) + include $(BUILD_PREBUILT) +endif + +####################################### +# adb key, if configured via PRODUCT_ADB_KEYS +ifdef PRODUCT_ADB_KEYS + ifneq ($(filter eng userdebug,$(TARGET_BUILD_VARIANT)),) + include $(CLEAR_VARS) + LOCAL_MODULE := adb_keys + LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 + LOCAL_LICENSE_CONDITIONS := notice + LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE + LOCAL_MODULE_CLASS := ETC + LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) + LOCAL_PREBUILT_MODULE_FILE := $(PRODUCT_ADB_KEYS) + include $(BUILD_PREBUILT) + endif +endif + + +####################################### +# otacerts: A keystore with the authorized keys in it, which is used to verify the authenticity of +# downloaded OTA packages. +include $(CLEAR_VARS) + +LOCAL_MODULE := otacerts +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_STEM := otacerts.zip +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/security +include $(BUILD_SYSTEM)/base_rules.mk + +extra_ota_keys := $(addsuffix .x509.pem,$(PRODUCT_EXTRA_OTA_KEYS)) + +$(LOCAL_BUILT_MODULE): PRIVATE_CERT := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem +$(LOCAL_BUILT_MODULE): PRIVATE_EXTRA_OTA_KEYS := $(extra_ota_keys) +$(LOCAL_BUILT_MODULE): \ + $(SOONG_ZIP) \ + $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem \ + $(extra_ota_keys) + $(SOONG_ZIP) -o $@ -j -symlinks=false \ + $(addprefix -f ,$(PRIVATE_CERT) $(PRIVATE_EXTRA_OTA_KEYS)) + + +####################################### +# otacerts for recovery image. +include $(CLEAR_VARS) + +LOCAL_MODULE := otacerts.recovery +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_STEM := otacerts.zip +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/security +include $(BUILD_SYSTEM)/base_rules.mk + +extra_recovery_keys := $(addsuffix .x509.pem,$(PRODUCT_EXTRA_RECOVERY_KEYS)) + +$(LOCAL_BUILT_MODULE): PRIVATE_CERT := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem +$(LOCAL_BUILT_MODULE): PRIVATE_EXTRA_RECOVERY_KEYS := $(extra_recovery_keys) +$(LOCAL_BUILT_MODULE): \ + $(SOONG_ZIP) \ + $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem \ + $(extra_recovery_keys) + $(SOONG_ZIP) -o $@ -j -symlinks=false \ + $(addprefix -f ,$(PRIVATE_CERT) $(PRIVATE_EXTRA_RECOVERY_KEYS)) diff --git a/make/target/product/security/README b/make/target/product/security/README new file mode 100644 index 0000000..2b161bb --- /dev/null +++ b/make/target/product/security/README @@ -0,0 +1,39 @@ +For detailed information on key types and image signing, please see: + +https://source.android.com/devices/tech/ota/sign_builds.html + +The test keys in this directory are used in development only and should +NEVER be used to sign packages in publicly released images (as that would +open a major security hole). + +key generation +-------------- + +The following commands were used to generate the test key pairs: + + development/tools/make_key testkey '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' + development/tools/make_key platform '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' + development/tools/make_key shared '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' + development/tools/make_key media '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' + development/tools/make_key cts_uicc_2021 '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' + +signing using the openssl commandline (for boot/system images) +-------------------------------------------------------------- + +1. convert pk8 format key to pem format + % openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem + +2. create a signature using the pem format key + % openssl dgst -binary -sha1 -sign testkey.pem FILE > FILE.sig + +extracting public keys for embedding +------------------------------------ + +dumpkey.jar is a Java tool that takes an x.509 certificate in PEM format as +input and prints a C structure to standard output: + + $ java -jar out/host/linux-x86/framework/dumpkey.jar build/make/target/product/security/testkey.x509.pem + {64,0xc926ad21,{1795090719,2141396315,950055447,2581568430,4268923165,1920809988,546586521,3498997798,1776797858,3740060814,1805317999,1429410244,129622599,1422441418,1783893377,1222374759,2563319927,323993566,28517732,609753416,1826472888,215237850,4261642700,4049082591,3228462402,774857746,154822455,2497198897,2758199418,3019015328,2794777644,87251430,2534927978,120774784,571297800,3695899472,2479925187,3811625450,3401832990,2394869647,3267246207,950095497,555058928,414729973,1136544882,3044590084,465547824,4058146728,2731796054,1689838846,3890756939,1048029507,895090649,247140249,178744550,3547885223,3165179243,109881576,3944604415,1044303212,3772373029,2985150306,3737520932,3599964420},{3437017481,3784475129,2800224972,3086222688,251333580,2131931323,512774938,325948880,2657486437,2102694287,3820568226,792812816,1026422502,2053275343,2800889200,3113586810,165549746,4273519969,4065247892,1902789247,772932719,3941848426,3652744109,216871947,3164400649,1942378755,3996765851,1055777370,964047799,629391717,2232744317,3910558992,191868569,2758883837,3682816752,2997714732,2702529250,3570700455,3776873832,3924067546,3555689545,2758825434,1323144535,61311905,1997411085,376844204,213777604,4077323584,9135381,1625809335,2804742137,2952293945,1117190829,4237312782,1825108855,3013147971,1111251351,2568837572,1684324211,2520978805,367251975,810756730,2353784344,1175080310}} + +This is called by build/make/core/Makefile to incorporate the OTA signing keys +into the recovery image. diff --git a/make/target/product/security/bluetooth.pk8 b/make/target/product/security/bluetooth.pk8 new file mode 100644 index 0000000..c6ea434 Binary files /dev/null and b/make/target/product/security/bluetooth.pk8 differ diff --git a/make/target/product/security/bluetooth.x509.pem b/make/target/product/security/bluetooth.x509.pem new file mode 100644 index 0000000..396d7c9 --- /dev/null +++ b/make/target/product/security/bluetooth.x509.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +MIIGOzCCBCOgAwIBAgIUEiZapaWZVSter06CJMf2kHi8PIswDQYJKoZIhvcNAQEL +BQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMScwJQYDVQQDDB5jb20uYW5kcm9pZC5ibHVldG9vdGguc2VydmljZXMxIjAg +BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMjIwMzE1MDAzNjAz +WhgPNDc2MDAyMDkwMDM2MDNaMIGrMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs +aWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9p +ZDEQMA4GA1UECwwHQW5kcm9pZDEnMCUGA1UEAwweY29tLmFuZHJvaWQuYmx1ZXRv +b3RoLnNlcnZpY2VzMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsVlq9pozUREGlb8u8Y0A +fYwPs5OuavNx/EsX03aTjmAXUfSOMAewqzUXDIRjw8UQvOW63utaZ0go9osDPzNf +VEftmGxW/AUC+HWGaLDQfCYO3ficPPOS7xpEhGZERNbnhvh5qX0NBt6mJygsfpOm +RPThbi6Ig2Brxh1eqVYqRkTjhNFKD6gCd1PdMmUSF88xEYaZWvTkET89Zh38lLza +2x/wfNZmCSAVurNw1Kf9NQfYsaGHwMsjrvTyhG93TTYXzRBFzAO2WlBiw6R0tQr8 +ZW5XCM9Yo6AS0KXiU0ZWwOXxhGdr38rNd7j9nZtpFwWmN1kgeb/vpEfq0Ylua9By +uURnfJZu2K4TbFamuyjihItra2ZKOtFNPDeuggKMCkuZz6WU8FCoMEpnq5P2agxN +OGAa7ynXdNzek98N3TGX8qtfEgCv6vyuM0gakJ6D9nM43nsCm1LkB/JA0CacWyRz +ljaLL1C4S43azEOYyOOb94ITnkZCQGtH33kxzamyPLIZ37VF4+v6yTXySLBzOnhe +Os5uBIDohVJuI838bLhZf8e5mIrnjiKwsmExXiQvgidbwvZKCz9n8YT4iUhWPx4F +W+GPcivZsvsECcnJ2QURK1zhir5QuLS7ZbAth4kiEUxJ6ujF5jftE+L/ClK2LiY0 +2IXWRCct8J1hfJZZx8lm3PUCAwEAAaNTMFEwHQYDVR0OBBYEFO5CgtQzKbTEd/Q9 +rxK14a9BBwFZMB8GA1UdIwQYMBaAFO5CgtQzKbTEd/Q9rxK14a9BBwFZMA8GA1Ud +EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAGrGS1zmaoARVq7qhoY+xzSc +1I/Tzf6vG6aHBC+CcIoSM2oqr6TGH+ADHAY6jhu/qzv1ij3gtoInAkBtkWvYsCIV +eISPj8Qomcd8EIeW77p+ArKzS4HY5m1c/O4D/5rkl6c0exFq4Pdw9V8xyM98QtLd +oj4xzzXUTPOIwkROHkj8otcML28m/MC0l/4b+flHnPqKFuLBjhxi9b/ZfwaXfjkx +TcXpM3nPH8zN7kaJpS1fPW1IJyxJYvT022uK+afpezTmyS/50aOncUGjDJRw8CcO +B88O8lpizDD3tD7P6jVOpRRJS4SnkVErbIn1xdWER6ubhnnycH7UmDVIx+vNd/t6 +YDa377au8Za+LnbDPfV1+Og+RaJSEIjJgfYyqnjBxGdRGN21VbqJdRzo/eO4ZFd2 +mGVtMosVr0jw4O8r60o9oMMWBTbFpxOI929QdcV+X1Lz8A8BZz0faXfZ2Z9usctu +W2FtZge3tsJ07z7kuhNdbnm2yQVfd0FqiJsapUjlhgcdFVoDWPuqOfWAoG31ble6 +eiNnxfjiCckPWyciIE6lw97nvavGjlUacH5qVG86hOWU7xyBgeQ0PH4e+Nxr50yU +A0GMxni1gefZFG8qEPdNRuDT1QdqDGh/8Ea11GEUMXdAxk0UzqyAtLDr6MbwK6lV +mqmeueFdogdjvQ3mXe94 +-----END CERTIFICATE----- diff --git a/make/target/product/security/cts_uicc_2021.pk8 b/make/target/product/security/cts_uicc_2021.pk8 new file mode 100644 index 0000000..3b2a7fa Binary files /dev/null and b/make/target/product/security/cts_uicc_2021.pk8 differ diff --git a/make/target/product/security/cts_uicc_2021.x509.pem b/make/target/product/security/cts_uicc_2021.x509.pem new file mode 100644 index 0000000..744afea --- /dev/null +++ b/make/target/product/security/cts_uicc_2021.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIECzCCAvOgAwIBAgIUHYLIIL60vWPD6aOBwZUcdbsae+cwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMB4XDTIxMDEyNjAwMjAyMVoXDTQ4MDYxMzAwMjAyMVowgZQxCzAJ +BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp +biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD +VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlOMSHqBu0ihUDfFgwMfO +pJtpyxHe0KKfHRndUQcYU/1v6/auy2YqkgKv+AraoukuU3gJeOiWoaqaWFNcm6md +WfGRNT4oABhhNS43n5PI4NlLjI4yeUJJppZn5LPpc/8vZ0P8ZFE9CJmtckCh+hES +BzqnxkCnq1PoxlcF3S/f8lOtd6ymaMDf3sYcePaoU8yTWFksl7EWRVwhBUIf7/r8 +epbNiV14/aH2cQfHVfpf54TIdk7s0/ehVA70A5gQp7Utn6mY2zEJlMrTKWRqA/a5 +oYiob3y+v2JWNcljHY6twwDOGwW7G0NWJVtaWj76Z3o9RpIhAglivhOrHTflIU3+ +2QIDAQABo1MwUTAdBgNVHQ4EFgQUZJ1oGb33n/OY+Mm8ykci4I6c9OcwHwYDVR0j +BBgwFoAUZJ1oGb33n/OY+Mm8ykci4I6c9OcwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAQEASajvU0KCN2kfATPV95LQVE3N/URPi/lX9MfQptE54E+R +6dHwHQIwU/fBFapAHfGgrpwUZftJO+Bad2iu5s1IhTJ0Q5v0yHdvWfo4EzVeMzPV ++/DWU786pPEomFkb9ZKhgVkFNPcbXlkUm/9HxRHPRTm8x+BE/75PKI+kh+pDmM+P +5v4W0qDKPgFzIY/D4F++gVyPZ3O+/GhunjsJozO+dvN+50FH6o/kBHm2+QqQNYPW +f232F3CYtH4uWI0TkbwmSvVGW8iOqh330Cef5zqwSdOkzybUirXFsHUu1Zad1aLT +t0mu6RgNEmX8efOQCcz2Z/on8lkIAxCBwLX7wkH5JA== +-----END CERTIFICATE----- diff --git a/make/target/product/security/fsverity-release.x509.der b/make/target/product/security/fsverity-release.x509.der new file mode 100644 index 0000000..cd8cd79 Binary files /dev/null and b/make/target/product/security/fsverity-release.x509.der differ diff --git a/make/target/product/security/media.pk8 b/make/target/product/security/media.pk8 new file mode 100644 index 0000000..aa64990 Binary files /dev/null and b/make/target/product/security/media.pk8 differ diff --git a/make/target/product/security/media.x509.pem b/make/target/product/security/media.x509.pem new file mode 100644 index 0000000..126e523 --- /dev/null +++ b/make/target/product/security/media.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUGEqCCRiF2Q0N7TxNe1vdJgKkB0QwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDQxM1oYDzIwNTExMjE1MDYwNDEzWjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8GlZHhQWjG/sAnH4u +/lppC9thHc7hjuYUa8R6PBhHUoKalAUYuL6vlbcxeirUXWRbSgL7iA29cp+RJpfv +Dtf7KvileBnFIFEfRbgna+j7r8lVRinzBq0BKq3G1MwCgwSjIM7kEq2+F6lV6REX +9px+eK+rGr8JV2Sdm6n0+SWXfkBWPcIfGEeR1lZ+FZTCtrYzJ/uusVJS+dRH3bMF +PO7k+owyW1Akf5jws9wujUfedaBwIEhibi10+R6fvp3cQDNEm4Fmfpv5UrMEshTC +Mhu0V1+XWTm3SJDG1TrfHKkHva+pq+bgRMADsobzqoCyP5wc0e8q7hywdkihoG96 +ObovAgMBAAGjUzBRMB0GA1UdDgQWBBRA+cKpUpFSFM4f5RlBjxsVdGo4bDAfBgNV +HSMEGDAWgBRA+cKpUpFSFM4f5RlBjxsVdGo4bDAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQAswwSceJr2P9tGlAronLGikfxj53bkCj6lbCEGZj3a +h0SFGswa3avwjBccv8aee0or4CoectwLGMnkAO4c+kUPxzjBpMqRxdECW9z7uB8+ +db2k1MaelVG4YIwN2VmmfOxlfnUO+D/5p1faSQzJaSWzzUM4q0FFtQuYVLsqDSUm +ojibsfENVAq/wM7hf9lp3p9CEwwcAKjNQcarFXSmcmY4rtvy0Ff40EfCiS9zSHdu +65Jahmpvu7AwmRYe/Keod3KFccZOZ3TYUSP3hSbvepLUulIK6o6JfJyqi0ONK+bY +ekHQ+cl3/SGvkN4DAlQpVWblfNEJz72DZFtGnBIY/Ier +-----END CERTIFICATE----- diff --git a/make/target/product/security/networkstack.pk8 b/make/target/product/security/networkstack.pk8 new file mode 100644 index 0000000..85b2f56 Binary files /dev/null and b/make/target/product/security/networkstack.pk8 differ diff --git a/make/target/product/security/networkstack.x509.pem b/make/target/product/security/networkstack.x509.pem new file mode 100644 index 0000000..9979d2e --- /dev/null +++ b/make/target/product/security/networkstack.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUbxcqiem4puLbYVRZZsZzpnUeHBAwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDQ0NVoYDzIwNTExMjE1MDYwNDQ1WjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCss1RxiVECUPoLgRvJ +ZCJj8MM/P314uyekK+pUrGaZ+JFzHmkjM/I+MKlU9WLd8Ui5LLxYexNdq+TrZWiP +4wNfQpGXDJnJ1Chfudq6i7H3cqKnGcbLx1yI2JrSHSOvQCSBmgC/5o/88eB92nui +w3PNk32+x2zhQ2gOOqQEJg8OweHMAbIWe7AVMAxfY5FrH8ow2zYvSz3gxKJAXHMk +DmA0+FL9607oGrm535v41DE8F7TvzJAEkqMzUYMVqtPQYavhwac01SQO0osVckil +HHEGG79kYeKsoTnij7xpU8vQa066Z7tkb1KEU8CW2/ckDFRauX5sFyoEZZiZUGrh +cWhXAgMBAAGjUzBRMB0GA1UdDgQWBBT1s2dML/MIKMz2mPHMH7XlbWL71zAfBgNV +HSMEGDAWgBT1s2dML/MIKMz2mPHMH7XlbWL71zAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQCVcD4FNkXkKytzyblsuLPXRc0bXHPK8JPji5Snl2Yc +joBT51pkZccCpDOD//bOKtiWbCGKpuo/qr8131G6bBj/QBqE2dtxnY16xSu+OQIQ +czOjidVR9HBo4QL0jTXQpAjJYXxxC9EgdZD3Pg/TQDQzDUfTa9sHf/da5gnC4D5M +Cxl1qQo+9Wt4cY6P883OcG94HAKJzEQVdMQz2XDbkhUJ8HYeIrLCDzypiYDz1Wb4 +V8V4E0jXSCN/TLwbv5VDS6UnBag2BE0sbuchjOTzNdCBB413DcYt3qLzO4surIA9 +kn/Aaxgzk+dsiIPrJfGb4jZqPlPL7f8WnJchv/gV5bK3 +-----END CERTIFICATE----- diff --git a/make/target/product/security/platform.keystore b/make/target/product/security/platform.keystore new file mode 100644 index 0000000..18466c4 Binary files /dev/null and b/make/target/product/security/platform.keystore differ diff --git a/make/target/product/security/platform.p12 b/make/target/product/security/platform.p12 new file mode 100644 index 0000000..7b89f25 Binary files /dev/null and b/make/target/product/security/platform.p12 differ diff --git a/make/target/product/security/platform.pem b/make/target/product/security/platform.pem new file mode 100644 index 0000000..a26733a --- /dev/null +++ b/make/target/product/security/platform.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMe2T5K4eZb0M4 +wXDqIDET3BULtWfO8vV4R8nz656XWJANweE5pHHyO3YMqRUV6tvdYnL0w7/veTUn +CFQVAQQycEXmygTpcxYQzCfnRQYINZwgC8ROKkw56GVPm2SzxskHx/Ap55xeEKKw +HMA8WaYqjGGfxLkyGWW3AV0IdLeD7JGpWztB+6Dg2L7z4N+H5lhWDAdz88aZ+X05 +568HDin0MpkOqCjG48uo16Nt9+tiFyRw08bTy2WbFZU2Y8IpRie5vir9doDLUXzp +5LS1AY91pGAJiGjdl8csT0YrO78m30Y/bW8E4CSKwfbRK5NPaJY2KwdvMhyuJJOA +AeWknv8RAgMBAAECggEABWis70agjs1lsz9SLt5AwBZL7/gppZyxg6/N8rPgiT82 +ace/bvp3qWU7Ylq5YB/bFqJBWVJBfiqYDmcS7s6M427q+W+t9TUmL/XvhweBUSem +YGZhSCJIK4a3QdWh4nvDUu9t7dTWovoYjJUpHFuQeLsHhM3zymSHt1Fb2jK4rT6R +Udjfrw/z/NpO9JVEMtxJd4EA8KkrxVrB63Z0Qzrb8pAweSbbzEjwsIKP77MRhYGz +DdtmB3jszmPbC/UIx0ile+URUxuVG+2ZL3MJrf15bxIAZxMwoJHsMY1bsfNe55V1 +GpBaFKaQAP3OkbycXIjmN/iQZjK1/F93SmdeEFGtgQKBgQDjvRU6/myGbvnF8I/i +HWE0/0XsdhTHw+tHZ86YRGS9/xFMR1MGp8npab9zcR1r13yxnWsx6nSbfhotVEEn +ydhjPbmvKt3LeKgbuKri3WHIgNNUZYN7Ggoz5tHYM326ci0Oi5NWd20Phlgut0QL +9JlqLsJ+vPe8U46I65s1bmLlgQKBgQDl23vn/+eWvXj+/cOr7ZmpkUZsQuHpr/91 +NFQITrPE/0pBWSW4Isa1+sup+RGVndxGIMSzPYmol1LRp2eiCFAcQFmfHkPNJI6d +nHC1DRrmvtoJ/uqox9/Tt0OicDJVAndcWZWSpl3b5/MUp2Kgjr4KnASIU0IXwqNO +xxED5XWBkQKBgQDO+49MKukf1cUVbeqXq/QpYsl+fGOunBY+s85E/gRB2o0yMrD3 +H5CXoBDtLyV6WkNuepBusZSBrj7EbAs7ymvs5rDYz7D9uQ+xAknuHx88SDnKPukc +/THAskyOn13qEtN4oMqsj6aDsap76vzhkNHOe+kxnzXZ6GdnchuCmG0yAQKBgQCt +OoecJel0WmFI0xjXht6Kw7iuOI2nn4NH4mL+S8oBymA89aGOr27QkS1ScSpGds0e +cO2tI9COJVU+OgrJO7icQgcI+zcf8NAmilMKr9C5W9ZZOCvDfbb7CR/I2QOlu5EX +IVCvUABmjd1NkISs+kbRGhn0X8LswzxuT2pMZG71cQKBgHfMgr1FHuh0zTMt70JX +eqjnbNCHbZ78M3j+tZmev7JULatvMMj9oZ4mk1+L7vQLzEahZa5aE8HruHH7bX1w +Qkf2aXXvgaFjZo2MLFfvwhZ4qWC5Xtsms/E/cpEYeTKue45kNttqlFU1teMXTmhA +YBaP3vpw+UmBFYvw+6IEr1NI +-----END PRIVATE KEY----- diff --git a/make/target/product/security/platform.pk8 b/make/target/product/security/platform.pk8 new file mode 100644 index 0000000..f5cba35 Binary files /dev/null and b/make/target/product/security/platform.pk8 differ diff --git a/make/target/product/security/platform.x509.pem b/make/target/product/security/platform.x509.pem new file mode 100644 index 0000000..a54edf3 --- /dev/null +++ b/make/target/product/security/platform.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUfUuGfouI4bGRccyMFHHCLOAbESYwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDM1NFoYDzIwNTExMjE1MDYwMzU0WjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMe2T5K4eZb0M4wXDq +IDET3BULtWfO8vV4R8nz656XWJANweE5pHHyO3YMqRUV6tvdYnL0w7/veTUnCFQV +AQQycEXmygTpcxYQzCfnRQYINZwgC8ROKkw56GVPm2SzxskHx/Ap55xeEKKwHMA8 +WaYqjGGfxLkyGWW3AV0IdLeD7JGpWztB+6Dg2L7z4N+H5lhWDAdz88aZ+X05568H +Din0MpkOqCjG48uo16Nt9+tiFyRw08bTy2WbFZU2Y8IpRie5vir9doDLUXzp5LS1 +AY91pGAJiGjdl8csT0YrO78m30Y/bW8E4CSKwfbRK5NPaJY2KwdvMhyuJJOAAeWk +nv8RAgMBAAGjUzBRMB0GA1UdDgQWBBQPh+WT+xCEkJsluw7KGHrJbrFlLTAfBgNV +HSMEGDAWgBQPh+WT+xCEkJsluw7KGHrJbrFlLTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQBVDMb7FDUaVNGph/gNyqXKXGuTa33OzakiAP8L2t2f +6ss3IbgVs9y5R3DgS45OhIEBkkvoVG6+AvxQLDW3ZfdUXrmi9QKgD6yPZ7mWlRCa +EonW38fi4rcK+B+0Xvdn+EVL4KTwe7m9MlsoSpunML3IAFBD77p6q3DCNnOPyBMn +tVGTrqy2OiYY8253l1y3H0PkObTE+fliqAb5loejVcErfpp+LrERxuLUzrc5tlkp ++dVYyLaCDJmxr4uxmhIrmaAnFKL2Rs3lnO15QpDG2eMjeusgYLs0Yli4eQ2zJ5dz +125xPQc3qfSgVRTdArWrluVcoV8bdtLkqcDtVHLj7FmS +-----END CERTIFICATE----- diff --git a/make/target/product/security/releasekey.pk8 b/make/target/product/security/releasekey.pk8 new file mode 100644 index 0000000..cc0b833 Binary files /dev/null and b/make/target/product/security/releasekey.pk8 differ diff --git a/make/target/product/security/releasekey.x509.pem b/make/target/product/security/releasekey.x509.pem new file mode 100644 index 0000000..0a3c28b --- /dev/null +++ b/make/target/product/security/releasekey.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIURV0eq8TFxgvUuKKfdqoFf+XJ/BAwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDU1N1oYDzIwNTExMjE1MDYwNTU3WjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1keKNLaasUAH+KRfv +1/boBgnT6eCAuYniDT2S1tqOs9hJglpU7J0vOtRAJZzZjb2keWR5WeSAXVRywaHF +b7m40HdgW0/XubCw7q3TT0N6vrJdWWbnCOS+L1+xOEQt68B5KaCLd9Uca0xBAsBy +yU4c7etwsiv0Ko5q3oMcBU5LW/XztwUoJg91NPEfS7qYvirY+cQNmUMHn0wyAg7Y +i5O+4rcnVEpGQrqHdttj/xyeCafi3LCqd4hYOt2KBvsv1kvg+pr+F20JoE2sn4v/ +A6Usm6xeYKnP0V1oMdUS9ZdrH8hTFSo1c0niamzQYAp0gwliCSeKQwzRH1sU8W2x +REdBAgMBAAGjUzBRMB0GA1UdDgQWBBTf0elozOWQp534PDG6YxtujWTT+zAfBgNV +HSMEGDAWgBTf0elozOWQp534PDG6YxtujWTT+zAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQAMLdrR3Z+AbX77sTnPv7fbiSr+KhMPrNXOjMOhtWNc +8ufpwTAsqtrmwncAntnA3UGDbyCP+weylH3923hmXxNQ72Eaf1HzMUtlZKkKaUOP +kvu/q5yuED5DzMueE6gQ8WfEPPyyRyhHzP7zTazNkZ3JeLLDW+53vQtonJVRv4ot +Xb1ho455NV5PFDdC6PEsVk45EOZ0W6ls7Q8OW3o80CmNZHOhKp7RL7Kesyuz/61h +UacQL3SQRsIE/XQyt8zpaZZTlx2Ma5Rm/y6tW4/9jq9xNwroPsEkw1lYNeIXhRi7 +mkzokxCiunKey0e1xsNQQZB7zeQ54tkjkFIrkN2rnTJf +-----END CERTIFICATE----- diff --git a/make/target/product/security/sdk_sandbox.pk8 b/make/target/product/security/sdk_sandbox.pk8 new file mode 100644 index 0000000..23b880b Binary files /dev/null and b/make/target/product/security/sdk_sandbox.pk8 differ diff --git a/make/target/product/security/sdk_sandbox.x509.pem b/make/target/product/security/sdk_sandbox.x509.pem new file mode 100644 index 0000000..0bd20f3 --- /dev/null +++ b/make/target/product/security/sdk_sandbox.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIECzCCAvOgAwIBAgIUMWJGQnrJU7zBEpPqv63u2HOlib0wDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMB4XDTIxMTEwMjE3MDIxNFoXDTQ5MDMyMDE3MDIxNFowgZQxCzAJ +BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp +biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD +VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA09j3dyTxv8ojb4sXjrWX +smXTYEez/u6X6po8+mWXp1xl1Y9xjYrxZROIE1MJL8aay8iYJihqx7RBWTPJYtYZ +TLElA3dyQuMgDIKtlQR3QAMRoc2IKrkfcIboEs71xl78EnTSQfRJTUEFvNigzjfB +e3JVtNDC9BR/33Iv9oNED84qW9C54h4TWHLyvo75unzPQUGS6uEIhhHa/8ynZZQW +YEd0NwAQNqbcMdbN8Bn6sRRCidEOIPd8Uu8DtIofLi7/YMo4CH1Q5f5UQbtPtqU2 +m8fjQN9WYzMazvWltRE+HYDH9YnXCLAsVicNdmFhAlXri15nG2AiRnSrHu/panAc +6wIDAQABo1MwUTAdBgNVHQ4EFgQU3F5r2DhJbRfkJKuqs1hjP/0dCUEwHwYDVR0j +BBgwFoAU3F5r2DhJbRfkJKuqs1hjP/0dCUEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAQEAwQQ8/D3f/WS5cwqcsFpT+Qzik9yTu53nsXz/pBDSbeM3 +zX1RCejXsmXhPjN7cu0uJYlrIuArOagHSC5pDci6GzcwunnnkRazSAmTpHLSRgeb +cLgKHLCph9sulI1r82x9upF47zLlbfkTrtGJryej+yWJ2Ne8irJIPeNR0z0sTBWJ +2Ngg55ezFWj3mihzw4Z6YU9txJB7Gj9eNYXdcubjoNs2mSU/6dR+HwJtD64FuH3x +QLGMZscizCN8N6b5xayjwPsszQhaHI4iR4oGJ9prbDd0JoylwWr2LrQhYuWQCn20 +cG5YhrtZshj6f1eGV1TDYd8xziapilqwzrchARvP8g== +-----END CERTIFICATE----- diff --git a/make/target/product/security/shared.pk8 b/make/target/product/security/shared.pk8 new file mode 100644 index 0000000..0e1166d Binary files /dev/null and b/make/target/product/security/shared.pk8 differ diff --git a/make/target/product/security/shared.x509.pem b/make/target/product/security/shared.x509.pem new file mode 100644 index 0000000..8256750 --- /dev/null +++ b/make/target/product/security/shared.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUBw+lU5QR1wjGPedDwGrgzwJxSmowDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDQwNFoYDzIwNTExMjE1MDYwNDA0WjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtO+aOFaaEkmSu1vpJ +1+xUuZEER6/oBnP1LeX106QqiNfy93Ziy1+b7lcJo81sThzYs7enB0LCNpK0QxhZ +UW49lxoZSRyf+5elI094l7CqyFsLpuvgrFU35SN42g+3zwGUCz1hC9m9u5AwH9tZ +8Y+nm2QTz5MVlxb6XwmU/JwTN4deDjxXeW5pZn7fvGX3pAPPjdPe0BvWxNgWaMSm +fMTSnbuH3cnca3lTHXcwPIWtBr91NP4RbNyy3gxkbUg8N+p9V2oU6f/HcWNbZuzH +ocXmT5WasFtS+tY+1wNehZKCIF46KE6ykW83wkiyjck2k7Zn4+yahtZC6UgUYi9T +nWCTAgMBAAGjUzBRMB0GA1UdDgQWBBStzuHpuREUfyfcEr+NgiXlNV0JljAfBgNV +HSMEGDAWgBStzuHpuREUfyfcEr+NgiXlNV0JljAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQAsmKyX/84mGRYGR2RH4ll7Cs0zmuswDM1QJgeEy3z8 +ABdv6yoSTaIS7Ewz6vFZzfv1RHeDeNjs2uzEnvC2ItpGl66W0KZu1F3t9uwpPBqM +TiHXwCcXvfw6lHOELyiLqJYBcz2U+T5TyMASGp68YAdFPSbpD5i4RxYLG155Lccn +Qjc0dCnFktnoflntcW64nXiF3HjeWEy5cQCgn/54zojBYUCzzi9FJHckUEfhWAbK +xecGq/EvH9OGbjNXzZo9kQ6cDYqdF7WUZLwuO9b3UqkTrr/egyzFoj5tAc9uzDVV +ij0L5g5lw7XqWsdCppXEJVBCSBel3XaudyG4aRXccZ37 +-----END CERTIFICATE----- diff --git a/make/target/product/security/testkey.pk8 b/make/target/product/security/testkey.pk8 new file mode 100644 index 0000000..ddd9ccb Binary files /dev/null and b/make/target/product/security/testkey.pk8 differ diff --git a/make/target/product/security/testkey.x509.pem b/make/target/product/security/testkey.x509.pem new file mode 100644 index 0000000..e950cf1 --- /dev/null +++ b/make/target/product/security/testkey.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUVZ2rjdvwo6JwLNZEBMA1IwjqQ6MwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDMxNloYDzIwNTExMjE1MDYwMzE2WjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClBEPvIELruWo0r/Fb +2Kz0b7hPL3QuGiXDZhqotE2kJHjxr40gqXFfr7P+p6y+h1DukGaWC/V9x0wNXA4F +VktT9clAuBH/wLa7bV7nPaxU723XMlO3txbZY3aIwaF6Fi4NNPOB9/49Wl17gWHk +QdjDn6ERrWj+zFDoUFy9/92Q+g79rINboQTlhnZe02930WQpye76TnaQr1LoXqrj +FIKtG2tBz7AFtEI9Ogl2snUl6EMVIArNTvLesIHZvXIPU31tWy0UiZOddLBiC9Ef +RnvUgUMrDcj3uAc4xJ5dLNfN7VCWCl7jwP4ETo1U1EnZ0VWQ0dohuXGufX6F9Hxz +eWZtAgMBAAGjUzBRMB0GA1UdDgQWBBQgVCmTu2GK2tax4SuU/inxjifnSTAfBgNV +HSMEGDAWgBQgVCmTu2GK2tax4SuU/inxjifnSTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQCf5xQDOiwGc7hgMxhxZDxB6L7kcDlFm8AMe7Fdxfve +nmlgz/3I8Z2evAfL9ebkhPR2CsqWACpfUlHTRVtj5pOiqVsLFDdYmWDYkflU/b3R +p/XASh6DaTf9ddM3TinBwrfsOv5mhhP7JePQupS7ow1WPttMftJxv7kAazysYIg9 +yivGeM+Nyg62fKTCruGOPIPEgGxbTtgIOCZnY01sGP/V3XGCIU4AOMPpvT8mlRO7 +MKGdHKEPR19hiat9mgada1b+kfsywEMVSIOApStD1SthoDfDbVShoo+9tcvoyrye +TR0sEq2IiIf/eXVm5+yqD3kNfFLJ39h1WMxpEChgg0gu +-----END CERTIFICATE----- diff --git a/make/target/product/security/verity.pk8 b/make/target/product/security/verity.pk8 new file mode 100644 index 0000000..93e68dc Binary files /dev/null and b/make/target/product/security/verity.pk8 differ diff --git a/make/target/product/security/verity.x509.pem b/make/target/product/security/verity.x509.pem new file mode 100644 index 0000000..9345a66 --- /dev/null +++ b/make/target/product/security/verity.x509.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUNCMvkg3Pu+vd/I9zv8klxkeRBoIwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu +ZHJvaWQuY29tMCAXDTI0MDcyOTA2MDUwMVoYDzIwNTExMjE1MDYwNTAxWjCBlDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 +YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxEDAO +BgNVBAMMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV7N14N8/FybPae0Et +egOj+0vrCiTl+0Xld6h3wUYhUrB5Q6MlwcWJ6NC/rgO+TTcj6VuMcAKmG5EmEw0A +zmP7637SzmK317ScvcBu7OqlayPxgZaoz1YUv3hdbROUg99raj2NShLr+kFnjxy9 +69CVtDcd3zGW5Kfp657frDGPHSuhPPSNyuFy5GC+cWCTBxl4QjtZSA2egahXCWVs +YJ/poCEPHzuz4FshbJj1l6ceb1IjEzGykMHkK1T+tJqERDwBSeFr1K/adI21gTFc +SxD3GoI1AO0ze1ltamaZSTwM1exFUfODS03y8CPMEp5OCvm2O/hSjaXc6JwEB3Sz +EkvJAgMBAAGjUzBRMB0GA1UdDgQWBBQq+3Em/QHD+UCWSYFNTec1muYJczAfBgNV +HSMEGDAWgBQq+3Em/QHD+UCWSYFNTec1muYJczAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQCGAmLpsZ28XmARC6k5fIt8rgwpr1IjbGMddNJsMpM/ +ITuoS0ovl+Kt3KRk3zYeRx7bwWx3SeyeXN1MzjzykrJvG+W5r4GMUrVzR2tiLjJJ +FKTyHTsB/t0yKZLMl4jjE2AcqzdH3mTFcOyYdLJhLSSCZN3PizKKzZ2sd7HT9Vre +d/Em4zz1fe4n9NulHqtkIsaCZNgfIoRCuiaLoxc0Tgok+hRregwM9ZvDJ92XQtrO +c70i7e2MP6O2MlAj390GPj5aA0RTV3QIUgX3+v4bM5HlGwL6ppoUhYxr9xLMs9WK +k17WPuLO7HuG2CIDdg07iPMdwCCLNa0bIUJeETs2VUI5 +-----END CERTIFICATE----- diff --git a/make/target/product/security/verity_key b/make/target/product/security/verity_key new file mode 100644 index 0000000..f27b001 Binary files /dev/null and b/make/target/product/security/verity_key differ diff --git a/make/target/product/sysconfig/Android.bp b/make/target/product/sysconfig/Android.bp new file mode 100644 index 0000000..29122e4 --- /dev/null +++ b/make/target/product/sysconfig/Android.bp @@ -0,0 +1,37 @@ +// Copyright (C} 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"}; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +prebuilt_etc { + name: "preinstalled-packages-platform-aosp-product.xml", + product_specific: true, + sub_dir: "sysconfig", + src: "preinstalled-packages-platform-aosp-product.xml", +} + +prebuilt_etc { + name: "preinstalled-packages-platform-full-base.xml", + sub_dir: "sysconfig", + src: "preinstalled-packages-platform-full-base.xml", +} + +prebuilt_etc { + name: "preinstalled-packages-platform-handheld-product.xml", + product_specific: true, + sub_dir: "sysconfig", + src: "preinstalled-packages-platform-handheld-product.xml", +} diff --git a/make/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml b/make/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml new file mode 100644 index 0000000..eec1326 --- /dev/null +++ b/make/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/make/target/product/sysconfig/preinstalled-packages-platform-full-base.xml b/make/target/product/sysconfig/preinstalled-packages-platform-full-base.xml new file mode 100644 index 0000000..f601355 --- /dev/null +++ b/make/target/product/sysconfig/preinstalled-packages-platform-full-base.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/make/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml b/make/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml new file mode 100644 index 0000000..a5d9ba2 --- /dev/null +++ b/make/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/make/target/product/telephony.mk b/make/target/product/telephony.mk new file mode 100644 index 0000000..3ad7a1f --- /dev/null +++ b/make/target/product/telephony.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# All modules for telephony +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk) +$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk) diff --git a/make/target/product/telephony_product.mk b/make/target/product/telephony_product.mk new file mode 100644 index 0000000..18374d4 --- /dev/null +++ b/make/target/product/telephony_product.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is the list of modules that are specific to products that have telephony +# hardware, and install to the product partition. + +# /product packages +PRODUCT_PACKAGES += \ + Dialer \ + ImsServiceEntitlement \ diff --git a/make/target/product/telephony_system.mk b/make/target/product/telephony_system.mk new file mode 100644 index 0000000..ef48719 --- /dev/null +++ b/make/target/product/telephony_system.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is the list of modules that are specific to products that have telephony +# hardware, and install on the system partition. + +PRODUCT_PACKAGES := \ + ONS \ + CarrierDefaultApp \ + CallLogBackup \ + com.android.cellbroadcast \ + CellBroadcastLegacyApp \ + +PRODUCT_COPY_FILES := \ diff --git a/make/target/product/telephony_system_ext.mk b/make/target/product/telephony_system_ext.mk new file mode 100644 index 0000000..f81a607 --- /dev/null +++ b/make/target/product/telephony_system_ext.mk @@ -0,0 +1,23 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is the list of modules that are specific to products that have telephony +# hardware, and install to the system_ext partition. + +# /system_ext packages +PRODUCT_PACKAGES += \ + CarrierConfig \ + EmergencyInfo \ diff --git a/make/target/product/telephony_vendor.mk b/make/target/product/telephony_vendor.mk new file mode 100644 index 0000000..94887cf --- /dev/null +++ b/make/target/product/telephony_vendor.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is the list of modules that are specific to products that have telephony +# hardware, and install outside the system partition. + +# /vendor packages +PRODUCT_PACKAGES := \ + rild \ diff --git a/make/target/product/updatable_apex.mk b/make/target/product/updatable_apex.mk new file mode 100644 index 0000000..d606e00 --- /dev/null +++ b/make/target/product/updatable_apex.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit this when the target needs to support updating APEXes + +ifneq ($(OVERRIDE_TARGET_FLATTEN_APEX),true) + # com.android.apex.cts.shim.v1_prebuilt overrides CtsShimPrebuilt + # and CtsShimPrivPrebuilt since they are packaged inside the APEX. + PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt + PRODUCT_VENDOR_PROPERTIES := ro.apex.updatable=true + TARGET_FLATTEN_APEX := false + # Use compressed apexes in pre-installed partitions. + # Note: this doesn't mean that all pre-installed apexes will be compressed. + # Whether an apex is compressed or not is controlled at apex Soong module + # via compresible property. + PRODUCT_COMPRESSED_APEX := true +endif diff --git a/make/target/product/userspace_reboot.mk b/make/target/product/userspace_reboot.mk new file mode 100644 index 0000000..f235d14 --- /dev/null +++ b/make/target/product/userspace_reboot.mk @@ -0,0 +1,19 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Inherit this when the target supports userspace reboot + +PRODUCT_VENDOR_PROPERTIES := init.userspace_reboot.is_supported=true diff --git a/make/target/product/vboot.mk b/make/target/product/vboot.mk new file mode 100644 index 0000000..48a4883 --- /dev/null +++ b/make/target/product/vboot.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Provides dependencies necessary for verified boot + +PRODUCT_SUPPORTS_VBOOT := true + +# The dev key is used to sign boot and recovery images. +# We expect this file to exist with the suffixes ".vbprivk" and ".vbpupk". +# TODO: find a proper location for this +PRODUCT_VBOOT_SIGNING_KEY := external/vboot_reference/tests/devkeys/kernel_data_key +PRODUCT_VBOOT_SIGNING_SUBKEY := external/vboot_reference/tests/devkeys/kernel_subkey diff --git a/make/target/product/verity.mk b/make/target/product/verity.mk new file mode 100644 index 0000000..5f09283 --- /dev/null +++ b/make/target/product/verity.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Provides dependencies necessary for verified boot. + +PRODUCT_SUPPORTS_BOOT_SIGNER := true +PRODUCT_SUPPORTS_VERITY := true +PRODUCT_SUPPORTS_VERITY_FEC := true + +# The dev key is used to sign boot and recovery images, and the verity +# metadata table. Actual product deliverables will be re-signed by hand. +# We expect this file to exist with the suffixes ".x509.pem" and ".pk8". +PRODUCT_VERITY_SIGNING_KEY := build/make/target/product/security/verity + +PRODUCT_PACKAGES += \ + verity_key diff --git a/make/target/product/virtual_ab_ota.mk b/make/target/product/virtual_ab_ota.mk new file mode 120000 index 0000000..16f7329 --- /dev/null +++ b/make/target/product/virtual_ab_ota.mk @@ -0,0 +1 @@ +virtual_ab_ota/launch.mk \ No newline at end of file diff --git a/make/target/product/virtual_ab_ota/README.md b/make/target/product/virtual_ab_ota/README.md new file mode 100644 index 0000000..2d40c03 --- /dev/null +++ b/make/target/product/virtual_ab_ota/README.md @@ -0,0 +1,16 @@ +# Virtual A/B makefiles + +Devices that uses Virtual A/B must inherit from one of the makefiles in this directory. + +## Structure + +``` +launch.mk + |- retrofit.mk + |- plus_non_ab.mk + +launch_with_vendor_ramdisk.mk + |- compression.mk + +compression_retrofit.mk +``` diff --git a/make/target/product/virtual_ab_ota/android_t_baseline.mk b/make/target/product/virtual_ab_ota/android_t_baseline.mk new file mode 100644 index 0000000..18e08e4 --- /dev/null +++ b/make/target/product/virtual_ab_ota/android_t_baseline.mk @@ -0,0 +1,52 @@ +# +# Copyright (C) 2022 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file enables baseline features, such as io_uring, +# userspace merge, etc. But sets compression method to none. +# This .mk file also removes snapuserd from vendor ramdisk, +# as T launching devices will have init_boot which has snapuserd +# in generic ramdisk. +# T launching devices should include this .mk file, and configure +# compression algorithm by setting +# PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD to gz or brotli. Complete +# set of supported algorithms can be found in +# system/core/fs_mgr/libsnapshot/cow_writer.cpp + +PRODUCT_VIRTUAL_AB_OTA := true + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled=true +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true + +PRODUCT_VIRTUAL_AB_COMPRESSION := true +PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD ?= none +PRODUCT_PACKAGES += \ + snapuserd \ + +# For dedicated recovery partitions, we need to include snapuserd +# For GKI devices, BOARD_USES_RECOVERY_AS_BOOT is empty, but +# so is BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT. +ifdef BUILDING_RECOVERY_IMAGE +ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true) +ifneq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) +PRODUCT_PACKAGES += \ + snapuserd.recovery +endif +endif +endif + diff --git a/make/target/product/virtual_ab_ota/compression.mk b/make/target/product/virtual_ab_ota/compression.mk new file mode 100644 index 0000000..d5bd2a5 --- /dev/null +++ b/make/target/product/virtual_ab_ota/compression.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2020 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk) + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled=true +PRODUCT_VIRTUAL_AB_COMPRESSION := true +PRODUCT_PACKAGES += \ + snapuserd.vendor_ramdisk \ + snapuserd \ + snapuserd.recovery diff --git a/make/target/product/virtual_ab_ota/compression_retrofit.mk b/make/target/product/virtual_ab_ota/compression_retrofit.mk new file mode 100644 index 0000000..6c29cba --- /dev/null +++ b/make/target/product/virtual_ab_ota/compression_retrofit.mk @@ -0,0 +1,28 @@ +# +# Copyright (C) 2020 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true +PRODUCT_VIRTUAL_AB_COMPRESSION := true + +# For devices that are not GKI-capable (eg do not have vendor_boot), +# snapuserd.ramdisk is included rather than snapuserd.vendor_ramdisk. +# When using virtual_ab_ota_compression_retrofit.mk, either +# virtual_ab_ota.mk or virtual_ab_ota_retrofit.mk must be inherited +# as well. +PRODUCT_PACKAGES += \ + snapuserd.ramdisk \ + snapuserd \ + snapuserd.recovery diff --git a/make/target/product/virtual_ab_ota/compression_with_xor.mk b/make/target/product/virtual_ab_ota/compression_with_xor.mk new file mode 100644 index 0000000..7d92532 --- /dev/null +++ b/make/target/product/virtual_ab_ota/compression_with_xor.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2021 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk) + + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true diff --git a/make/target/product/virtual_ab_ota/launch.mk b/make/target/product/virtual_ab_ota/launch.mk new file mode 100644 index 0000000..e4c4575 --- /dev/null +++ b/make/target/product/virtual_ab_ota/launch.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2019 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_VIRTUAL_AB_OTA := true + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true + +PRODUCT_PACKAGES += e2fsck_ramdisk diff --git a/make/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk b/make/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk new file mode 100644 index 0000000..de1f07d --- /dev/null +++ b/make/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2020 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Devices launching with Virtual A/B and has a vendor_boot partition is +# preferred to inherit from this makefile instead of launch.mk. + +PRODUCT_VIRTUAL_AB_OTA := true + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true + +PRODUCT_PACKAGES += \ + linker.vendor_ramdisk \ + e2fsck.vendor_ramdisk \ + fsck.f2fs.vendor_ramdisk \ diff --git a/make/target/product/virtual_ab_ota/plus_non_ab.mk b/make/target/product/virtual_ab_ota/plus_non_ab.mk new file mode 100644 index 0000000..820fa1e --- /dev/null +++ b/make/target/product/virtual_ab_ota/plus_non_ab.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2020 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk) + +PRODUCT_OTA_FORCE_NON_AB_PACKAGE := true + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.allow_non_ab=true diff --git a/make/target/product/virtual_ab_ota/retrofit.mk b/make/target/product/virtual_ab_ota/retrofit.mk new file mode 100644 index 0000000..93b42b7 --- /dev/null +++ b/make/target/product/virtual_ab_ota/retrofit.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2019 The Android Open-Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk) + +PRODUCT_VIRTUAL_AB_OTA_RETROFIT := true + +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.retrofit=true diff --git a/make/target/product/virtual_ab_ota_plus_non_ab.mk b/make/target/product/virtual_ab_ota_plus_non_ab.mk new file mode 120000 index 0000000..4979957 --- /dev/null +++ b/make/target/product/virtual_ab_ota_plus_non_ab.mk @@ -0,0 +1 @@ +virtual_ab_ota/plus_non_ab.mk \ No newline at end of file diff --git a/make/target/product/virtual_ab_ota_retrofit.mk b/make/target/product/virtual_ab_ota_retrofit.mk new file mode 120000 index 0000000..1e16ca8 --- /dev/null +++ b/make/target/product/virtual_ab_ota_retrofit.mk @@ -0,0 +1 @@ +virtual_ab_ota/retrofit.mk \ No newline at end of file diff --git a/make/target/product/window_extensions.mk b/make/target/product/window_extensions.mk new file mode 100644 index 0000000..5f5431f --- /dev/null +++ b/make/target/product/window_extensions.mk @@ -0,0 +1,24 @@ +# +# 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. +# + +# /system_ext packages +PRODUCT_PACKAGES += \ + androidx.window.extensions \ + androidx.window.sidecar + +# properties +PRODUCT_PRODUCT_PROPERTIES += \ + persist.wm.extensions.enabled=true diff --git a/make/tests/artifact_path_requirements/inherit1.rbc b/make/tests/artifact_path_requirements/inherit1.rbc new file mode 100644 index 0000000..dcef1bf --- /dev/null +++ b/make/tests/artifact_path_requirements/inherit1.rbc @@ -0,0 +1,21 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load(":inherit3.rbc", _inherit3_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + + rblf.inherit(handle, "test/inherit3", _inherit3_init) diff --git a/make/tests/artifact_path_requirements/inherit2.rbc b/make/tests/artifact_path_requirements/inherit2.rbc new file mode 100644 index 0000000..597b4e9 --- /dev/null +++ b/make/tests/artifact_path_requirements/inherit2.rbc @@ -0,0 +1,22 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load(":inherit4.rbc", _inherit4_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + + rblf.inherit(handle, "test/inherit4", _inherit4_init) + rblf.require_artifacts_in_path(handle, "vendor/", "") diff --git a/make/tests/artifact_path_requirements/inherit3.rbc b/make/tests/artifact_path_requirements/inherit3.rbc new file mode 100644 index 0000000..597b4e9 --- /dev/null +++ b/make/tests/artifact_path_requirements/inherit3.rbc @@ -0,0 +1,22 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load(":inherit4.rbc", _inherit4_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + + rblf.inherit(handle, "test/inherit4", _inherit4_init) + rblf.require_artifacts_in_path(handle, "vendor/", "") diff --git a/make/tests/artifact_path_requirements/inherit4.rbc b/make/tests/artifact_path_requirements/inherit4.rbc new file mode 100644 index 0000000..52028fe --- /dev/null +++ b/make/tests/artifact_path_requirements/inherit4.rbc @@ -0,0 +1,21 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + + rblf.setdefault(handle, "PRODUCT_COPY_FILES") + cfg["PRODUCT_COPY_FILES"] += ["foo/bar/baz.txt:vendor/etc/baz.txt"] diff --git a/make/tests/artifact_path_requirements/product.rbc b/make/tests/artifact_path_requirements/product.rbc new file mode 100644 index 0000000..7d1f169 --- /dev/null +++ b/make/tests/artifact_path_requirements/product.rbc @@ -0,0 +1,24 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load(":inherit1.rbc", _inherit1_init = "init") +load(":inherit2.rbc", _inherit2_init = "init") +load(":inherit3.rbc", _inherit3_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + rblf.inherit(handle, "test/inherit1", _inherit1_init) + rblf.inherit(handle, "test/inherit2", _inherit2_init) + rblf.inherit(handle, "test/inherit3", _inherit3_init) diff --git a/make/tests/artifact_path_requirements/test.rbc b/make/tests/artifact_path_requirements/test.rbc new file mode 100644 index 0000000..0a344d1 --- /dev/null +++ b/make/tests/artifact_path_requirements/test.rbc @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load("//build/make/tests/input_variables.rbc", input_variables_init = "init") +load(":product.rbc", "init") + +def assert_eq(expected, actual): + if expected != actual: + fail("Expected '%s', got '%s'" % (expected, actual)) + +def test(): + (globals, globals_base) = rblf.product_configuration("test/product", init, input_variables_init) + assert_eq(["foo/bar/baz.txt:vendor/etc/baz.txt"], globals["PRODUCTS.test/product.mk.PRODUCT_COPY_FILES"]) + assert_eq(["foo/bar/baz.txt:vendor/etc/baz.txt"], globals["PRODUCTS.test/inherit2.mk.PRODUCT_COPY_FILES"]) + assert_eq(["foo/bar/baz.txt:vendor/etc/baz.txt"], globals["PRODUCTS.test/inherit3.mk.PRODUCT_COPY_FILES"]) diff --git a/make/tests/board.rbc b/make/tests/board.rbc new file mode 100644 index 0000000..8696e40 --- /dev/null +++ b/make/tests/board.rbc @@ -0,0 +1,19 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + g["A_LIST_VARIABLE"] += ["bar"] diff --git a/make/tests/board_input_vars.rbc b/make/tests/board_input_vars.rbc new file mode 100644 index 0000000..69d9cd6 --- /dev/null +++ b/make/tests/board_input_vars.rbc @@ -0,0 +1,19 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + g["A_LIST_VARIABLE"] = ["foo"] diff --git a/make/tests/conversion_error.rbc b/make/tests/conversion_error.rbc new file mode 100644 index 0000000..5212378 --- /dev/null +++ b/make/tests/conversion_error.rbc @@ -0,0 +1,27 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Run test configuration and verify its result. +# The main configuration file is device.rbc. +# It inherits part1.rbc and also includes include1.rbc +# TODO(asmundak): more tests are needed to verify that: +# * multi-level inheritance works as expected +# * all runtime functions (wildcard, regex, etc.) work + +load("//build/make/core:product_config.rbc", "rblf") +load(":version_defaults.rbc", "version_defaults") +load(":device.rbc", "init") + +rblf.mk2rbc_error("file.mk:123", "cannot convert") diff --git a/make/tests/envsetup_tests.sh b/make/tests/envsetup_tests.sh new file mode 100755 index 0000000..abdcd56 --- /dev/null +++ b/make/tests/envsetup_tests.sh @@ -0,0 +1,37 @@ +#!/bin/bash -e + +source $(dirname $0)/../envsetup.sh + +unset TARGET_PRODUCT TARGET_BUILD_VARIANT TARGET_PLATFORM_VERSION + +function check_lunch +( + echo lunch $1 + set +e + lunch $1 > /dev/null 2> /dev/null + set -e + [ "$TARGET_PRODUCT" = "$2" ] || ( echo "lunch $1: expected TARGET_PRODUCT='$2', got '$TARGET_PRODUCT'" && exit 1 ) + [ "$TARGET_BUILD_VARIANT" = "$3" ] || ( echo "lunch $1: expected TARGET_BUILD_VARIANT='$3', got '$TARGET_BUILD_VARIANT'" && exit 1 ) + [ "$TARGET_PLATFORM_VERSION" = "$4" ] || ( echo "lunch $1: expected TARGET_PLATFORM_VERSION='$4', got '$TARGET_PLATFORM_VERSION'" && exit 1 ) +) + +default_version=$(get_build_var DEFAULT_PLATFORM_VERSION) +valid_version=PPR1 + +# lunch tests +check_lunch "aosp_arm64" "aosp_arm64" "eng" "" +check_lunch "aosp_arm64-userdebug" "aosp_arm64" "userdebug" "" +check_lunch "aosp_arm64-userdebug-$default_version" "aosp_arm64" "userdebug" "$default_version" +check_lunch "aosp_arm64-userdebug-$valid_version" "aosp_arm64" "userdebug" "$valid_version" +check_lunch "abc" "" "" "" +check_lunch "aosp_arm64-abc" "" "" "" +check_lunch "aosp_arm64-userdebug-abc" "" "" "" +check_lunch "aosp_arm64-abc-$valid_version" "" "" "" +check_lunch "abc-userdebug-$valid_version" "" "" "" +check_lunch "-" "" "" "" +check_lunch "--" "" "" "" +check_lunch "-userdebug" "" "" "" +check_lunch "-userdebug-" "" "" "" +check_lunch "-userdebug-$valid_version" "" "" "" +check_lunch "aosp_arm64-userdebug-$valid_version-" "" "" "" +check_lunch "aosp_arm64-userdebug-$valid_version-abc" "" "" "" diff --git a/make/tests/include1.rbc b/make/tests/include1.rbc new file mode 100644 index 0000000..c0c9b3b --- /dev/null +++ b/make/tests/include1.rbc @@ -0,0 +1,25 @@ + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Included file (not inherited) +# Converted from makefile +### PRODUCT_PACKAGES += inc + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + rblf.setdefault(handle, "PRODUCT_PACKAGES") + cfg["PRODUCT_PACKAGES"] += ["inc"] diff --git a/make/tests/input_variables.rbc b/make/tests/input_variables.rbc new file mode 100644 index 0000000..0bb100f --- /dev/null +++ b/make/tests/input_variables.rbc @@ -0,0 +1,28 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file was generated by running `m RBC_PRODUCT_CONFIG=1 nothing` +# and then copying it from out/rbc/out/rbc/make_vars_pre_product_config.rbc. +# It was manually trimmed down afterwards to just the variables we need. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + g["PLATFORM_VERSION_CODENAME"] = "Tiramisu" + g["PLATFORM_VERSION"] = "Tiramisu" + g["TARGET_BUILD_VARIANT"] = "userdebug" + g["TARGET_BUILD_TYPE"] = "release" + g["TARGET_PRODUCT"] = "aosp_arm64" + g["PLATFORM_SDK_VERSION"] = "31" diff --git a/make/tests/part1.rbc b/make/tests/part1.rbc new file mode 100644 index 0000000..ae79d32 --- /dev/null +++ b/make/tests/part1.rbc @@ -0,0 +1,30 @@ + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Part configuration +# Converted from +### PRODUCT_COPY_FILES += part_from:part_to +### PRODUCT_PRODUCT_PROPERTIES += part_properties + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + rblf.setdefault(handle, "PRODUCT_COPY_FILES") + cfg["PRODUCT_COPY_FILES"] += ["part_from:part_to"] + rblf.setdefault(handle, "PRODUCT_PRODUCT_PROPERTIES") + cfg["PRODUCT_PRODUCT_PROPERTIES"] += ["part_properties"] + rblf.soong_config_namespace(g, "NS1") + rblf.soong_config_append(g, "NS1", "v1", "abc_part1") diff --git a/make/tests/prefixed_sort_order/base-secondary.rbc b/make/tests/prefixed_sort_order/base-secondary.rbc new file mode 100644 index 0000000..5446e8f --- /dev/null +++ b/make/tests/prefixed_sort_order/base-secondary.rbc @@ -0,0 +1,21 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + + g.setdefault("MY_VAR", []) + g["MY_VAR"] += ["foo"] diff --git a/make/tests/prefixed_sort_order/base.rbc b/make/tests/prefixed_sort_order/base.rbc new file mode 100644 index 0000000..05b0d5d --- /dev/null +++ b/make/tests/prefixed_sort_order/base.rbc @@ -0,0 +1,21 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + + g.setdefault("MY_VAR", []) + g["MY_VAR"] += ["bar"] diff --git a/make/tests/prefixed_sort_order/product.rbc b/make/tests/prefixed_sort_order/product.rbc new file mode 100644 index 0000000..619b2c0 --- /dev/null +++ b/make/tests/prefixed_sort_order/product.rbc @@ -0,0 +1,29 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load(":base.rbc", _base_init = "init") +load(":base-secondary.rbc", _base_secondary_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + + # It's important that base-secondary uses a dash, an underscore won't expose the sort order issue: + # >>> sorted(["base", "base-secondary"]) + # ['base', 'base-secondary'] + # >>> sorted(["base.mk", "base-secondary.mk"]) + # ['base-secondary.mk', 'base.mk'] + + rblf.inherit(handle, "base", _base_init) + rblf.inherit(handle, "base-secondary", _base_secondary_init) diff --git a/make/tests/prefixed_sort_order/test.rbc b/make/tests/prefixed_sort_order/test.rbc new file mode 100644 index 0000000..e59a509 --- /dev/null +++ b/make/tests/prefixed_sort_order/test.rbc @@ -0,0 +1,26 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load("//build/make/tests/input_variables.rbc", input_variables_init = "init") +load(":product.rbc", "init") + + +def assert_eq(expected, actual): + if expected != actual: + fail("Expected '%s', got '%s'" % (expected, actual)) + +def test(): + (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) + assert_eq(["foo", "bar"], globals["MY_VAR"]) diff --git a/make/tests/product.rbc b/make/tests/product.rbc new file mode 100644 index 0000000..9ae6393 --- /dev/null +++ b/make/tests/product.rbc @@ -0,0 +1,71 @@ + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Top-level test configuration. +# Converted from the following makefile +### PRODUCT_PACKAGES += dev +### PRODUCT_HOST_PACKAGES += host +### $(call inherit-product, $(LOCAL_PATH)/part1.mk) +### PRODUCT_COPY_FILES += device_from:device_to +### include $(LOCAL_PATH)/include1.mk +### PRODUCT_PACKAGES += dev_after +### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz:/etc/xyz +### PRODUCT_COPY_FILES += $(call copy-files,x.xml y.xml,/etc) +### $(call add_soong_config_namespace,NS1) +### $(call soong_config_append,NS1,v1,abc) +### $(call soong_config_append,NS1,v2,def) +### $(call add_soong_config_var_value,NS2,v3,abc) +### $(call soong_config_set,NS2,v3,xyz) + +load("//build/make/core:product_config.rbc", "rblf") +load(":part1.rbc", _part1_init = "init") +load(":include1.rbc", _include1_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + rblf.setdefault(handle, "PRODUCT_PACKAGES") + cfg["PRODUCT_PACKAGES"] += ["dev"] + rblf.setdefault(handle, "PRODUCT_HOST_PACKAGES") + cfg["PRODUCT_HOST_PACKAGES"] += ["host"] + rblf.inherit(handle, "test/part1", _part1_init) + rblf.setdefault(handle, "PRODUCT_COPY_FILES") + cfg["PRODUCT_COPY_FILES"] += ["device_from:device_to"] + _include1_init(g, handle) + cfg["PRODUCT_PACKAGES"] += ["dev_after"] + cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin", "||VENDOR-PATH-PH||/etc") + + ["xyz:/etc/xyz"]) + cfg["PRODUCT_COPY_FILES"] += rblf.copy_files("x.xml y.xml", "/etc") + cfg["PRODUCT_COPY_FILES"] += rblf.copy_files(["from/sub/x", "from/sub/y"], "to") + + rblf.soong_config_namespace(g, "NS1") + rblf.soong_config_append(g, "NS1", "v1", "abc") + rblf.soong_config_append(g, "NS1", "v2", "def") + rblf.soong_config_set(g, "NS2", "v3", "abc") + rblf.soong_config_set(g, "NS2", "v3", "xyz") + + rblf.mkdist_for_goals(g, "goal", "dir1/file1:out1 dir1/file2:out2") + rblf.mkdist_for_goals(g, "goal", "dir2/file2:") + + if rblf.board_platform_in(g, "board1 board2"): + cfg["PRODUCT_PACKAGES"] += ["bad_package"] + g["TARGET_BOARD_PLATFORM"] = "board1" + if rblf.board_platform_in(g, "board1 board2"): + cfg["PRODUCT_PACKAGES"] += ["board1_in"] + if rblf.board_platform_in(g, ["board3","board2"]): + cfg["PRODUCT_PACKAGES"] += ["bad_board_in"] + if rblf.board_platform_is(g, "board1"): + cfg["PRODUCT_PACKAGES"] += ["board1_is"] + if rblf.board_platform_is(g, "board2"): + cfg["PRODUCT_PACKAGES"] += ["bad_board1_is"] diff --git a/make/tests/run.rbc b/make/tests/run.rbc new file mode 100644 index 0000000..2d35e85 --- /dev/null +++ b/make/tests/run.rbc @@ -0,0 +1,154 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Run test product configuration and verify its result. +# The main configuration file is product.rbc. +# It inherits part1.rbc and also includes include1.rbc +# TODO(asmundak): more tests are needed to verify that: +# * multi-level inheritance works as expected +# * all runtime functions (wildcard, regex, etc.) work + +load("//build/make/core:product_config.rbc", "rblf") +load(":input_variables.rbc", input_variables_init = "init") +load(":product.rbc", "init") +load(":board.rbc", board_init = "init") +load(":board_input_vars.rbc", board_input_vars_init = "init") +load("//build/make/tests/single_value_inheritance:test.rbc", test_single_value_inheritance = "test") +load("//build/make/tests/artifact_path_requirements:test.rbc", test_artifact_path_requirements = "test") +load("//build/make/tests/prefixed_sort_order:test.rbc", test_prefixed_sort_order = "test") + +def assert_eq(expected, actual): + if expected != actual: + fail("Expected '%s', got '%s'" % (expected, actual)) + +def assert_dict_subset(expected, actual): + for key, val in expected.items(): + assert_eq(val, actual[key]) + +# Unit tests for non-trivial runtime functions +assert_eq(["a", "b", "c"], rblf.mksort("b a c c")) +assert_eq(["a", "b", "c"], rblf.mksort(["b", "a", "c", "c"])) + +assert_eq("", rblf.mkstrip(" \n \t ")) +assert_eq("a b c", rblf.mkstrip(" a b \n c \t")) +assert_eq(1, rblf.mkstrip(1)) + +assert_eq("b1 b2", rblf.mksubst("a", "b", "a1 a2")) +assert_eq(["b1", "x2"], rblf.mksubst("a", "b", ["a1", "x2"])) + +assert_eq("ABcdYZ", rblf.mkpatsubst("ab%yz", "AB%YZ", "abcdyz")) +assert_eq("bcz", rblf.mkpatsubst("a%z", "A%Z", "bcz")) +assert_eq(["Ay", "Az"], rblf.mkpatsubst("a%", "A%", ["ay", "az"])) +assert_eq("AcZ bcz", rblf.mkpatsubst("a%z", "A%Z", "acz bcz")) +assert_eq("Abcd", rblf.mkpatsubst("a%", "A%", "abcd")) +assert_eq("abcZ", rblf.mkpatsubst("%z", "%Z", "abcz")) +assert_eq("azx b", rblf.mkpatsubst("az", "AZ", "azx b")) +assert_eq(["azx", "b"], rblf.mkpatsubst("az", "AZ", ["azx", "b"])) +assert_eq("ABC", rblf.mkpatsubst("abc", "ABC", "abc")) +assert_eq(["%/foo"], rblf.mkpatsubst("%", "\\%/%", ["foo"])) +assert_eq(["foo/%"], rblf.mkpatsubst("%", "%/%", ["foo"])) +assert_eq(["from/a:to/a", "from/b:to/b"], rblf.product_copy_files_by_pattern("from/%", "to/%", "a b")) + +assert_eq([], rblf.filter(["a", "", "b"], "f")) +assert_eq(["ab%c", "axyzb%c"], rblf.filter(["a%b%c"], ["ab%c", "axyzb%c", "axyzb%cd", "axyzbwc"])) +assert_eq(["abc", "bcd"], rblf.filter(["a%", "b%"], ["abc", "def", "bcd", "xabc"])) +assert_eq(["b", "ab"], rblf.filter_out(["a", "" ], ["a", "", "b", "ab"])) +assert_eq(["c"], rblf.filter_out(["a", "b" ], ["a", "b", "c"])) +assert_eq(["c"], rblf.filter_out(["a%", "b" ], ["abc", "b", "c"])) + +assert_eq("foo.c no_folder", rblf.notdir(["src/foo.c", "no_folder"])) +assert_eq("foo.c no_folder", rblf.notdir("src/foo.c no_folder")) +assert_eq("", rblf.notdir("/")) +assert_eq("", rblf.notdir("")) + +cwd = rblf_shell('pwd') +assert_eq(cwd+"/foo/bar", rblf.abspath("foo/bar")) +assert_eq(cwd+"/bar", rblf.abspath("foo/.././bar")) +assert_eq(cwd+"/bar", rblf.abspath("foo/..////bar//")) +assert_eq("/foo/baz", rblf.abspath("/foo/bar/../baz")) +assert_eq(cwd+"/foo/bar "+cwd+"/foo/baz", rblf.abspath("foo/bar foo/baz")) +assert_eq("/baz", rblf.abspath("/../../../../../../../../../../../../../../../../baz")) + +assert_eq( + ["build/make/tests/board.rbc", "build/make/tests/board_input_vars.rbc"], + rblf.expand_wildcard("build/make/tests/board*.rbc") +) +assert_eq( + ["build/make/tests/run.rbc", "build/make/tests/product.rbc"], + rblf.expand_wildcard("build/make/tests/run.rbc build/make/tests/product.rbc") +) +assert_eq( + ["build/make/tests/run.rbc"], + rblf.expand_wildcard("build/make/tests/run.rbc build/make/tests/nonexistent.rbc") +) + +(globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) +assert_dict_subset({ + "PRODUCTS.test/device.mk.PRODUCT_COPY_FILES": [ + "part_from:part_to", + "device_from:device_to", + "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio/audio_platform_info_noextcodec_snd.xml", + "xyz:/etc/xyz", + "x.xml:/etc/x.xml", + "y.xml:/etc/y.xml", + "from/sub/x:to/x", + "from/sub/y:to/y", + ], + "PRODUCTS.test/device.mk.PRODUCT_HOST_PACKAGES": ["host"], + "PRODUCTS.test/device.mk.PRODUCT_PACKAGES": [ + "dev", + "inc", + "dev_after", + "board1_in", + "board1_is", + ], + "PRODUCTS.test/device.mk.PRODUCT_PRODUCT_PROPERTIES": ["part_properties"] +}, globals) + +ns = globals["$SOONG_CONFIG_NAMESPACES"] +assert_eq( + { + "NS1": { + "v1": "abc abc_part1", + "v2": "def" + }, + "NS2": { + "v3": "xyz" + } + }, + {k:v for k, v in sorted(ns.items()) } +) + +assert_eq("Tiramisu", globals["PLATFORM_VERSION"]) +assert_eq("31", globals["PLATFORM_SDK_VERSION"]) + +assert_eq("xyz", rblf.soong_config_get(globals, "NS2", "v3")) +assert_eq(None, rblf.soong_config_get(globals, "NS2", "nonexistant_var")) + +goals = globals["$dist_for_goals"] +assert_eq( + { + "goal": [("dir1/file1", "out1"), ("dir1/file2", "out2"), ("dir2/file2", "file2")] + }, + { k:v for k,v in sorted(goals.items()) } +) + +(board_globals, board_globals_base) = rblf.board_configuration(board_init, board_input_vars_init) +assert_eq({"A_LIST_VARIABLE": ["foo", "bar"]}, board_globals) +assert_eq({"A_LIST_VARIABLE": ["foo"]}, board_globals_base) + +test_single_value_inheritance() +test_artifact_path_requirements() +test_prefixed_sort_order() diff --git a/make/tests/single_value_inheritance/inherit1.rbc b/make/tests/single_value_inheritance/inherit1.rbc new file mode 100644 index 0000000..0cc98a9 --- /dev/null +++ b/make/tests/single_value_inheritance/inherit1.rbc @@ -0,0 +1,23 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + + cfg["PRODUCT_CHARACTERISTICS"] = "tablet" + cfg["PRODUCT_DEFAULT_DEV_CERTIFICATE"] = "vendor/myvendor/certs/devkeys/devkey" + cfg.setdefault("PRODUCT_PACKAGES", []) + cfg["PRODUCT_PACKAGES"] += ["bar"] diff --git a/make/tests/single_value_inheritance/inherit2.rbc b/make/tests/single_value_inheritance/inherit2.rbc new file mode 100644 index 0000000..ed5e569 --- /dev/null +++ b/make/tests/single_value_inheritance/inherit2.rbc @@ -0,0 +1,22 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + + cfg["PRODUCT_CHARACTERISTICS"] = "nosdcard" + cfg.setdefault("PRODUCT_PACKAGES", []) + cfg["PRODUCT_PACKAGES"] += ["foo"] diff --git a/make/tests/single_value_inheritance/product.rbc b/make/tests/single_value_inheritance/product.rbc new file mode 100644 index 0000000..d090af6 --- /dev/null +++ b/make/tests/single_value_inheritance/product.rbc @@ -0,0 +1,24 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load(":inherit1.rbc", _inherit1_init = "init") +load(":inherit2.rbc", _inherit2_init = "init") + +def init(g, handle): + cfg = rblf.cfg(handle) + rblf.inherit(handle, "test/inherit2", _inherit2_init) + rblf.inherit(handle, "test/inherit1", _inherit1_init) + + cfg["PRODUCT_DEFAULT_DEV_CERTIFICATE"] = "" diff --git a/make/tests/single_value_inheritance/test.rbc b/make/tests/single_value_inheritance/test.rbc new file mode 100644 index 0000000..e4f44f4 --- /dev/null +++ b/make/tests/single_value_inheritance/test.rbc @@ -0,0 +1,28 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/make/core:product_config.rbc", "rblf") +load("//build/make/tests/input_variables.rbc", input_variables_init = "init") +load(":product.rbc", "init") + + +def assert_eq(expected, actual): + if expected != actual: + fail("Expected '%s', got '%s'" % (expected, actual)) + +def test(): + (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) + assert_eq("tablet", globals["PRODUCTS.test/device.mk.PRODUCT_CHARACTERISTICS"]) + assert_eq("vendor/myvendor/certs/devkeys/devkey", globals["PRODUCTS.test/device.mk.PRODUCT_DEFAULT_DEV_CERTIFICATE"]) + assert_eq(["foo", "bar"], globals["PRODUCTS.test/device.mk.PRODUCT_PACKAGES"]) diff --git a/make/tests/version_defaults.rbc b/make/tests/version_defaults.rbc new file mode 100644 index 0000000..9b35b57 --- /dev/null +++ b/make/tests/version_defaults.rbc @@ -0,0 +1,11 @@ +version_defaults = struct( + codenames = { "SP1A" : "S" }, + default_platform_version = "SP1A", + max_platform_version = "SP1A", + min_platform_version = "SP1A", + platform_base_sdk_extension_version = 0, + platform_sdk_extension_version = 1, + platform_sdk_version = 30, + platform_security_patch = "2021-08-05", + platform_version_last_stable = 11, +) diff --git a/make/tools/Android.bp b/make/tools/Android.bp new file mode 100644 index 0000000..6601c60 --- /dev/null +++ b/make/tools/Android.bp @@ -0,0 +1,51 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +python_binary_host { + name: "generate-self-extracting-archive", + srcs: ["generate-self-extracting-archive.py"], +} + +python_binary_host { + name: "post_process_props", + srcs: ["post_process_props.py"], +} + +python_test_host { + name: "post_process_props_unittest", + main: "test_post_process_props.py", + srcs: [ + "post_process_props.py", + "test_post_process_props.py", + ], + test_config: "post_process_props_unittest.xml", + test_suites: ["general-tests"], +} + +python_binary_host { + name: "extract_kernel", + srcs: ["extract_kernel.py"], +} + +genrule_defaults { + name: "extract_kernel_release_defaults", + tools: ["extract_kernel", "lz4"], + out: ["kernel_release.txt"], + cmd: "$(location) --tools lz4:$(location lz4) --input $(in) --output-release > $(out)" +} diff --git a/make/tools/BUILD.bazel b/make/tools/BUILD.bazel new file mode 100644 index 0000000..3170820 --- /dev/null +++ b/make/tools/BUILD.bazel @@ -0,0 +1,20 @@ +py_library( + name="event_log_tags", + srcs = ["event_log_tags.py"], +) + +py_binary( + name="java-event-log-tags", + srcs=["java-event-log-tags.py"], + deps=[":event_log_tags"], + visibility = ["//visibility:public"], + python_version = "PY3", +) + +py_binary( + name="merge-event-log-tags", + srcs=["merge-event-log-tags.py"], + deps=[":event_log_tags"], + visibility = ["//visibility:public"], + python_version = "PY3", +) diff --git a/make/tools/OWNERS b/make/tools/OWNERS new file mode 100644 index 0000000..7d666f1 --- /dev/null +++ b/make/tools/OWNERS @@ -0,0 +1 @@ +per-file warn.py,checkowners.py = chh@google.com diff --git a/make/tools/acp/Android.bp b/make/tools/acp/Android.bp new file mode 100644 index 0000000..47b23b2 --- /dev/null +++ b/make/tools/acp/Android.bp @@ -0,0 +1,19 @@ +// Copyright 2005 The Android Open Source Project +// +// Custom version of cp. + +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary_host { + + srcs: ["acp.c"], + cflags: ["-Wall", "-Werror"], + + static_libs: ["libhost"], + name: "acp", + stl: "none", + +} diff --git a/make/tools/acp/README b/make/tools/acp/README new file mode 100644 index 0000000..a1809d9 --- /dev/null +++ b/make/tools/acp/README @@ -0,0 +1,40 @@ +README for Android "acp" Command + +The "cp" command was judged and found wanting. The issues are: + +Mac OS X: + - Uses the BSD cp, not the fancy GNU cp. It lacks the "-u" flag, which + only copies files if they are newer than the destination. This can + slow the build when copying lots of content. + - Doesn't take the "-d" flag, which causes symlinks to be copied as + links. This is the default behavior, so it's not all bad, but it + complains if you supply "-d". + +MinGW/Cygwin: + - Gets really weird when copying a file called "foo.exe", failing with + "cp: skipping file 'foo.exe', as it was replaced while being copied". + This only seems to happen when the source file is on an NFS/Samba + volume. "cp" works okay copying from local disk. + +Linux: + - On some systems it's possible to have microsecond-accurate timestamps + on an NFS volume, and non-microsecond timestamps on a local volume. + If you copy from NFS to local disk, your NFS files will always be + newer, because the local disk time stamp is truncated rather than + rounded up. This foils the "-u" flag if you also supply the "-p" flag + to preserve timestamps. + - The Darwin linker insists that ranlib be current. If you copy the + library, the time stamp no longer matches. Preserving the time + stamp is essential, so simply turning the "-p" flag off doesn't work. + +Futzing around these in make with GNU make functions is awkward at best. +It's easier and more reliable to write a cp command that works properly. + + +The "acp" command takes most of the standard flags, following the GNU +conventions. It adds a "-e" flag, used when copying executables around. +On most systems it is ignored, but on MinGW/Cygwin it allows "cp foo bar" +to work when what is actually meant is "cp foo.exe bar.exe". Unlike the +default Cygwin cp, "acp foo bar" will not find foo.exe unless you add +the "-e" flag, avoiding potential ambiguity. + diff --git a/make/tools/acp/acp.c b/make/tools/acp/acp.c new file mode 100644 index 0000000..d4a9fbc --- /dev/null +++ b/make/tools/acp/acp.c @@ -0,0 +1,251 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Android "cp" replacement. + * + * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead + * of utime(), and getxattr()/setxattr() instead of chmod(). These are + * probably "better", but are non-portable, and not necessary for our + * purposes. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#define DEBUG_MSGS*/ +#ifdef DEBUG_MSGS +# define DBUG(x) printf x +#else +# define DBUG(x) ((void)0) +#endif + +#define FSSEP '/' /* filename separator char */ + + +/* + * Process the command-line file arguments. + * + * Returns 0 on success. + */ +int process(int argc, char* const argv[], unsigned int options) +{ + int retVal = 0; + int i; + char* stripDest = NULL; + int stripDestLen; + bool destMustBeDir = false; + struct stat sb; + + assert(argc >= 2); + + /* + * Check for and trim a trailing slash on the last arg. + * + * It's useful to be able to say "cp foo bar/" when you want to copy + * a single file into a directory. If you say "cp foo bar", and "bar" + * does not exist, it will create "bar", when what you really wanted + * was for the cp command to fail with "directory does not exist". + */ + stripDestLen = strlen(argv[argc-1]); + stripDest = malloc(stripDestLen+1); + memcpy(stripDest, argv[argc-1], stripDestLen+1); + if (stripDest[stripDestLen-1] == FSSEP) { + stripDest[--stripDestLen] = '\0'; + destMustBeDir = true; + } + + if (argc > 2) + destMustBeDir = true; + + /* + * Start with a quick check to ensure that, if we're expecting to copy + * to a directory, the target already exists and is actually a directory. + * It's okay if it's a symlink to a directory. + * + * If it turns out to be a directory, go ahead and raise the + * destMustBeDir flag so we do some path concatenation below. + */ + if (stat(stripDest, &sb) < 0) { + if (destMustBeDir) { + if (errno == ENOENT) + fprintf(stderr, + "acp: destination directory '%s' does not exist\n", + stripDest); + else + fprintf(stderr, "acp: unable to stat dest dir\n"); + retVal = 1; + goto bail; + } + } else { + if (S_ISDIR(sb.st_mode)) { + DBUG(("--- dest exists and is a dir, setting flag\n")); + destMustBeDir = true; + } else if (destMustBeDir) { + fprintf(stderr, + "acp: destination '%s' is not a directory\n", + stripDest); + retVal = 1; + goto bail; + } + } + + /* + * Copying files. + * + * Strip trailing slashes off. They shouldn't be there, but + * sometimes file completion will put them in for directories. + * + * The observed behavior of GNU and BSD cp is that they print warnings + * if something fails, but continue on. If any part fails, the command + * exits with an error status. + */ + for (i = 0; i < argc-1; i++) { + const char* srcName; + char* src; + char* dst; + int copyResult; + int srcLen; + + /* make a copy of the source name, and strip trailing '/' */ + srcLen = strlen(argv[i]); + src = malloc(srcLen+1); + memcpy(src, argv[i], srcLen+1); + + if (src[srcLen-1] == FSSEP) + src[--srcLen] = '\0'; + + /* find just the name part */ + srcName = strrchr(src, FSSEP); + if (srcName == NULL) { + srcName = src; + } else { + srcName++; + assert(*srcName != '\0'); + } + + if (destMustBeDir) { + /* concatenate dest dir and src name */ + int srcNameLen = strlen(srcName); + + dst = malloc(stripDestLen +1 + srcNameLen +1); + memcpy(dst, stripDest, stripDestLen); + dst[stripDestLen] = FSSEP; + memcpy(dst + stripDestLen+1, srcName, srcNameLen+1); + } else { + /* simple */ + dst = stripDest; + } + + /* + * Copy the source to the destination. + */ + copyResult = copyFile(src, dst, options); + + if (copyResult != 0) + retVal = 1; + + free(src); + if (dst != stripDest) + free(dst); + } + +bail: + free(stripDest); + return retVal; +} + +/* + * Set up the options. + */ +int main(int argc, char* const argv[]) +{ + bool wantUsage; + int ic, retVal; + int verboseLevel; + unsigned int options; + + verboseLevel = 0; + options = 0; + wantUsage = false; + + while (1) { + ic = getopt(argc, argv, "defprtuv"); + if (ic < 0) + break; + + switch (ic) { + case 'd': + options |= COPY_NO_DEREFERENCE; + break; + case 'e': + options |= COPY_TRY_EXE; + break; + case 'f': + options |= COPY_FORCE; + break; + case 'p': + options |= COPY_PERMISSIONS; + break; + case 't': + options |= COPY_TIMESTAMPS; + break; + case 'r': + options |= COPY_RECURSIVE; + break; + case 'u': + options |= COPY_UPDATE_ONLY; + break; + case 'v': + verboseLevel++; + break; + default: + fprintf(stderr, "Unexpected arg -%c\n", ic); + wantUsage = true; + break; + } + + if (wantUsage) + break; + } + + options |= verboseLevel & COPY_VERBOSE_MASK; + + if (optind == argc-1) { + fprintf(stderr, "acp: missing destination file\n"); + return 2; + } else if (optind+2 > argc) + wantUsage = true; + + if (wantUsage) { + fprintf(stderr, "Usage: acp [OPTION]... SOURCE DEST\n"); + fprintf(stderr, " or: acp [OPTION]... SOURCE... DIRECTORY\n"); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -d never follow (dereference) symbolic links\n"); + fprintf(stderr, " -e if source file doesn't exist, try adding " + "'.exe' [Win32 only]\n"); + fprintf(stderr, " -f use force, removing existing file if it's " + "not writeable\n"); + fprintf(stderr, " -p preserve mode, ownership\n"); + fprintf(stderr, " -r recursive copy\n"); + fprintf(stderr, " -t preserve timestamps\n"); + fprintf(stderr, " -u update only: don't copy if dest is newer\n"); + fprintf(stderr, " -v verbose output (-vv is more verbose)\n"); + return 2; + } + + retVal = process(argc-optind, argv+optind, options); + DBUG(("EXIT: %d\n", retVal)); + return retVal; +} + diff --git a/make/tools/apicheck/Android.bp b/make/tools/apicheck/Android.bp new file mode 100644 index 0000000..f58042f --- /dev/null +++ b/make/tools/apicheck/Android.bp @@ -0,0 +1,26 @@ +// Copyright (C) 2008 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_binary_host { + name: "apicheck", + wrapper: "etc/apicheck", + static_libs: [ + "doclava", + "jsilver", + ], +} diff --git a/make/tools/apicheck/etc/apicheck b/make/tools/apicheck/etc/apicheck new file mode 100644 index 0000000..e4ffb77 --- /dev/null +++ b/make/tools/apicheck/etc/apicheck @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Copyright (C) 2005, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +# +# The classpath and other java options used in apicheck are specified in +# build/make/core/tasks/apicheck.mk. + +prog="$0" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +cd "${oldwd}" + +javaOpts="" +while expr "x$1" : 'x-J' >/dev/null; do + opt=`expr "x$1" : 'x-J\(.*\)'` + javaOpts="${javaOpts} -${opt}" + shift +done + +exec java $javaOpts com.google.doclava.apicheck.ApiCheck "$@" diff --git a/make/tools/atree/Android.bp b/make/tools/atree/Android.bp new file mode 100644 index 0000000..fdae3e0 --- /dev/null +++ b/make/tools/atree/Android.bp @@ -0,0 +1,19 @@ +// Copyright 2007 The Android Open Source Project +// +// Copies files into the directory structure described by a manifest + +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary_host { + name: "atree", + srcs: [ + "atree.cpp", + "files.cpp", + "fs.cpp", + ], + cflags: ["-Wall", "-Werror"], + static_libs: ["libhost"], +} diff --git a/make/tools/atree/atree.cpp b/make/tools/atree/atree.cpp new file mode 100644 index 0000000..7deca7e --- /dev/null +++ b/make/tools/atree/atree.cpp @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include "options.h" +#include "files.h" +#include "fs.h" +#include +#include +#include + +using namespace std; + +bool g_debug = getenv("ATREE_DEBUG") != NULL; +vector g_listFiles; +vector g_inputBases; +map g_variables; +string g_outputBase; +string g_dependency; +bool g_useHardLinks = false; + +const char* USAGE = +"\n" +"Usage: atree OPTIONS\n" +"\n" +"Options:\n" +" -f FILELIST Specify one or more files containing the\n" +" list of files to copy.\n" +" -I INPUTDIR Specify one or more base directories in\n" +" which to look for the files\n" +" -o OUTPUTDIR Specify the directory to copy all of the\n" +" output files to.\n" +" -l Use hard links instead of copying the files.\n" +" -m DEPENDENCY Output a make-formatted file containing the list.\n" +" of files included. It sets the variable ATREE_FILES.\n" +" -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n" +" -d Verbose debug mode.\n" +"\n" +"FILELIST file format:\n" +" The FILELIST files contain the list of files that will end up\n" +" in the final OUTPUTDIR. Atree will look for files in the INPUTDIR\n" +" directories in the order they are specified.\n" +"\n" +" In a FILELIST file, comment lines start with a #. Other lines\n" +" are of the format:\n" +"\n" +" [rm|strip] DEST\n" +" SRC [strip] DEST\n" +" -SRCPATTERN\n" +"\n" +" DEST should be path relative to the output directory.\n" +" 'rm DEST' removes the destination file and fails if it's missing.\n" +" 'strip DEST' strips the binary destination file.\n" +" If SRC is supplied, the file names can be different.\n" +" SRCPATTERN is a pattern for the filenames.\n" +"\n"; + +int usage() +{ + fwrite(USAGE, strlen(USAGE), 1, stderr); + return 1; +} + +static bool +add_variable(const char* arg) { + const char* p = arg; + while (*p && *p != '=') p++; + + if (*p == 0 || p == arg || p[1] == 0) { + return false; + } + + ostringstream var; + var << "${" << string(arg, p-arg) << "}"; + g_variables[var.str()] = string(p+1); + return true; +} + +static void +debug_printf(const char* format, ...) +{ + if (g_debug) { + fflush(stderr); + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + fflush(stdout); + } +} + +// Escape the filename so that it can be added to the makefile properly. +static string +escape_filename(const string& name) +{ + ostringstream new_name; + for (string::const_iterator iter = name.begin(); iter != name.end(); ++iter) + { + switch (*iter) + { + case '$': + new_name << "$$"; + break; + default: + new_name << *iter; + break; + } + } + return new_name.str(); +} + +int +main(int argc, char* const* argv) +{ + int err; + bool done = false; + while (!done) { + int opt = getopt(argc, argv, "f:I:o:hlm:v:d"); + switch (opt) + { + case -1: + done = true; + break; + case 'f': + g_listFiles.push_back(string(optarg)); + break; + case 'I': + g_inputBases.push_back(string(optarg)); + break; + case 'o': + if (g_outputBase.length() != 0) { + fprintf(stderr, "%s: -o may only be supplied once -- " + "-o %s\n", argv[0], optarg); + return usage(); + } + g_outputBase = optarg; + break; + case 'l': + g_useHardLinks = true; + break; + case 'm': + if (g_dependency.length() != 0) { + fprintf(stderr, "%s: -m may only be supplied once -- " + "-m %s\n", argv[0], optarg); + return usage(); + } + g_dependency = optarg; + break; + case 'v': + if (!add_variable(optarg)) { + fprintf(stderr, "%s Invalid expression in '-v %s': " + "expected format is '-v VAR=VALUE'.\n", + argv[0], optarg); + return usage(); + } + break; + case 'd': + g_debug = true; + break; + default: + case '?': + case 'h': + return usage(); + } + } + if (optind != argc) { + fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]); + return usage(); + } + + if (g_listFiles.size() == 0) { + fprintf(stderr, "%s: At least one -f option must be supplied.\n", + argv[0]); + return usage(); + } + + if (g_inputBases.size() == 0) { + fprintf(stderr, "%s: At least one -I option must be supplied.\n", + argv[0]); + return usage(); + } + + if (g_outputBase.length() == 0) { + fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]); + return usage(); + } + + +#if 0 + for (vector::iterator it=g_listFiles.begin(); + it!=g_listFiles.end(); it++) { + printf("-f \"%s\"\n", it->c_str()); + } + for (vector::iterator it=g_inputBases.begin(); + it!=g_inputBases.end(); it++) { + printf("-I \"%s\"\n", it->c_str()); + } + printf("-o \"%s\"\n", g_outputBase.c_str()); + if (g_useHardLinks) { + printf("-l\n"); + } +#endif + + vector files; + vector more; + vector excludes; + set directories; + set deleted; + + // read file lists + for (vector::iterator it=g_listFiles.begin(); + it!=g_listFiles.end(); it++) { + err = read_list_file(*it, g_variables, &files, &excludes); + if (err != 0) { + return err; + } + } + + // look for input files + err = 0; + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + err |= locate(&(*it), g_inputBases); + } + + // expand the directories that we should copy into a list of files + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + if (it->sourceIsDir) { + err |= list_dir(*it, excludes, &more); + } + } + for (vector::iterator it=more.begin(); + it!=more.end(); it++) { + files.push_back(*it); + } + + // get the name and modtime of the output files + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + stat_out(g_outputBase, &(*it)); + } + + if (err != 0) { + return 1; + } + + // gather directories + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + if (it->sourceIsDir) { + directories.insert(it->outPath); + } else { + string s = dir_part(it->outPath); + if (s != ".") { + directories.insert(s); + } + } + } + + // gather files that should become directores + // and directories that should become files + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) { + deleted.insert(it->outPath); + } + } + + // delete files + for (set::iterator it=deleted.begin(); + it!=deleted.end(); it++) { + debug_printf("deleting %s\n", it->c_str()); + err = remove_recursively(*it); + if (err != 0) { + return err; + } + } + + // remove all files or directories as requested from the input atree file. + // must be done before create new directories. + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + if (!it->sourceIsDir) { + if (it->fileOp == FILE_OP_REMOVE && + deleted.count(it->outPath) == 0) { + debug_printf("remove %s\n", it->outPath.c_str()); + err = remove_recursively(it->outPath); + if (err != 0) { + return err; + } + } + } + } + + // make directories + for (set::iterator it=directories.begin(); + it!=directories.end(); it++) { + debug_printf("mkdir %s\n", it->c_str()); + err = mkdir_recursively(*it); + if (err != 0) { + return err; + } + } + + // copy (or link) files that are newer or of different size + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + if (!it->sourceIsDir) { + if (it->fileOp == FILE_OP_REMOVE) { + continue; + } + + debug_printf("copy %s(%ld) ==> %s(%ld)", + it->sourcePath.c_str(), it->sourceMod, + it->outPath.c_str(), it->outMod); + + if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) { + err = copy_file(it->sourcePath, it->outPath); + debug_printf(" done.\n"); + if (err != 0) { + return err; + } + } else { + debug_printf(" skipping.\n"); + } + + if (it->fileOp == FILE_OP_STRIP) { + debug_printf("strip %s\n", it->outPath.c_str()); + err = strip_file(it->outPath); + if (err != 0) { + return err; + } + } + } + } + + // output the dependency file + if (g_dependency.length() != 0) { + FILE *f = fopen(g_dependency.c_str(), "w"); + if (f != NULL) { + fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n"); + for (vector::iterator it=files.begin(); + it!=files.end(); it++) { + if (!it->sourceIsDir) { + fprintf(f, "%s \\\n", + escape_filename(it->sourcePath).c_str()); + } + } + fprintf(f, "\n"); + fclose(f); + } else { + fprintf(stderr, "error opening manifest file for write: %s\n", + g_dependency.c_str()); + } + } + + return 0; +} diff --git a/make/tools/atree/files.cpp b/make/tools/atree/files.cpp new file mode 100644 index 0000000..b90f8b3 --- /dev/null +++ b/make/tools/atree/files.cpp @@ -0,0 +1,473 @@ +#include "files.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool +is_comment_line(const char* p) +{ + while (*p && isspace(*p)) { + p++; + } + return *p == '#'; +} + +static string +path_append(const string& base, const string& leaf) +{ + string full = base; + if (base.length() > 0 && leaf.length() > 0) { + full += '/'; + } + full += leaf; + return full; +} + +static bool +is_whitespace_line(const char* p) +{ + while (*p) { + if (!isspace(*p)) { + return false; + } + p++; + } + return true; +} + +static bool +is_exclude_line(const char* p) { + while (*p) { + if (*p == '-') { + return true; + } + else if (isspace(*p)) { + p++; + } + else { + return false; + } + } + return false; +} + +void +split_line(const char* p, vector* out) +{ + const char* q = p; + enum { WHITE, TEXT, IN_QUOTE } state = WHITE; + while (*p) { + if (*p == '#') { + break; + } + + switch (state) + { + case WHITE: + if (!isspace(*p)) { + q = p; + state = (*p == '"') ? IN_QUOTE : TEXT; + } + break; + case IN_QUOTE: + if (*p == '"') { + state = TEXT; + break; + } + [[fallthrough]]; + case TEXT: + if (state != IN_QUOTE && isspace(*p)) { + if (q != p) { + const char* start = q; + size_t len = p-q; + if (len > 2 && *start == '"' && start[len - 1] == '"') { + start++; + len -= 2; + } + out->push_back(string(start, len)); + } + state = WHITE; + } + break; + } + p++; + } + if (state == TEXT) { + const char* start = q; + size_t len = p-q; + if (len > 2 && *start == '"' && start[len - 1] == '"') { + start++; + len -= 2; + } + out->push_back(string(start, len)); + } +} + +static void +add_file(vector* files, const FileOpType fileOp, + const string& listFile, int listLine, + const string& sourceName, const string& outName) +{ + FileRecord rec; + rec.listFile = listFile; + rec.listLine = listLine; + rec.fileOp = fileOp; + rec.sourceName = sourceName; + rec.outName = outName; + files->push_back(rec); +} + +static string +replace_variables(const string& input, + const map& variables, + bool* error) { + if (variables.empty()) { + return input; + } + + // Abort if the variable prefix is not found + if (input.find("${") == string::npos) { + return input; + } + + string result = input; + + // Note: rather than be fancy to detect recursive replacements, + // we simply iterate till a given threshold is met. + + int retries = 1000; + bool did_replace; + + do { + did_replace = false; + for (map::const_iterator it = variables.begin(); + it != variables.end(); ++it) { + string::size_type pos = 0; + while((pos = result.find(it->first, pos)) != string::npos) { + result = result.replace(pos, it->first.length(), it->second); + pos += it->second.length(); + did_replace = true; + } + } + if (did_replace && --retries == 0) { + *error = true; + fprintf(stderr, "Recursive replacement detected during variables " + "substitution. Full list of variables is: "); + + for (map::const_iterator it = variables.begin(); + it != variables.end(); ++it) { + fprintf(stderr, " %s=%s\n", + it->first.c_str(), it->second.c_str()); + } + + return result; + } + } while (did_replace); + + return result; +} + +int +read_list_file(const string& filename, + const map& variables, + vector* files, + vector* excludes) +{ + int err = 0; + FILE* f = NULL; + long size; + char* buf = NULL; + char *p, *q; + int i, lineCount; + + f = fopen(filename.c_str(), "r"); + if (f == NULL) { + fprintf(stderr, "Could not open list file (%s): %s\n", + filename.c_str(), strerror(errno)); + err = errno; + goto cleanup; + } + + err = fseek(f, 0, SEEK_END); + if (err != 0) { + fprintf(stderr, "Could not seek to the end of file %s. (%s)\n", + filename.c_str(), strerror(errno)); + err = errno; + goto cleanup; + } + + size = ftell(f); + + err = fseek(f, 0, SEEK_SET); + if (err != 0) { + fprintf(stderr, "Could not seek to the beginning of file %s. (%s)\n", + filename.c_str(), strerror(errno)); + err = errno; + goto cleanup; + } + + buf = (char*)malloc(size+1); + if (buf == NULL) { + // (potentially large) + fprintf(stderr, "out of memory (%ld)\n", size); + err = ENOMEM; + goto cleanup; + } + + if (1 != fread(buf, size, 1, f)) { + fprintf(stderr, "error reading file %s. (%s)\n", + filename.c_str(), strerror(errno)); + err = errno; + goto cleanup; + } + + // split on lines + p = buf; + q = buf+size; + lineCount = 0; + while (ppush_back(string(p)); + } + else { + vector words; + + split_line(p, &words); + +#if 0 + printf("[ "); + for (size_t k=0; k::iterator it = words.begin(); it != words.end(); ++it) { + const string& word = *it; + if (word == "rm") { + if (op != FILE_OP_COPY) { + errstr = "Error: you can only specifiy 'rm' or 'strip' once per line."; + break; + } + op = FILE_OP_REMOVE; + } else if (word == "strip") { + if (op != FILE_OP_COPY) { + errstr = "Error: you can only specifiy 'rm' or 'strip' once per line."; + break; + } + op = FILE_OP_STRIP; + } else if (pcount < 2) { + bool error = false; + paths[pcount++] = replace_variables(word, variables, &error); + if (error) { + err = 1; + goto cleanup; + } + } else { + errstr = "Error: More than 2 paths per line."; + break; + } + } + + if (pcount == 0 && !errstr.empty()) { + errstr = "Error: No path found on line."; + } + + if (!errstr.empty()) { + fprintf(stderr, "%s:%d: bad format: %s\n%s\nExpected: [SRC] [rm|strip] DEST\n", + filename.c_str(), i+1, p, errstr.c_str()); + err = 1; + } else { + if (pcount == 1) { + // pattern: [rm|strip] DEST + paths[1] = paths[0]; + } + + add_file(files, op, filename, i+1, paths[0], paths[1]); + } + } + p = q; + } + +cleanup: + if (buf != NULL) { + free(buf); + } + if (f != NULL) { + fclose(f); + } + return err; +} + + +int +locate(FileRecord* rec, const vector& search) +{ + if (rec->fileOp == FILE_OP_REMOVE) { + // Don't touch source files when removing a destination. + rec->sourceMod = 0; + rec->sourceSize = 0; + rec->sourceIsDir = false; + return 0; + } + + int err; + + for (vector::const_iterator it=search.begin(); + it!=search.end(); it++) { + string full = path_append(*it, rec->sourceName); + struct stat st; + err = stat(full.c_str(), &st); + if (err == 0) { + rec->sourceBase = *it; + rec->sourcePath = full; + rec->sourceMod = st.st_mtime; + rec->sourceSize = st.st_size; + rec->sourceIsDir = S_ISDIR(st.st_mode); + return 0; + } + } + + fprintf(stderr, "%s:%d: couldn't locate source file: %s\n", + rec->listFile.c_str(), rec->listLine, rec->sourceName.c_str()); + return 1; +} + +void +stat_out(const string& base, FileRecord* rec) +{ + rec->outPath = path_append(base, rec->outName); + + int err; + struct stat st; + err = stat(rec->outPath.c_str(), &st); + if (err == 0) { + rec->outMod = st.st_mtime; + rec->outSize = st.st_size; + rec->outIsDir = S_ISDIR(st.st_mode); + } else { + rec->outMod = 0; + rec->outSize = 0; + rec->outIsDir = false; + } +} + +string +dir_part(const string& filename) +{ + int pos = filename.rfind('/'); + if (pos <= 0) { + return "."; + } + return filename.substr(0, pos); +} + +static void +add_more(const string& entry, bool isDir, + const FileRecord& rec, vector*more) +{ + FileRecord r; + r.listFile = rec.listFile; + r.listLine = rec.listLine; + r.sourceName = path_append(rec.sourceName, entry); + r.sourcePath = path_append(rec.sourceBase, r.sourceName); + struct stat st; + int err = stat(r.sourcePath.c_str(), &st); + if (err == 0) { + r.sourceMod = st.st_mtime; + } + r.sourceIsDir = isDir; + r.outName = path_append(rec.outName, entry); + more->push_back(r); +} + +static bool +matches_excludes(const char* file, const vector& excludes) +{ + for (vector::const_iterator it=excludes.begin(); + it!=excludes.end(); it++) { + if (0 == fnmatch(it->c_str(), file, FNM_PERIOD)) { + return true; + } + } + return false; +} + +static int +list_dir(const string& path, const FileRecord& rec, + const vector& excludes, + vector* more) +{ + string full = path_append(rec.sourceBase, rec.sourceName); + full = path_append(full, path); + + DIR *d = opendir(full.c_str()); + if (d == NULL) { + return errno; + } + + vector dirs; + + struct dirent *ent; + while (NULL != (ent = readdir(d))) { + if (0 == strcmp(".", ent->d_name) + || 0 == strcmp("..", ent->d_name)) { + continue; + } + if (matches_excludes(ent->d_name, excludes)) { + continue; + } + string entry = path_append(path, ent->d_name); + bool is_directory = (ent->d_type == DT_DIR); + add_more(entry, is_directory, rec, more); + if (is_directory) { + dirs.push_back(entry); + } + } + closedir(d); + + for (vector::iterator it=dirs.begin(); it!=dirs.end(); it++) { + list_dir(*it, rec, excludes, more); + } + + return 0; +} + +int +list_dir(const FileRecord& rec, const vector& excludes, + vector* files) +{ + return list_dir("", rec, excludes, files); +} + +FileRecord::FileRecord() { + fileOp = FILE_OP_COPY; +} + diff --git a/make/tools/atree/files.h b/make/tools/atree/files.h new file mode 100644 index 0000000..f6bf8a6 --- /dev/null +++ b/make/tools/atree/files.h @@ -0,0 +1,50 @@ +#ifndef FILES_H +#define FILES_H + +#include +#include +#include +#include + +using namespace std; + +enum FileOpType { + FILE_OP_COPY = 0, + FILE_OP_REMOVE, + FILE_OP_STRIP +}; + +struct FileRecord +{ + FileRecord(); + + string listFile; + int listLine; + + string sourceBase; + string sourceName; + string sourcePath; + bool sourceIsDir; + time_t sourceMod; + off_t sourceSize; + FileOpType fileOp; + + string outName; + string outPath; + off_t outSize; + time_t outMod; + bool outIsDir; + unsigned int mode; +}; + +int read_list_file(const string& filename, + const map& variables, + vector* files, + vector* excludes); +int locate(FileRecord* rec, const vector& search); +void stat_out(const string& base, FileRecord* rec); +string dir_part(const string& filename); +int list_dir(const FileRecord& rec, const vector& excludes, + vector* files); + +#endif // FILES_H diff --git a/make/tools/atree/fs.cpp b/make/tools/atree/fs.cpp new file mode 100644 index 0000000..6cd080e --- /dev/null +++ b/make/tools/atree/fs.cpp @@ -0,0 +1,212 @@ +#include "fs.h" +#include "files.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static bool +is_dir(const string& path) +{ + int err; + struct stat st; + err = stat(path.c_str(), &st); + return err != 0 || S_ISDIR(st.st_mode); +} + +static int +remove_file(const string& path) +{ + int err = unlink(path.c_str()); + if (err != 0) { + fprintf(stderr, "error deleting file %s (%s)\n", path.c_str(), + strerror(errno)); + return errno; + } + return 0; +} + +int +remove_recursively(const string& path) +{ + int err; + + if (is_dir(path)) { + DIR *d = opendir(path.c_str()); + if (d == NULL) { + fprintf(stderr, "error getting directory contents %s (%s)\n", + path.c_str(), strerror(errno)); + return errno; + } + + vector files; + vector dirs; + + struct dirent *ent; + while (NULL != (ent = readdir(d))) { + if (0 == strcmp(".", ent->d_name) + || 0 == strcmp("..", ent->d_name)) { + continue; + } + string full = path; + full += '/'; + full += ent->d_name; + bool is_directory = (ent->d_type == DT_DIR); + if (is_directory) { + dirs.push_back(full); + } else { + files.push_back(full); + } + } + closedir(d); + + for (vector::iterator it=files.begin(); it!=files.end(); it++) { + err = remove_file(*it); + if (err != 0) { + return err; + } + } + + for (vector::iterator it=dirs.begin(); it!=dirs.end(); it++) { + err = remove_recursively(*it); + if (err != 0) { + return err; + } + } + + err = rmdir(path.c_str()); + if (err != 0) { + fprintf(stderr, "error deleting directory %s (%s)\n", path.c_str(), + strerror(errno)); + return errno; + } + return 0; + } else { + return remove_file(path); + } +} + +int +mkdir_recursively(const string& path) +{ + int err; + size_t pos = 0; + // For absolute pathnames, that starts with leading '/' + // use appropriate initial value. + if (path.length() != 0 and path[0] == '/') pos++; + + while (true) { + pos = path.find('/', pos); + string p = path.substr(0, pos); + struct stat st; + err = stat(p.c_str(), &st); + if (err != 0) { + err = mkdir(p.c_str(), 0770); + if (err != 0) { + fprintf(stderr, "can't create directory %s (%s)\n", + path.c_str(), strerror(errno)); + return errno; + } + } + else if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "can't create directory %s because %s is a file.\n", + path.c_str(), p.c_str()); + return 1; + } + pos++; + if (p == path) { + return 0; + } + } +} + +int +copy_file(const string& src, const string& dst) +{ + int err; + + err = copyFile(src.c_str(), dst.c_str(), + COPY_NO_DEREFERENCE | COPY_FORCE | COPY_PERMISSIONS); + return err; +} + +int +strip_file(const string& path) +{ + // Default strip command to run is "strip" unless overridden by the ATREE_STRIP env var. + const char* strip_cmd = getenv("ATREE_STRIP"); + if (!strip_cmd || !strip_cmd[0]) { + strip_cmd = "strip"; + } + pid_t pid = fork(); + if (pid == -1) { + // Fork failed. errno should be set. + return -1; + } else if (pid == 0) { + // Exec in the child. Only returns if execve failed. + + int num_args = 0; + const char *s = strip_cmd; + while (*s) { + while (*s == ' ') ++s; + if (*s && *s != ' ') { + ++num_args; + while (*s && *s != ' ') ++s; + } + } + + if (num_args <= 0) { + fprintf(stderr, "Invalid ATREE_STRIP command '%s'\n", strip_cmd); + return 1; + + } else if (num_args == 1) { + return execlp(strip_cmd, strip_cmd, path.c_str(), (char *)NULL); + + } else { + // Split the arguments if more than 1 + char* cmd = strdup(strip_cmd); + const char** args = (const char**) malloc(sizeof(const char*) * (num_args + 2)); + + const char** curr = args; + char* s = cmd; + while (*s) { + while (*s == ' ') ++s; + if (*s && *s != ' ') { + *curr = s; + ++curr; + while (*s && *s != ' ') ++s; + if (*s) { + *s = '\0'; + ++s; + } + } + } + + args[num_args] = path.c_str(); + args[num_args + 1] = NULL; + + int ret = execvp(args[0], (char* const*)args); + free(args); + free(cmd); + return ret; + } + } else { + // Wait for child pid and return its exit code. + int status; + waitpid(pid, &status, 0); + return status; + } +} + diff --git a/make/tools/atree/fs.h b/make/tools/atree/fs.h new file mode 100644 index 0000000..fd4ae3e --- /dev/null +++ b/make/tools/atree/fs.h @@ -0,0 +1,13 @@ +#ifndef FS_H +#define FS_H + +#include + +using namespace std; + +int remove_recursively(const string& path); +int mkdir_recursively(const string& path); +int copy_file(const string& src, const string& dst); +int strip_file(const string& path); + +#endif // FS_H diff --git a/make/tools/atree/options.h b/make/tools/atree/options.h new file mode 100644 index 0000000..a227d0f --- /dev/null +++ b/make/tools/atree/options.h @@ -0,0 +1,14 @@ +#ifndef OPTIONS_H +#define OPTIONS_H + +#include +#include + +using namespace std; + +extern vector g_listFiles; +extern vector g_inputBases; +extern string g_outputBase; +extern bool g_useHardLinks; + +#endif // OPTIONS_H diff --git a/make/tools/auto_gen_test_config.py b/make/tools/auto_gen_test_config.py new file mode 100755 index 0000000..943f238 --- /dev/null +++ b/make/tools/auto_gen_test_config.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A tool to generate TradeFed test config file. +""" + +import os +import shutil +import sys +from xml.dom.minidom import parse + +ATTRIBUTE_LABEL = 'android:label' +ATTRIBUTE_RUNNER = 'android:name' +ATTRIBUTE_PACKAGE = 'package' + +PLACEHOLDER_LABEL = '{LABEL}' +PLACEHOLDER_EXTRA_CONFIGS = '{EXTRA_CONFIGS}' +PLACEHOLDER_MODULE = '{MODULE}' +PLACEHOLDER_PACKAGE = '{PACKAGE}' +PLACEHOLDER_RUNNER = '{RUNNER}' +PLACEHOLDER_TEST_TYPE = '{TEST_TYPE}' + + +def main(argv): + """Entry point of auto_gen_test_config. + + Args: + argv: A list of arguments. + Returns: + 0 if no error, otherwise 1. + """ + if len(argv) != 4 and len(argv) != 6: + sys.stderr.write( + 'Invalid arguments. The script requires 4 arguments for file paths: ' + 'target_config android_manifest empty_config ' + 'instrumentation_test_config_template ' + 'and 2 optional arguments for extra configs: ' + '--extra-configs \'EXTRA_CONFIGS\'.\n') + return 1 + + target_config = argv[0] + android_manifest = argv[1] + empty_config = argv[2] + instrumentation_test_config_template = argv[3] + extra_configs = '\n'.join(argv[5].split('\\n')) if len(argv) == 6 else '' + + manifest = parse(android_manifest) + instrumentation_elements = manifest.getElementsByTagName('instrumentation') + manifest_elements = manifest.getElementsByTagName('manifest') + if len(instrumentation_elements) != 1 or len(manifest_elements) != 1: + # Failed to locate instrumentation or manifest element in AndroidManifest. + # file. Empty test config file will be created. + shutil.copyfile(empty_config, target_config) + return 0 + + module = os.path.splitext(os.path.basename(target_config))[0] + instrumentation = instrumentation_elements[0] + manifest = manifest_elements[0] + if instrumentation.attributes.has_key(ATTRIBUTE_LABEL): + label = instrumentation.attributes[ATTRIBUTE_LABEL].value + else: + label = module + runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value + package = manifest.attributes[ATTRIBUTE_PACKAGE].value + test_type = ('InstrumentationTest' + if runner.endswith('.InstrumentationTestRunner') + else 'AndroidJUnitTest') + + with open(instrumentation_test_config_template) as template: + config = template.read() + config = config.replace(PLACEHOLDER_LABEL, label) + config = config.replace(PLACEHOLDER_MODULE, module) + config = config.replace(PLACEHOLDER_PACKAGE, package) + config = config.replace(PLACEHOLDER_TEST_TYPE, test_type) + config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs) + config = config.replace(PLACEHOLDER_RUNNER, runner) + with open(target_config, 'w') as config_file: + config_file.write(config) + return 0 + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/make/tools/auto_gen_test_config_test.py b/make/tools/auto_gen_test_config_test.py new file mode 100644 index 0000000..51a8583 --- /dev/null +++ b/make/tools/auto_gen_test_config_test.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# +# Copyright 2017, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittests for auto_gen_test_config.""" + +import os +import shutil +import tempfile +import unittest + +import auto_gen_test_config + +TEST_MODULE = 'TestModule' + +MANIFEST_INVALID = """ + + +""" + +MANIFEST_JUNIT_TEST = """ + + + +""" + +MANIFEST_INSTRUMENTATION_TEST = """ + + + +""" + +EXPECTED_JUNIT_TEST_CONFIG = """ + + + + + + + + + +""" + +EXPECTED_INSTRUMENTATION_TEST_CONFIG = """ + + + + + + + + + +""" + +TOOLS_DIR = os.path.dirname(os.path.dirname(__file__)) +EMPTY_TEST_CONFIG = os.path.join( + TOOLS_DIR, '..', 'core', 'empty_test_config.xml') +INSTRUMENTATION_TEST_CONFIG_TEMPLATE = os.path.join( + TOOLS_DIR, '..', 'core', 'instrumentation_test_config_template.xml') + + +class AutoGenTestConfigUnittests(unittest.TestCase): + """Unittests for auto_gen_test_config.""" + + def setUp(self): + """Setup directory for test.""" + self.test_dir = tempfile.mkdtemp() + self.config_file = os.path.join(self.test_dir, TEST_MODULE + '.config') + self.manifest_file = os.path.join(self.test_dir, 'AndroidManifest.xml') + + def tearDown(self): + """Cleanup the test directory.""" + shutil.rmtree(self.test_dir, ignore_errors=True) + + def testInvalidManifest(self): + """An empty test config should be generated if AndroidManifest is invalid. + """ + with open(self.manifest_file, 'w') as f: + f.write(MANIFEST_INVALID) + + argv = [self.config_file, + self.manifest_file, + EMPTY_TEST_CONFIG, + INSTRUMENTATION_TEST_CONFIG_TEMPLATE] + auto_gen_test_config.main(argv) + with open(self.config_file) as config_file: + with open(EMPTY_TEST_CONFIG) as empty_config: + self.assertEqual(config_file.read(), empty_config.read()) + + def testCreateJUnitTestConfig(self): + """Test creating test config for AndroidJUnitTest. + """ + with open(self.manifest_file, 'w') as f: + f.write(MANIFEST_JUNIT_TEST) + + argv = [self.config_file, + self.manifest_file, + EMPTY_TEST_CONFIG, + INSTRUMENTATION_TEST_CONFIG_TEMPLATE] + auto_gen_test_config.main(argv) + with open(self.config_file) as config_file: + self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG) + + def testCreateInstrumentationTestConfig(self): + """Test creating test config for InstrumentationTest. + """ + with open(self.manifest_file, 'w') as f: + f.write(MANIFEST_INSTRUMENTATION_TEST) + + argv = [self.config_file, + self.manifest_file, + EMPTY_TEST_CONFIG, + INSTRUMENTATION_TEST_CONFIG_TEMPLATE] + auto_gen_test_config.main(argv) + with open(self.config_file) as config_file: + self.assertEqual( + config_file.read(), EXPECTED_INSTRUMENTATION_TEST_CONFIG) + +if __name__ == '__main__': + unittest.main() diff --git a/make/tools/brillo-clang-format b/make/tools/brillo-clang-format new file mode 100644 index 0000000..a69d9d2 --- /dev/null +++ b/make/tools/brillo-clang-format @@ -0,0 +1,37 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +### DO NOT COPY THIS FILE TO YOUR PROJECT. ### + +# +# This is the .clang-format file used by all Brillo projects, conforming to the +# style guide defined by Brillo. To use this file create a *relative* symlink in +# your project pointing to this file, as this repository is expected to be +# present in all manifests. +# +# See go/brillo-c++-style for details about the style guide. +# + +BasedOnStyle: Google +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackArguments: false +BinPackParameters: false +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +PointerAlignment: Left +TabWidth: 2 diff --git a/make/tools/buildinfo.sh b/make/tools/buildinfo.sh new file mode 100755 index 0000000..536a381 --- /dev/null +++ b/make/tools/buildinfo.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +echo "# begin build properties" +echo "# autogenerated by buildinfo.sh" + +# The ro.build.id will be set dynamically by init, by appending the unique vbmeta digest. +if [ "$BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT" = "true" ] ; then + echo "ro.build.legacy.id=$BUILD_ID" +else + echo "ro.build.id=$BUILD_ID" +fi +echo "ro.build.display.id=$BUILD_DISPLAY_ID" +echo "ro.build.version.incremental=$BUILD_NUMBER" +echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION" +echo "ro.build.version.preview_sdk=$PLATFORM_PREVIEW_SDK_VERSION" +echo "ro.build.version.preview_sdk_fingerprint=$PLATFORM_PREVIEW_SDK_FINGERPRINT" +echo "ro.build.version.codename=$PLATFORM_VERSION_CODENAME" +echo "ro.build.version.all_codenames=$PLATFORM_VERSION_ALL_CODENAMES" +echo "ro.build.version.known_codenames=$PLATFORM_VERSION_KNOWN_CODENAMES" +echo "ro.build.version.release=$PLATFORM_VERSION_LAST_STABLE" +echo "ro.build.version.release_or_codename=$PLATFORM_VERSION" +echo "ro.build.version.release_or_preview_display=$PLATFORM_DISPLAY_VERSION" +echo "ro.build.version.security_patch=$PLATFORM_SECURITY_PATCH" +echo "ro.build.version.base_os=$PLATFORM_BASE_OS" +echo "ro.build.version.min_supported_target_sdk=$PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION" +echo "ro.build.date=`$DATE`" +echo "ro.build.date.utc=`$DATE +%s`" +echo "ro.build.type=$TARGET_BUILD_TYPE" +echo "ro.build.user=$BUILD_USERNAME" +echo "ro.build.host=$BUILD_HOSTNAME" +echo "ro.build.tags=$BUILD_VERSION_TAGS" +echo "ro.build.flavor=$TARGET_BUILD_FLAVOR" +if [ -n "$BOARD_BUILD_SYSTEM_ROOT_IMAGE" ] ; then + echo "ro.build.system_root_image=$BOARD_BUILD_SYSTEM_ROOT_IMAGE" +fi + +# These values are deprecated, use "ro.product.cpu.abilist" +# instead (see below). +echo "# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete," +echo "# use ro.product.cpu.abilist instead." +echo "ro.product.cpu.abi=$TARGET_CPU_ABI" +if [ -n "$TARGET_CPU_ABI2" ] ; then + echo "ro.product.cpu.abi2=$TARGET_CPU_ABI2" +fi + +if [ -n "$PRODUCT_DEFAULT_LOCALE" ] ; then + echo "ro.product.locale=$PRODUCT_DEFAULT_LOCALE" +fi +echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS" + +echo "# ro.build.product is obsolete; use ro.product.device" +echo "ro.build.product=$TARGET_DEVICE" + +echo "# Do not try to parse description or thumbprint" +echo "ro.build.description=$PRIVATE_BUILD_DESC" +if [ -n "$BUILD_THUMBPRINT" ] ; then + echo "ro.build.thumbprint=$BUILD_THUMBPRINT" +fi + +echo "# end build properties" diff --git a/make/tools/canoninja/README.md b/make/tools/canoninja/README.md new file mode 100644 index 0000000..506acf7 --- /dev/null +++ b/make/tools/canoninja/README.md @@ -0,0 +1,151 @@ +# Ninja File Canonicalizer + +Suppose we have a tool that generates a Ninja file from some other description (think Kati and makefiles), and during +the testing we discovered a regression. Furthermore, suppose that the generated Ninja file is large (think millions of +lines). And, the new Ninja file has build statements and rules in a slightly different order. As the tool generates the +rule names, the real differences in the output of the `diff` command are drowned in noise. Enter Canoninja. + +Canoninja renames each Ninja rule to the hash of its contents. After that, we can just sort the build statements, and a +simple `comm` command immediately reveal the essential difference between the files. + +## Example + +Consider the following makefile + +```makefile +second := +first: foo +foo: + @echo foo +second: bar +bar: + @echo bar +``` + +Depending on Kati version converting it to Ninja file will yield either: + +``` +$ cat /tmp/1.ninja +# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb + +pool local_pool + depth = 72 + +build _kati_always_build_: phony + +build first: phony foo +rule rule0 + description = build $out + command = /bin/sh -c "echo foo" +build foo: rule0 +build second: phony bar +rule rule1 + description = build $out + command = /bin/sh -c "echo bar" +build bar: rule1 + +default first +``` + +or + +``` +$ cat 2.ninja +# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310 + +pool local_pool + depth = 72 + +build _kati_always_build_: phony + +build second: phony bar +rule rule0 + description = build $out + command = /bin/sh -c "echo bar" +build bar: rule0 +build first: phony foo +rule rule1 + description = build $out + command = /bin/sh -c "echo foo" +build foo: rule1 + +default first +``` + +This is a quirk in Kati, see https://github.com/google/kati/issues/238 + +Trying to find out the difference between the targets even after sorting them isn't too helpful: + +``` +diff <(grep '^build' /tmp/1.ninja|sort) <(grep '^build' /tmp/2.ninja | sort) +1c1 +< build bar: rule1 +--- +> build bar: rule0 +3c3 +< build foo: rule0 +--- +> build foo: rule1 +``` + +However, running these files through `canoninja` yields + +``` +$ canoninja /tmp/1.ninja +# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb + +pool local_pool + depth = 72 + +build _kati_always_build_: phony + +build first: phony foo +rule R2f9981d3c152fc255370dc67028244f7bed72a03 + description = build $out + command = /bin/sh -c "echo foo" +build foo: R2f9981d3c152fc255370dc67028244f7bed72a03 +build second: phony bar +rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c + description = build $out + command = /bin/sh -c "echo bar" +build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c + +default first +``` + +and + +``` +~/go/bin/canoninja /tmp/2.ninja +# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310 + +pool local_pool + depth = 72 + +build _kati_always_build_: phony + +build second: phony bar +rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c + description = build $out + command = /bin/sh -c "echo bar" +build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c +build first: phony foo +rule R2f9981d3c152fc255370dc67028244f7bed72a03 + description = build $out + command = /bin/sh -c "echo foo" +build foo: R2f9981d3c152fc255370dc67028244f7bed72a03 + +default first +``` + +and when we extract only build statements and sort them, we see that both Ninja files define the same graph: + +```shell +$ diff <(~/go/bin/canoninja /tmp/1.ninja | grep '^build' | sort) \ + <(~/go/bin/canoninja /tmp/2.ninja | grep '^build' | sort) +``` + +# Todo + +* Optionally output only the build statements, optionally sorted +* Handle continuation lines correctly diff --git a/make/tools/canoninja/canoninja.go b/make/tools/canoninja/canoninja.go new file mode 100644 index 0000000..681a694 --- /dev/null +++ b/make/tools/canoninja/canoninja.go @@ -0,0 +1,130 @@ +package canoninja + +import ( + "bytes" + "crypto/sha1" + "encoding/hex" + "fmt" + "io" +) + +var ( + rulePrefix = []byte("rule ") + buildPrefix = []byte("build ") + phonyRule = []byte("phony") +) + +func Generate(path string, buffer []byte, sink io.Writer) error { + // Break file into lines + from := 0 + var lines [][]byte + for from < len(buffer) { + line := getLine(buffer[from:]) + lines = append(lines, line) + from += len(line) + } + + // FOr each rule, calculate and remember its digest + ruleDigest := make(map[string]string) + for i := 0; i < len(lines); { + if bytes.HasPrefix(lines[i], rulePrefix) { + // Find ruleName + rn := ruleName(lines[i]) + if len(rn) == 0 { + return fmt.Errorf("%s:%d: rule name is missing or on the next line", path, i+1) + } + sRuleName := string(rn) + if _, ok := ruleDigest[sRuleName]; ok { + return fmt.Errorf("%s:%d: the rule %s has been already defined", path, i+1, sRuleName) + } + // Calculate rule text digest as a digests of line digests. + var digests []byte + doDigest := func(b []byte) { + h := sha1.New() + h.Write(b) + digests = h.Sum(digests) + + } + // For the first line, digest everything after rule's name + doDigest(lines[i][cap(lines[i])+len(rn)-cap(rn):]) + for i++; i < len(lines) && lines[i][0] == ' '; i++ { + doDigest(lines[i]) + } + h := sha1.New() + h.Write(digests) + ruleDigest[sRuleName] = "R" + hex.EncodeToString(h.Sum(nil)) + + } else { + i++ + } + } + + // Rewrite rule names. + for i, line := range lines { + if bytes.HasPrefix(line, buildPrefix) { + brn := getBuildRuleName(line) + if bytes.Equal(brn, phonyRule) { + sink.Write(line) + continue + } + if len(brn) == 0 { + return fmt.Errorf("%s:%d: build statement lacks rule name", path, i+1) + } + sink.Write(line[0 : cap(line)-cap(brn)]) + if digest, ok := ruleDigest[string(brn)]; ok { + sink.Write([]byte(digest)) + } else { + return fmt.Errorf("%s:%d: no rule for this build target", path, i+1) + } + sink.Write(line[cap(line)+len(brn)-cap(brn):]) + } else if bytes.HasPrefix(line, rulePrefix) { + rn := ruleName(line) + // Write everything before it + sink.Write(line[0 : cap(line)-cap(rn)]) + sink.Write([]byte(ruleDigest[string(rn)])) + sink.Write(line[cap(line)+len(rn)-cap(rn):]) + } else { + //goland:noinspection GoUnhandledErrorResult + sink.Write(line) + } + } + return nil +} + +func getLine(b []byte) []byte { + if n := bytes.IndexByte(b, '\n'); n >= 0 { + return b[:n+1] + } + return b +} + +// Returns build statement's rule name +func getBuildRuleName(line []byte) []byte { + n := bytes.IndexByte(line, ':') + if n <= 0 { + return nil + } + ruleName := line[n+1:] + if ruleName[0] == ' ' { + ruleName = bytes.TrimLeft(ruleName, " ") + } + if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 { + ruleName = ruleName[0:n] + } + return ruleName +} + +// Returns rule statement's rule name +func ruleName(lineAfterRule []byte) []byte { + ruleName := lineAfterRule[len(rulePrefix):] + if len(ruleName) == 0 { + return ruleName + } + if ruleName[0] == ' ' { + ruleName = bytes.TrimLeft(ruleName, " ") + } + if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 { + ruleName = ruleName[0:n] + } + return ruleName +} diff --git a/make/tools/canoninja/canoninja_test.go b/make/tools/canoninja/canoninja_test.go new file mode 100644 index 0000000..3c45f8c --- /dev/null +++ b/make/tools/canoninja/canoninja_test.go @@ -0,0 +1,47 @@ +package canoninja + +import ( + "bytes" + "testing" +) + +func TestGenerate(t *testing.T) { + tests := []struct { + name string + in []byte + wantSink string + wantErr bool + }{ + { + name: "1", + in: []byte(` +rule rule1 + abcd +rule rule2 + abcd +build x: rule1 +`), + wantSink: ` +rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b + abcd +rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b + abcd +build x: R9c97aba7f61994be6862f5ea9a62d26130c7f48b +`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sink := &bytes.Buffer{} + err := Generate("", tt.in, sink) + if (err != nil) != tt.wantErr { + t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotSink := sink.String(); gotSink != tt.wantSink { + t.Errorf("Generate() gotSink = %v, want %v", gotSink, tt.wantSink) + } + }) + } +} diff --git a/make/tools/canoninja/cmd/canoninja.go b/make/tools/canoninja/cmd/canoninja.go new file mode 100644 index 0000000..71802ef --- /dev/null +++ b/make/tools/canoninja/cmd/canoninja.go @@ -0,0 +1,36 @@ +package main + +/* + Canoninja reads a Ninja file and changes the rule names to be the digest of the rule contents. + Feed it to a filter that extracts only build statements, sort them, and you will have a crude + but effective tool to find small differences between two Ninja files. +*/ + +import ( + "canoninja" + "flag" + "fmt" + "os" +) + +func main() { + flag.Parse() + files := flag.Args() + if len(files) == 0 { + files = []string{"/dev/stdin"} + } + rc := 0 + for _, f := range files { + if buffer, err := os.ReadFile(f); err == nil { + err = canoninja.Generate(f, buffer, os.Stdout) + if err != nil { + fmt.Fprintln(os.Stderr, err) + rc = 1 + } + } else { + fmt.Fprintf(os.Stderr, "%s: %s\n", f, err) + rc = 1 + } + } + os.Exit(rc) +} diff --git a/make/tools/canoninja/go.mod b/make/tools/canoninja/go.mod new file mode 100644 index 0000000..c5a924e --- /dev/null +++ b/make/tools/canoninja/go.mod @@ -0,0 +1 @@ +module canoninja diff --git a/make/tools/check_elf_file.py b/make/tools/check_elf_file.py new file mode 100755 index 0000000..045cb1d --- /dev/null +++ b/make/tools/check_elf_file.py @@ -0,0 +1,557 @@ +#!/usr/bin/env python +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""ELF file checker. + +This command ensures all undefined symbols in an ELF file can be resolved to +global (or weak) symbols defined in shared objects specified in DT_NEEDED +entries. +""" + +from __future__ import print_function + +import argparse +import collections +import os +import os.path +import re +import struct +import subprocess +import sys + + +_ELF_MAGIC = b'\x7fELF' + + +# Known machines +_EM_386 = 3 +_EM_ARM = 40 +_EM_X86_64 = 62 +_EM_AARCH64 = 183 + +_KNOWN_MACHINES = {_EM_386, _EM_ARM, _EM_X86_64, _EM_AARCH64} + + +# ELF header struct +_ELF_HEADER_STRUCT = ( + ('ei_magic', '4s'), + ('ei_class', 'B'), + ('ei_data', 'B'), + ('ei_version', 'B'), + ('ei_osabi', 'B'), + ('ei_pad', '8s'), + ('e_type', 'H'), + ('e_machine', 'H'), + ('e_version', 'I'), +) + +_ELF_HEADER_STRUCT_FMT = ''.join(_fmt for _, _fmt in _ELF_HEADER_STRUCT) + + +ELFHeader = collections.namedtuple( + 'ELFHeader', [_name for _name, _ in _ELF_HEADER_STRUCT]) + + +ELF = collections.namedtuple( + 'ELF', + ('dt_soname', 'dt_needed', 'imported', 'exported', 'header')) + + +def _get_os_name(): + """Get the host OS name.""" + if sys.platform == 'linux2': + return 'linux' + if sys.platform == 'darwin': + return 'darwin' + raise ValueError(sys.platform + ' is not supported') + + +def _get_build_top(): + """Find the build top of the source tree ($ANDROID_BUILD_TOP).""" + prev_path = None + curr_path = os.path.abspath(os.getcwd()) + while prev_path != curr_path: + if os.path.exists(os.path.join(curr_path, '.repo')): + return curr_path + prev_path = curr_path + curr_path = os.path.dirname(curr_path) + return None + + +def _select_latest_llvm_version(versions): + """Select the latest LLVM prebuilts version from a set of versions.""" + pattern = re.compile('clang-r([0-9]+)([a-z]?)') + found_rev = 0 + found_ver = None + for curr_ver in versions: + match = pattern.match(curr_ver) + if not match: + continue + curr_rev = int(match.group(1)) + if not found_ver or curr_rev > found_rev or ( + curr_rev == found_rev and curr_ver > found_ver): + found_rev = curr_rev + found_ver = curr_ver + return found_ver + + +def _get_latest_llvm_version(llvm_dir): + """Find the latest LLVM prebuilts version from `llvm_dir`.""" + return _select_latest_llvm_version(os.listdir(llvm_dir)) + + +def _get_llvm_dir(): + """Find the path to LLVM prebuilts.""" + build_top = _get_build_top() + + llvm_prebuilts_base = os.environ.get('LLVM_PREBUILTS_BASE') + if not llvm_prebuilts_base: + llvm_prebuilts_base = os.path.join('prebuilts', 'clang', 'host') + + llvm_dir = os.path.join( + build_top, llvm_prebuilts_base, _get_os_name() + '-x86') + + if not os.path.exists(llvm_dir): + return None + + llvm_prebuilts_version = os.environ.get('LLVM_PREBUILTS_VERSION') + if not llvm_prebuilts_version: + llvm_prebuilts_version = _get_latest_llvm_version(llvm_dir) + + llvm_dir = os.path.join(llvm_dir, llvm_prebuilts_version) + + if not os.path.exists(llvm_dir): + return None + + return llvm_dir + + +def _get_llvm_readobj(): + """Find the path to llvm-readobj executable.""" + llvm_dir = _get_llvm_dir() + llvm_readobj = os.path.join(llvm_dir, 'bin', 'llvm-readobj') + return llvm_readobj if os.path.exists(llvm_readobj) else 'llvm-readobj' + + +class ELFError(ValueError): + """Generic ELF parse error""" + pass + + +class ELFInvalidMagicError(ELFError): + """Invalid ELF magic word error""" + def __init__(self): + super(ELFInvalidMagicError, self).__init__('bad ELF magic') + + +class ELFParser(object): + """ELF file parser""" + + @classmethod + def _read_elf_header(cls, elf_file_path): + """Read the ELF magic word from the beginning of the file.""" + with open(elf_file_path, 'rb') as elf_file: + buf = elf_file.read(struct.calcsize(_ELF_HEADER_STRUCT_FMT)) + try: + return ELFHeader(*struct.unpack(_ELF_HEADER_STRUCT_FMT, buf)) + except struct.error: + return None + + + @classmethod + def open(cls, elf_file_path, llvm_readobj): + """Open and parse the ELF file.""" + # Parse the ELF header to check the magic word. + header = cls._read_elf_header(elf_file_path) + if not header or header.ei_magic != _ELF_MAGIC: + raise ELFInvalidMagicError() + + # Run llvm-readobj and parse the output. + return cls._read_llvm_readobj(elf_file_path, header, llvm_readobj) + + + @classmethod + def _find_prefix(cls, pattern, lines_it): + """Iterate `lines_it` until finding a string that starts with `pattern`.""" + for line in lines_it: + if line.startswith(pattern): + return True + return False + + + @classmethod + def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj): + """Run llvm-readobj and parse the output.""" + cmd = [llvm_readobj, '--dynamic-table', '--dyn-symbols', elf_file_path] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, _ = proc.communicate() + rc = proc.returncode + if rc != 0: + raise subprocess.CalledProcessError(rc, cmd, out) + lines = out.splitlines() + return cls._parse_llvm_readobj(elf_file_path, header, lines) + + + @classmethod + def _parse_llvm_readobj(cls, elf_file_path, header, lines): + """Parse the output of llvm-readobj.""" + lines_it = iter(lines) + dt_soname, dt_needed = cls._parse_dynamic_table(elf_file_path, lines_it) + imported, exported = cls._parse_dynamic_symbols(lines_it) + return ELF(dt_soname, dt_needed, imported, exported, header) + + + _DYNAMIC_SECTION_START_PATTERN = 'DynamicSection [' + + _DYNAMIC_SECTION_NEEDED_PATTERN = re.compile( + '^ 0x[0-9a-fA-F]+\\s+NEEDED\\s+Shared library: \\[(.*)\\]$') + + _DYNAMIC_SECTION_SONAME_PATTERN = re.compile( + '^ 0x[0-9a-fA-F]+\\s+SONAME\\s+Library soname: \\[(.*)\\]$') + + _DYNAMIC_SECTION_END_PATTERN = ']' + + + @classmethod + def _parse_dynamic_table(cls, elf_file_path, lines_it): + """Parse the dynamic table section.""" + dt_soname = os.path.basename(elf_file_path) + dt_needed = [] + + dynamic = cls._find_prefix(cls._DYNAMIC_SECTION_START_PATTERN, lines_it) + if not dynamic: + return (dt_soname, dt_needed) + + for line in lines_it: + if line == cls._DYNAMIC_SECTION_END_PATTERN: + break + + match = cls._DYNAMIC_SECTION_NEEDED_PATTERN.match(line) + if match: + dt_needed.append(match.group(1)) + continue + + match = cls._DYNAMIC_SECTION_SONAME_PATTERN.match(line) + if match: + dt_soname = match.group(1) + continue + + return (dt_soname, dt_needed) + + + _DYNAMIC_SYMBOLS_START_PATTERN = 'DynamicSymbols [' + _DYNAMIC_SYMBOLS_END_PATTERN = ']' + + _SYMBOL_ENTRY_START_PATTERN = ' Symbol {' + _SYMBOL_ENTRY_PATTERN = re.compile('^ ([A-Za-z0-9_]+): (.*)$') + _SYMBOL_ENTRY_PAREN_PATTERN = re.compile( + '\\s+\\((?:(?:\\d+)|(?:0x[0-9a-fA-F]+))\\)$') + _SYMBOL_ENTRY_END_PATTERN = ' }' + + + @staticmethod + def _parse_symbol_name(name_with_version): + """Split `name_with_version` into name and version. This function may split + at last occurrence of `@@` or `@`.""" + pos = name_with_version.rfind('@') + if pos == -1: + name = name_with_version + version = '' + else: + if pos > 0 and name_with_version[pos - 1] == '@': + name = name_with_version[0:pos - 1] + else: + name = name_with_version[0:pos] + version = name_with_version[pos + 1:] + return (name, version) + + + @classmethod + def _parse_dynamic_symbols(cls, lines_it): + """Parse dynamic symbol table and collect imported and exported symbols.""" + imported = collections.defaultdict(set) + exported = collections.defaultdict(set) + + for symbol in cls._parse_dynamic_symbols_internal(lines_it): + name, version = cls._parse_symbol_name(symbol['Name']) + if name: + if symbol['Section'] == 'Undefined': + if symbol['Binding'] != 'Weak': + imported[name].add(version) + else: + if symbol['Binding'] != 'Local': + exported[name].add(version) + + # Freeze the returned imported/exported dict. + return (dict(imported), dict(exported)) + + + @classmethod + def _parse_dynamic_symbols_internal(cls, lines_it): + """Parse symbols entries and yield each symbols.""" + + if not cls._find_prefix(cls._DYNAMIC_SYMBOLS_START_PATTERN, lines_it): + return + + for line in lines_it: + if line == cls._DYNAMIC_SYMBOLS_END_PATTERN: + return + + if line == cls._SYMBOL_ENTRY_START_PATTERN: + symbol = {} + continue + + if line == cls._SYMBOL_ENTRY_END_PATTERN: + yield symbol + symbol = None + continue + + match = cls._SYMBOL_ENTRY_PATTERN.match(line) + if match: + key = match.group(1) + value = cls._SYMBOL_ENTRY_PAREN_PATTERN.sub('', match.group(2)) + symbol[key] = value + continue + + +class Checker(object): + """ELF file checker that checks DT_SONAME, DT_NEEDED, and symbols.""" + + def __init__(self, llvm_readobj): + self._file_path = '' + self._file_under_test = None + self._shared_libs = [] + + self._llvm_readobj = llvm_readobj + + + if sys.stderr.isatty(): + _ERROR_TAG = '\033[0;1;31merror:\033[m' # Red error + _NOTE_TAG = '\033[0;1;30mnote:\033[m' # Black note + else: + _ERROR_TAG = 'error:' # Red error + _NOTE_TAG = 'note:' # Black note + + + def _error(self, *args): + """Emit an error to stderr.""" + print(self._file_path + ': ' + self._ERROR_TAG, *args, file=sys.stderr) + + + def _note(self, *args): + """Emit a note to stderr.""" + print(self._file_path + ': ' + self._NOTE_TAG, *args, file=sys.stderr) + + + def _load_elf_file(self, path, skip_bad_elf_magic): + """Load an ELF file from the `path`.""" + try: + return ELFParser.open(path, self._llvm_readobj) + except (IOError, OSError): + self._error('Failed to open "{}".'.format(path)) + sys.exit(2) + except ELFInvalidMagicError: + if skip_bad_elf_magic: + sys.exit(0) + else: + self._error('File "{}" must have a valid ELF magic word.'.format(path)) + sys.exit(2) + except: + self._error('An unknown error occurred while opening "{}".'.format(path)) + raise + + + def load_file_under_test(self, path, skip_bad_elf_magic, + skip_unknown_elf_machine): + """Load file-under-test (either an executable or a shared lib).""" + self._file_path = path + self._file_under_test = self._load_elf_file(path, skip_bad_elf_magic) + + if skip_unknown_elf_machine and \ + self._file_under_test.header.e_machine not in _KNOWN_MACHINES: + sys.exit(0) + + + def load_shared_libs(self, shared_lib_paths): + """Load shared libraries.""" + for path in shared_lib_paths: + self._shared_libs.append(self._load_elf_file(path, False)) + + + def check_dt_soname(self, soname): + """Check whether DT_SONAME matches installation file name.""" + if self._file_under_test.dt_soname != soname: + self._error('DT_SONAME "{}" must be equal to the file name "{}".' + .format(self._file_under_test.dt_soname, soname)) + sys.exit(2) + + + def check_dt_needed(self, system_shared_lib_names): + """Check whether all DT_NEEDED entries are specified in the build + system.""" + + missing_shared_libs = False + + # Collect the DT_SONAMEs from shared libs specified in the build system. + specified_sonames = {lib.dt_soname for lib in self._shared_libs} + + # Chech whether all DT_NEEDED entries are specified. + for lib in self._file_under_test.dt_needed: + if lib not in specified_sonames: + self._error('DT_NEEDED "{}" is not specified in shared_libs.' + .format(lib.decode('utf-8'))) + missing_shared_libs = True + + if missing_shared_libs: + dt_needed = sorted(set(self._file_under_test.dt_needed)) + modules = [re.sub('\\.so$', '', lib) for lib in dt_needed] + + # Remove system shared libraries from the suggestion since they are added + # by default. + modules = [name for name in modules + if name not in system_shared_lib_names] + + self._note() + self._note('Fix suggestions:') + self._note( + ' Android.bp: shared_libs: [' + + ', '.join('"' + module + '"' for module in modules) + '],') + self._note( + ' Android.mk: LOCAL_SHARED_LIBRARIES := ' + ' '.join(modules)) + + self._note() + self._note('If the fix above doesn\'t work, bypass this check with:') + self._note(' Android.bp: check_elf_files: false,') + self._note(' Android.mk: LOCAL_CHECK_ELF_FILES := false') + + sys.exit(2) + + + @staticmethod + def _find_symbol(lib, name, version): + """Check whether the symbol name and version matches a definition in + lib.""" + try: + lib_sym_vers = lib.exported[name] + except KeyError: + return False + if version == '': # Symbol version is not requested + return True + return version in lib_sym_vers + + + @classmethod + def _find_symbol_from_libs(cls, libs, name, version): + """Check whether the symbol name and version is defined in one of the + shared libraries in libs.""" + for lib in libs: + if cls._find_symbol(lib, name, version): + return lib + return None + + + def check_symbols(self): + """Check whether all undefined symbols are resolved to a definition.""" + all_elf_files = [self._file_under_test] + self._shared_libs + missing_symbols = [] + for sym, imported_vers in self._file_under_test.imported.iteritems(): + for imported_ver in imported_vers: + lib = self._find_symbol_from_libs(all_elf_files, sym, imported_ver) + if not lib: + missing_symbols.append((sym, imported_ver)) + + if missing_symbols: + for sym, ver in sorted(missing_symbols): + sym = sym.decode('utf-8') + if ver: + sym += '@' + ver.decode('utf-8') + self._error('Unresolved symbol: {}'.format(sym)) + + self._note() + self._note('Some dependencies might be changed, thus the symbol(s) ' + 'above cannot be resolved.') + self._note('Please re-build the prebuilt file: "{}".' + .format(self._file_path)) + + self._note() + self._note('If this is a new prebuilt file and it is designed to have ' + 'unresolved symbols, add one of the following properties:') + self._note(' Android.bp: allow_undefined_symbols: true,') + self._note(' Android.mk: LOCAL_ALLOW_UNDEFINED_SYMBOLS := true') + + sys.exit(2) + + +def _parse_args(): + """Parse command line options.""" + parser = argparse.ArgumentParser() + + # Input file + parser.add_argument('file', + help='Path to the input file to be checked') + parser.add_argument('--soname', + help='Shared object name of the input file') + + # Shared library dependencies + parser.add_argument('--shared-lib', action='append', default=[], + help='Path to shared library dependencies') + + # System Shared library names + parser.add_argument('--system-shared-lib', action='append', default=[], + help='System shared libraries to be hidden from fix ' + 'suggestions') + + # Check options + parser.add_argument('--skip-bad-elf-magic', action='store_true', + help='Ignore the input file without the ELF magic word') + parser.add_argument('--skip-unknown-elf-machine', action='store_true', + help='Ignore the input file with unknown machine ID') + parser.add_argument('--allow-undefined-symbols', action='store_true', + help='Ignore unresolved undefined symbols') + + # Other options + parser.add_argument('--llvm-readobj', + help='Path to the llvm-readobj executable') + + return parser.parse_args() + + +def main(): + """Main function""" + args = _parse_args() + + llvm_readobj = args.llvm_readobj + if not llvm_readobj: + llvm_readobj = _get_llvm_readobj() + + # Load ELF files + checker = Checker(llvm_readobj) + checker.load_file_under_test( + args.file, args.skip_bad_elf_magic, args.skip_unknown_elf_machine) + checker.load_shared_libs(args.shared_lib) + + # Run checks + if args.soname: + checker.check_dt_soname(args.soname) + + checker.check_dt_needed(args.system_shared_lib) + + if not args.allow_undefined_symbols: + checker.check_symbols() + + +if __name__ == '__main__': + main() diff --git a/make/tools/check_identical_lib.sh b/make/tools/check_identical_lib.sh new file mode 100755 index 0000000..c9f436f --- /dev/null +++ b/make/tools/check_identical_lib.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +STRIP_PATH="${1}" +CORE="${2}" +VENDOR="${3}" + +TMPDIR="$(mktemp -d ${CORE}.vndk_lib_check.XXXXXXXX)" +stripped_core="${TMPDIR}/core" +stripped_vendor="${TMPDIR}/vendor" + +function cleanup() { + rm -f "${stripped_core}" "${stripped_vendor}" + rmdir "${TMPDIR}" +} +trap cleanup EXIT + +function strip_lib() { + ${STRIP_PATH} \ + -i ${1} \ + -o ${2} \ + -d /dev/null \ + --remove-build-id +} + +strip_lib ${CORE} ${stripped_core} +strip_lib ${VENDOR} ${stripped_vendor} +if ! cmp -s ${stripped_core} ${stripped_vendor}; then + echo "ERROR: VNDK library $(basename ${CORE%.so}) has different core and" \ + "vendor variants! This means that the copy used in the system.img/etc" \ + "and vendor.img/etc images are different. In order to preserve space on" \ + "some devices, it is helpful if they are the same. Frequently, " \ + "libraries are different because they or their dependencies compile" \ + "things based on the macro '__ANDROID_VNDK__' or they specify custom" \ + "options under 'target: { vendor: { ... } }'. Here are some possible" \ + "resolutions:" + echo "ERROR: 1). Remove differences, possibly using the libvndksupport" \ + "function android_is_in_vendor_process in order to turn this into a" \ + "runtime difference." + echo "ERROR: 2). Add the library to the VndkMustUseVendorVariantList" \ + "variable in build/soong/cc/config/vndk.go, which is used to" \ + "acknowledge this difference." + exit 1 +fi diff --git a/make/tools/check_radio_versions.py b/make/tools/check_radio_versions.py new file mode 100755 index 0000000..ebe621f --- /dev/null +++ b/make/tools/check_radio_versions.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import os + +try: + from hashlib import sha1 +except ImportError: + from sha import sha as sha1 + +if len(sys.argv) < 2: + sys.exit(0) + +build_info = {} +f = open(sys.argv[1]) +for line in f: + line = line.strip() + if line.startswith("require"): + key, value = line.split()[1].split("=", 1) + build_info[key] = value +f.close() + +bad = False + +for item in sys.argv[2:]: + key, fn = item.split(":", 1) + + values = build_info.get(key, None) + if not values: + continue + values = values.split("|") + + f = open(fn, "rb") + digest = sha1(f.read()).hexdigest() + f.close() + + versions = {} + try: + f = open(fn + ".sha1") + except IOError: + if not bad: print + print "*** Error opening \"%s.sha1\"; can't verify %s" % (fn, key) + bad = True + continue + for line in f: + line = line.strip() + if not line or line.startswith("#"): continue + h, v = line.split() + versions[h] = v + + if digest not in versions: + if not bad: print + print "*** SHA-1 hash of \"%s\" doesn't appear in \"%s.sha1\"" % (fn, fn) + bad = True + continue + + if versions[digest] not in values: + if not bad: print + print "*** \"%s\" is version %s; not any %s allowed by \"%s\"." % ( + fn, versions[digest], key, sys.argv[1]) + bad = True + +if bad: + print + sys.exit(1) diff --git a/make/tools/checkowners.py b/make/tools/checkowners.py new file mode 100755 index 0000000..f037321 --- /dev/null +++ b/make/tools/checkowners.py @@ -0,0 +1,87 @@ +#!/usr/bin/python + +"""Parse and check syntax errors of a given OWNERS file.""" + +import argparse +import re +import sys +import urllib.request, urllib.parse, urllib.error +import urllib.request, urllib.error, urllib.parse + +parser = argparse.ArgumentParser(description='Check OWNERS file syntax') +parser.add_argument('-v', '--verbose', dest='verbose', + action='store_true', default=False, + help='Verbose output to debug') +parser.add_argument('-c', '--check_address', dest='check_address', + action='store_true', default=False, + help='Check email addresses') +parser.add_argument(dest='owners', metavar='OWNERS', nargs='+', + help='Path to OWNERS file') +args = parser.parse_args() + +gerrit_server = 'https://android-review.googlesource.com' +checked_addresses = {} + + +def echo(msg): + if args.verbose: + print(msg) + + +def find_address(address): + if address not in checked_addresses: + request = (gerrit_server + '/accounts/?n=1&q=email:' + + urllib.parse.quote(address)) + echo('Checking email address: ' + address) + result = urllib.request.urlopen(request).read() + checked_addresses[address] = result.find('"_account_id":') >= 0 + if checked_addresses[address]: + echo('Found email address: ' + address) + return checked_addresses[address] + + +def check_address(fname, num, address): + if find_address(address): + return 0 + print('%s:%d: ERROR: unknown email address: %s' % (fname, num, address)) + return 1 + + +def main(): + # One regular expression to check all valid lines. + noparent = 'set +noparent' + email = '([^@ ]+@[^ @]+|\\*)' + emails = '(%s( *, *%s)*)' % (email, email) + file_directive = 'file: *([^ :]+ *: *)?[^ ]+' + directive = '(%s|%s|%s)' % (emails, noparent, file_directive) + glob = '[a-zA-Z0-9_\\.\\-\\*\\?]+' + globs = '(%s( *, *%s)*)' % (glob, glob) + perfile = 'per-file +' + globs + ' *= *' + directive + include = 'include +([^ :]+ *: *)?[^ ]+' + pats = '(|%s|%s|%s|%s|%s)$' % (noparent, email, perfile, include, file_directive) + patterns = re.compile(pats) + address_pattern = re.compile('([^@ ]+@[^ @]+)') + perfile_pattern = re.compile('per-file +.*=(.*)') + + error = 0 + for fname in args.owners: + echo('Checking file: ' + fname) + num = 0 + for line in open(fname, 'r'): + num += 1 + stripped_line = re.sub('#.*$', '', line).strip() + if not patterns.match(stripped_line): + error += 1 + print('%s:%d: ERROR: unknown line [%s]' % (fname, num, line.strip())) + elif args.check_address: + if perfile_pattern.match(stripped_line): + for addr in perfile_pattern.match(stripped_line).group(1).split(','): + a = addr.strip() + if a and a != '*': + error += check_address(fname, num, addr.strip()) + elif address_pattern.match(stripped_line): + error += check_address(fname, num, stripped_line) + sys.exit(error) + +if __name__ == '__main__': + main() diff --git a/make/tools/compare_builds.py b/make/tools/compare_builds.py new file mode 100755 index 0000000..838a628 --- /dev/null +++ b/make/tools/compare_builds.py @@ -0,0 +1,661 @@ +#!/usr/bin/env -S python3 -u + +""" +This script helps find various build behaviors that make builds less hermetic +and repeatable. Depending on the flags, it runs a sequence of builds and looks +for files that have changed or have been improperly regenerated, updating +their timestamps incorrectly. It also looks for changes that the build has +done to the source tree, and for files whose contents are dependent on the +location of the out directory. + +This utility has two major modes, full and incremental. By default, this tool +runs in full mode. To run in incremental mode, pass the --incremental flag. + + +FULL MODE + +In full mode, this tool helps verify BUILD CORRECTNESS by examining its +REPEATABILITY. In full mode, this tool runs two complete builds in different +directories and compares the CONTENTS of the two directories. Lists of any +files that are added, removed or changed are printed, sorted by the timestamp +of that file, to aid finding which dependencies trigger the rebuilding of +other files. + + +INCREMENTAL MODE + +In incremental mode, this tool helps verfiy the SPEED of the build. It runs two +builds and looks at the TIMESTAMPS of the generated files, and reports files +that were changed by the second build. In theory, an incremental build with no +source files touched should not have any generated targets changed. As in full +builds, the file list is returned sorted by timestamp. + + +OTHER CHECKS + +In both full and incremental mode, this tool looks at the timestamps of all +source files in the tree, and reports on files that have been touched. In the +output, these are labeled with the header "Source files touched after start of +build." + +In addition, by default, this tool sets the OUT_DIR environment variable to +something other than "out" in order to find build rules that are not respecting +the OUT_DIR. If you see these, you should fix them, but if your build can not +complete for some reason because of this, you can pass the --no-check-out-dir +flag to suppress this check. + + +OTHER FLAGS + +In full mode, the --detect-embedded-paths flag does the two builds in different +directories, to help in finding rules that embed the out directory path into +the targets. + +The --hide-build-output flag hides the output of successful bulds, to make +script output cleaner. The output of builds that fail is still shown. + +The --no-build flag is useful if you have already done a build and would +just like to re-run the analysis. + +The --target flag lets you specify a build target other than the default +full build (droid). You can pass "nothing" as in the example below, or a +specific target, to reduce the scope of the checks performed. + +The --touch flag lets you specify a list of source files to touch between +the builds, to examine the consequences of editing a particular file. + + +EXAMPLE COMMANDLINES + +Please run build/make/tools/compare_builds.py --help for a full listing +of the commandline flags. Here are a sampling of useful combinations. + + 1. Find files changed during an incremental build that doesn't build + any targets. + + build/make/tools/compare_builds.py --incremental --target nothing + + Long incremental build times, or consecutive builds that re-run build actions + are usually caused by files being touched as part of loading the makefiles. + + The nothing build (m nothing) loads the make and blueprint files, generates + the dependency graph, but then doesn't actually build any targets. Checking + against this build is the fastest and easiest way to find files that are + modified while makefiles are read, for example with $(shell) invocations. + + 2. Find packaging targets that are different, ignoring intermediate files. + + build/make/tools/compare_builds.py --subdirs --detect-embedded-paths + + These flags will compare the final staging directories for partitions, + as well as the APKs, apexes, testcases, and the like (the full directory + list is in the DEFAULT_DIRS variable below). Since these are the files + that are ultimately released, it is more important that these files be + replicable, even if the intermediates that went into them are not (for + example, when debugging symbols are stripped). + + 3. Check that all targets are repeatable. + + build/make/tools/compare_builds.py --detect-embedded-paths + + This check will list all of the differences in built targets that it can + find. Be aware that the AOSP tree still has quite a few targets that + are flagged by this check, so OEM changes might be lost in that list. + That said, each file shown here is a potential blocker for a repeatable + build. + + 4. See what targets are rebuilt when a file is touched between builds. + + build/make/tools/compare_builds.py --incremental \ + --touch frameworks/base/core/java/android/app/Activity.java + + This check simulates the common engineer workflow of touching a single + file and rebuilding the whole system. To see a restricted view, consider + also passing a --target option for a common use case. For example: + + build/make/tools/compare_builds.py --incremental --target framework \ + --touch frameworks/base/core/java/android/app/Activity.java +""" + +import argparse +import itertools +import os +import shutil +import stat +import subprocess +import sys + + +# Soong +SOONG_UI = "build/soong/soong_ui.bash" + + +# Which directories to use if no --subdirs is supplied without explicit directories. +DEFAULT_DIRS = ( + "apex", + "data", + "product", + "ramdisk", + "recovery", + "root", + "system", + "system_ext", + "system_other", + "testcases", + "vendor", +) + + +# Files to skip for incremental timestamp checking +BUILD_INTERNALS_PREFIX_SKIP = ( + "soong/.glob/", + ".path/", +) + + +BUILD_INTERNALS_SUFFIX_SKIP = ( + "/soong/soong_build_metrics.pb", + "/.installable_test_files", + "/files.db", + "/.blueprint.bootstrap", + "/build_number.txt", + "/build.ninja", + "/.out-dir", + "/build_fingerprint.txt", + "/build_thumbprint.txt", + "/.copied_headers_list", + "/.installable_files", +) + + +class DiffType(object): + def __init__(self, code, message): + self.code = code + self.message = message + +DIFF_NONE = DiffType("DIFF_NONE", "Files are the same") +DIFF_MODE = DiffType("DIFF_MODE", "Stat mode bits differ") +DIFF_SIZE = DiffType("DIFF_SIZE", "File size differs") +DIFF_SYMLINK = DiffType("DIFF_SYMLINK", "Symlinks point to different locations") +DIFF_CONTENTS = DiffType("DIFF_CONTENTS", "File contents differ") + + +def main(): + argparser = argparse.ArgumentParser(description="Diff build outputs from two builds.", + epilog="Run this command from the root of the tree." + + " Before running this command, the build environment" + + " must be set up, including sourcing build/envsetup.sh" + + " and running lunch.") + argparser.add_argument("--detect-embedded-paths", action="store_true", + help="Use unique out dirs to detect paths embedded in binaries.") + argparser.add_argument("--incremental", action="store_true", + help="Compare which files are touched in two consecutive builds without a clean in between.") + argparser.add_argument("--hide-build-output", action="store_true", + help="Don't print the build output for successful builds") + argparser.add_argument("--no-build", dest="run_build", action="store_false", + help="Don't build or clean, but do everything else.") + argparser.add_argument("--no-check-out-dir", dest="check_out_dir", action="store_false", + help="Don't check for rules not honoring movable out directories.") + argparser.add_argument("--subdirs", nargs="*", + help="Only scan these subdirs of $PRODUCT_OUT instead of the whole out directory." + + " The --subdirs argument with no listed directories will give a default list.") + argparser.add_argument("--target", default="droid", + help="Make target to run. The default is droid") + argparser.add_argument("--touch", nargs="+", default=[], + help="Files to touch between builds. Must pair with --incremental.") + args = argparser.parse_args(sys.argv[1:]) + + if args.detect_embedded_paths and args.incremental: + sys.stderr.write("Can't pass --detect-embedded-paths and --incremental together.\n") + sys.exit(1) + if args.detect_embedded_paths and not args.check_out_dir: + sys.stderr.write("Can't pass --detect-embedded-paths and --no-check-out-dir together.\n") + sys.exit(1) + if args.touch and not args.incremental: + sys.stderr.write("The --incremental flag is required if the --touch flag is passed.") + sys.exit(1) + + AssertAtTop() + RequireEnvVar("TARGET_PRODUCT") + RequireEnvVar("TARGET_BUILD_VARIANT") + + # Out dir file names: + # - dir_prefix - The directory we'll put everything in (except for maybe the top level + # out/ dir). + # - *work_dir - The directory that we will build directly into. This is in dir_prefix + # unless --no-check-out-dir is set. + # - *out_dir - After building, if work_dir is different from out_dir, we move the out + # directory to here so we can do the comparisions. + # - timestamp_* - Files we touch so we know the various phases between the builds, so we + # can compare timestamps of files. + if args.incremental: + dir_prefix = "out_incremental" + if args.check_out_dir: + first_work_dir = first_out_dir = dir_prefix + "/out" + second_work_dir = second_out_dir = dir_prefix + "/out" + else: + first_work_dir = first_out_dir = "out" + second_work_dir = second_out_dir = "out" + else: + dir_prefix = "out_full" + first_out_dir = dir_prefix + "/out_1" + second_out_dir = dir_prefix + "/out_2" + if not args.check_out_dir: + first_work_dir = second_work_dir = "out" + elif args.detect_embedded_paths: + first_work_dir = first_out_dir + second_work_dir = second_out_dir + else: + first_work_dir = dir_prefix + "/work" + second_work_dir = dir_prefix + "/work" + timestamp_start = dir_prefix + "/timestamp_start" + timestamp_between = dir_prefix + "/timestamp_between" + timestamp_end = dir_prefix + "/timestamp_end" + + if args.run_build: + # Initial clean, if necessary + print("Cleaning " + dir_prefix + "/") + Clean(dir_prefix) + print("Cleaning out/") + Clean("out") + CreateEmptyFile(timestamp_start) + print("Running the first build in " + first_work_dir) + RunBuild(first_work_dir, first_out_dir, args.target, args.hide_build_output) + for f in args.touch: + print("Touching " + f) + TouchFile(f) + CreateEmptyFile(timestamp_between) + print("Running the second build in " + second_work_dir) + RunBuild(second_work_dir, second_out_dir, args.target, args.hide_build_output) + CreateEmptyFile(timestamp_end) + print("Done building") + print() + + # Which out directories to scan + if args.subdirs is not None: + if args.subdirs: + subdirs = args.subdirs + else: + subdirs = DEFAULT_DIRS + first_files = ProductFiles(RequireBuildVar(first_out_dir, "PRODUCT_OUT"), subdirs) + second_files = ProductFiles(RequireBuildVar(second_out_dir, "PRODUCT_OUT"), subdirs) + else: + first_files = OutFiles(first_out_dir) + second_files = OutFiles(second_out_dir) + + printer = Printer() + + if args.incremental: + # Find files that were rebuilt unnecessarily + touched_incrementally = FindOutFilesTouchedAfter(first_files, + GetFileTimestamp(timestamp_between)) + printer.PrintList("Touched in incremental build", touched_incrementally) + else: + # Compare the two out dirs + added, removed, changed = DiffFileList(first_files, second_files) + printer.PrintList("Added", added) + printer.PrintList("Removed", removed) + printer.PrintList("Changed", changed, "%s %s") + + # Find files in the source tree that were touched + touched_during = FindSourceFilesTouchedAfter(GetFileTimestamp(timestamp_start)) + printer.PrintList("Source files touched after start of build", touched_during) + + # Find files and dirs that were output to "out" and didn't respect $OUT_DIR + if args.check_out_dir: + bad_out_dir_contents = FindFilesAndDirectories("out") + printer.PrintList("Files and directories created by rules that didn't respect $OUT_DIR", + bad_out_dir_contents) + + # If we didn't find anything, print success message + if not printer.printed_anything: + print("No bad behaviors found.") + + +def AssertAtTop(): + """If the current directory is not the top of an android source tree, print an error + message and exit.""" + if not os.access(SOONG_UI, os.X_OK): + sys.stderr.write("FAILED: Please run from the root of the tree.\n") + sys.exit(1) + + +def RequireEnvVar(name): + """Gets an environment variable. If that fails, then print an error message and exit.""" + result = os.environ.get(name) + if not result: + sys.stderr.write("error: Can't determine %s. Please run lunch first.\n" % name) + sys.exit(1) + return result + + +def RunSoong(out_dir, args, capture_output): + env = dict(os.environ) + env["OUT_DIR"] = out_dir + args = [SOONG_UI,] + args + if capture_output: + proc = subprocess.Popen(args, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + combined_output, none = proc.communicate() + return proc.returncode, combined_output + else: + result = subprocess.run(args, env=env) + return result.returncode, None + + +def GetBuildVar(out_dir, name): + """Gets a variable from the build system.""" + returncode, output = RunSoong(out_dir, ["--dumpvar-mode", name], True) + if returncode != 0: + return None + else: + return output.decode("utf-8").strip() + + +def RequireBuildVar(out_dir, name): + """Gets a variable from the builds system. If that fails, then print an error + message and exit.""" + value = GetBuildVar(out_dir, name) + if not value: + sys.stderr.write("error: Can't determine %s. Please run lunch first.\n" % name) + sys.exit(1) + return value + + +def Clean(directory): + """"Deletes the supplied directory.""" + try: + shutil.rmtree(directory) + except FileNotFoundError: + pass + + +def RunBuild(work_dir, out_dir, target, hide_build_output): + """Runs a build. If the build fails, prints a message and exits.""" + returncode, output = RunSoong(work_dir, + ["--build-mode", "--all-modules", "--dir=" + os.getcwd(), target], + hide_build_output) + if work_dir != out_dir: + os.replace(work_dir, out_dir) + if returncode != 0: + if hide_build_output: + # The build output was hidden, so print it now for debugging + sys.stderr.buffer.write(output) + sys.stderr.write("FAILED: Build failed. Stopping.\n") + sys.exit(1) + + +def DiffFileList(first_files, second_files): + """Examines the files. + + Returns: + Filenames of files in first_filelist but not second_filelist (added files) + Filenames of files in second_filelist but not first_filelist (removed files) + 2-Tuple of filenames for the files that are in both but are different (changed files) + """ + # List of files, relative to their respective PRODUCT_OUT directories + first_filelist = sorted([x for x in first_files], key=lambda x: x[1]) + second_filelist = sorted([x for x in second_files], key=lambda x: x[1]) + + added = [] + removed = [] + changed = [] + + first_index = 0 + second_index = 0 + + while first_index < len(first_filelist) and second_index < len(second_filelist): + # Path relative to source root and path relative to PRODUCT_OUT + first_full_filename, first_relative_filename = first_filelist[first_index] + second_full_filename, second_relative_filename = second_filelist[second_index] + + if first_relative_filename < second_relative_filename: + # Removed + removed.append(first_full_filename) + first_index += 1 + elif first_relative_filename > second_relative_filename: + # Added + added.append(second_full_filename) + second_index += 1 + else: + # Both present + diff_type = DiffFiles(first_full_filename, second_full_filename) + if diff_type != DIFF_NONE: + changed.append((first_full_filename, second_full_filename)) + first_index += 1 + second_index += 1 + + while first_index < len(first_filelist): + first_full_filename, first_relative_filename = first_filelist[first_index] + removed.append(first_full_filename) + first_index += 1 + + while second_index < len(second_filelist): + second_full_filename, second_relative_filename = second_filelist[second_index] + added.append(second_full_filename) + second_index += 1 + + return (SortByTimestamp(added), + SortByTimestamp(removed), + SortByTimestamp(changed, key=lambda item: item[1])) + + +def FindOutFilesTouchedAfter(files, timestamp): + """Find files in the given file iterator that were touched after timestamp.""" + result = [] + for full, relative in files: + ts = GetFileTimestamp(full) + if ts > timestamp: + result.append(TouchedFile(full, ts)) + return [f.filename for f in sorted(result, key=lambda f: f.timestamp)] + + +def GetFileTimestamp(filename): + """Get timestamp for a file (just wraps stat).""" + st = os.stat(filename, follow_symlinks=False) + return st.st_mtime + + +def SortByTimestamp(items, key=lambda item: item): + """Sort the list by timestamp of files. + Args: + items - the list of items to sort + key - a function to extract a filename from each element in items + """ + return [x[0] for x in sorted([(item, GetFileTimestamp(key(item))) for item in items], + key=lambda y: y[1])] + + +def FindSourceFilesTouchedAfter(timestamp): + """Find files in the source tree that have changed after timestamp. Ignores + the out directory.""" + result = [] + for root, dirs, files in os.walk(".", followlinks=False): + if root == ".": + RemoveItemsFromList(dirs, (".repo", "out", "out_full", "out_incremental")) + for f in files: + full = os.path.sep.join((root, f))[2:] + ts = GetFileTimestamp(full) + if ts > timestamp: + result.append(TouchedFile(full, ts)) + return [f.filename for f in sorted(result, key=lambda f: f.timestamp)] + + +def FindFilesAndDirectories(directory): + """Finds all files and directories inside a directory.""" + result = [] + for root, dirs, files in os.walk(directory, followlinks=False): + result += [os.path.sep.join((root, x, "")) for x in dirs] + result += [os.path.sep.join((root, x)) for x in files] + return result + + +def CreateEmptyFile(filename): + """Create an empty file with now as the timestamp at filename.""" + try: + os.makedirs(os.path.dirname(filename)) + except FileExistsError: + pass + open(filename, "w").close() + os.utime(filename) + + +def TouchFile(filename): + os.utime(filename) + + +def DiffFiles(first_filename, second_filename): + def AreFileContentsSame(remaining, first_filename, second_filename): + """Compare the file contents. They must be known to be the same size.""" + CHUNK_SIZE = 32*1024 + with open(first_filename, "rb") as first_file: + with open(second_filename, "rb") as second_file: + while remaining > 0: + size = min(CHUNK_SIZE, remaining) + if first_file.read(CHUNK_SIZE) != second_file.read(CHUNK_SIZE): + return False + remaining -= size + return True + + first_stat = os.stat(first_filename, follow_symlinks=False) + second_stat = os.stat(first_filename, follow_symlinks=False) + + # Mode bits + if first_stat.st_mode != second_stat.st_mode: + return DIFF_MODE + + # File size + if first_stat.st_size != second_stat.st_size: + return DIFF_SIZE + + # Contents + if stat.S_ISLNK(first_stat.st_mode): + if os.readlink(first_filename) != os.readlink(second_filename): + return DIFF_SYMLINK + elif stat.S_ISREG(first_stat.st_mode): + if not AreFileContentsSame(first_stat.st_size, first_filename, second_filename): + return DIFF_CONTENTS + + return DIFF_NONE + + +class FileIterator(object): + """Object that produces an iterator containing all files in a given directory. + + Each iteration yields a tuple containing: + + [0] (full) Path to file relative to source tree. + [1] (relative) Path to the file relative to the base directory given in the + constructor. + """ + + def __init__(self, base_dir): + self._base_dir = base_dir + + def __iter__(self): + return self._Iterator(self, self._base_dir) + + def ShouldIncludeFile(self, root, path): + return False + + class _Iterator(object): + def __init__(self, parent, base_dir): + self._parent = parent + self._base_dir = base_dir + self._walker = os.walk(base_dir, followlinks=False) + self._current_index = 0 + self._current_dir = [] + + def __iter__(self): + return self + + def __next__(self): + # os.walk's iterator will eventually terminate by raising StopIteration + while True: + if self._current_index >= len(self._current_dir): + root, dirs, files = self._walker.__next__() + full_paths = [os.path.sep.join((root, f)) for f in files] + pairs = [(f, f[len(self._base_dir)+1:]) for f in full_paths] + self._current_dir = [(full, relative) for full, relative in pairs + if self._parent.ShouldIncludeFile(root, relative)] + self._current_index = 0 + if not self._current_dir: + continue + index = self._current_index + self._current_index += 1 + return self._current_dir[index] + + +class OutFiles(FileIterator): + """Object that produces an iterator containing all files in a given out directory, + except for files which are known to be touched as part of build setup. + """ + def __init__(self, out_dir): + super().__init__(out_dir) + self._out_dir = out_dir + + def ShouldIncludeFile(self, root, relative): + # Skip files in root, although note that this could actually skip + # files that are sadly generated directly into that directory. + if root == self._out_dir: + return False + # Skiplist + for skip in BUILD_INTERNALS_PREFIX_SKIP: + if relative.startswith(skip): + return False + for skip in BUILD_INTERNALS_SUFFIX_SKIP: + if relative.endswith(skip): + return False + return True + + +class ProductFiles(FileIterator): + """Object that produces an iterator containing files in listed subdirectories of $PRODUCT_OUT. + """ + def __init__(self, product_out, subdirs): + super().__init__(product_out) + self._subdirs = subdirs + + def ShouldIncludeFile(self, root, relative): + for subdir in self._subdirs: + if relative.startswith(subdir): + return True + return False + + +class TouchedFile(object): + """A file in the out directory with a timestamp.""" + def __init__(self, filename, timestamp): + self.filename = filename + self.timestamp = timestamp + + +def RemoveItemsFromList(haystack, needles): + for needle in needles: + try: + haystack.remove(needle) + except ValueError: + pass + + +class Printer(object): + def __init__(self): + self.printed_anything = False + + def PrintList(self, title, items, fmt="%s"): + if items: + if self.printed_anything: + sys.stdout.write("\n") + sys.stdout.write("%s:\n" % title) + for item in items: + sys.stdout.write(" %s\n" % fmt % item) + self.printed_anything = True + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + + +# vim: ts=2 sw=2 sts=2 nocindent diff --git a/make/tools/compare_fileslist.py b/make/tools/compare_fileslist.py new file mode 100755 index 0000000..1f507d8 --- /dev/null +++ b/make/tools/compare_fileslist.py @@ -0,0 +1,106 @@ +#!/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 """ + + + + + """ + print "" + print "" + for input in inputs: + combo = input.split(os.path.sep)[1] + print " " % cgi.escape(combo) + print "" + + for row in rows: + print "" + for sz in row[1:]: + if not sz: + print " " + elif IsDifferent(row[1:]): + print " " % sz + else: + print " " % sz + print " " % cgi.escape(row[0]) + print "" + print "
%s
 %d%d%s
" + print "" + +if __name__ == '__main__': + main(sys.argv) + + diff --git a/make/tools/compliance/Android.bp b/make/tools/compliance/Android.bp new file mode 100644 index 0000000..225f3a5 --- /dev/null +++ b/make/tools/compliance/Android.bp @@ -0,0 +1,164 @@ +// +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +blueprint_go_binary { + name: "compliance_checkshare", + srcs: ["cmd/checkshare/checkshare.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/checkshare/checkshare_test.go"], +} + +blueprint_go_binary { + name: "compliancenotice_bom", + srcs: ["cmd/bom/bom.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/bom/bom_test.go"], +} + +blueprint_go_binary { + name: "compliancenotice_shippedlibs", + srcs: ["cmd/shippedlibs/shippedlibs.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/shippedlibs/shippedlibs_test.go"], +} + +blueprint_go_binary { + name: "compliance_listshare", + srcs: ["cmd/listshare/listshare.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/listshare/listshare_test.go"], +} + +blueprint_go_binary { + name: "compliance_dumpgraph", + srcs: ["cmd/dumpgraph/dumpgraph.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/dumpgraph/dumpgraph_test.go"], +} + +blueprint_go_binary { + name: "compliance_dumpresolutions", + srcs: ["cmd/dumpresolutions/dumpresolutions.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/dumpresolutions/dumpresolutions_test.go"], +} + +blueprint_go_binary { + name: "htmlnotice", + srcs: ["cmd/htmlnotice/htmlnotice.go"], + deps: [ + "compliance-module", + "blueprint-deptools", + "soong-response", + ], + testSrcs: ["cmd/htmlnotice/htmlnotice_test.go"], +} + +blueprint_go_binary { + name: "compliance_rtrace", + srcs: ["cmd/rtrace/rtrace.go"], + deps: [ + "compliance-module", + "soong-response", + ], + testSrcs: ["cmd/rtrace/rtrace_test.go"], +} + +blueprint_go_binary { + name: "textnotice", + srcs: ["cmd/textnotice/textnotice.go"], + deps: [ + "compliance-module", + "blueprint-deptools", + "soong-response", + ], + testSrcs: ["cmd/textnotice/textnotice_test.go"], +} + +blueprint_go_binary { + name: "xmlnotice", + srcs: ["cmd/xmlnotice/xmlnotice.go"], + deps: [ + "compliance-module", + "blueprint-deptools", + "soong-response", + ], + testSrcs: ["cmd/xmlnotice/xmlnotice_test.go"], +} + +bootstrap_go_package { + name: "compliance-module", + srcs: [ + "condition.go", + "conditionset.go", + "doc.go", + "graph.go", + "noticeindex.go", + "policy_policy.go", + "policy_resolve.go", + "policy_resolvenotices.go", + "policy_resolveshare.go", + "policy_resolveprivacy.go", + "policy_shareprivacyconflicts.go", + "policy_shipped.go", + "policy_walk.go", + "readgraph.go", + "resolution.go", + "resolutionset.go", + ], + testSrcs: [ + "condition_test.go", + "conditionset_test.go", + "readgraph_test.go", + "policy_policy_test.go", + "policy_resolve_test.go", + "policy_resolvenotices_test.go", + "policy_resolveshare_test.go", + "policy_resolveprivacy_test.go", + "policy_shareprivacyconflicts_test.go", + "policy_shipped_test.go", + "policy_walk_test.go", + "resolutionset_test.go", + "test_util.go", + ], + deps: [ + "golang-protobuf-proto", + "golang-protobuf-encoding-prototext", + "license_metadata_proto", + ], + pkgPath: "android/soong/tools/compliance", +} diff --git a/make/tools/compliance/cmd/bom/bom.go b/make/tools/compliance/cmd/bom/bom.go new file mode 100644 index 0000000..187f828 --- /dev/null +++ b/make/tools/compliance/cmd/bom/bom.go @@ -0,0 +1,189 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + + "android/soong/response" + "android/soong/tools/compliance" +) + +var ( + failNoneRequested = fmt.Errorf("\nNo license metadata files requested") + failNoLicenses = fmt.Errorf("No licenses found") +) + +type context struct { + stdout io.Writer + stderr io.Writer + rootFS fs.FS + stripPrefix []string +} + +func (ctx context) strip(installPath string) string { + for _, prefix := range ctx.stripPrefix { + if strings.HasPrefix(installPath, prefix) { + p := strings.TrimPrefix(installPath, prefix) + if 0 == len(p) { + continue + } + return p + } + } + return installPath +} + +// newMultiString creates a flag that allows multiple values in an array. +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { + var f multiString + flags.Var(&f, name, usage) + return &f +} + +// multiString implements the flag `Value` interface for multiple strings. +type multiString []string + +func (ms *multiString) String() string { return strings.Join(*ms, ", ") } +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } + +func main() { + var expandedArgs []string + for _, arg := range os.Args[1:] { + if strings.HasPrefix(arg, "@") { + f, err := os.Open(strings.TrimPrefix(arg, "@")) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + respArgs, err := response.ReadRspFile(f) + f.Close() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + expandedArgs = append(expandedArgs, respArgs...) + } else { + expandedArgs = append(expandedArgs, arg) + } + } + + flags := flag.NewFlagSet("flags", flag.ExitOnError) + + flags.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} + +Outputs a bill of materials. i.e. the list of installed paths. + +Options: +`, filepath.Base(os.Args[0])) + flags.PrintDefaults() + } + + outputFile := flags.String("o", "-", "Where to write the bill of materials. (default stdout)") + stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") + + flags.Parse(expandedArgs) + + // Must specify at least one root target. + if flags.NArg() == 0 { + flags.Usage() + os.Exit(2) + } + + if len(*outputFile) == 0 { + flags.Usage() + fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") + os.Exit(2) + } else { + dir, err := filepath.Abs(filepath.Dir(*outputFile)) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) + os.Exit(1) + } + fi, err := os.Stat(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) + os.Exit(1) + } + if !fi.IsDir() { + fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) + os.Exit(1) + } + } + + var ofile io.Writer + ofile = os.Stdout + if *outputFile != "-" { + ofile = &bytes.Buffer{} + } + + ctx := &context{ofile, os.Stderr, compliance.FS, *stripPrefix} + + err := billOfMaterials(ctx, flags.Args()...) + if err != nil { + if err == failNoneRequested { + flags.Usage() + } + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + if *outputFile != "-" { + err := os.WriteFile(*outputFile, ofile.(*bytes.Buffer).Bytes(), 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err) + os.Exit(1) + } + } + os.Exit(0) +} + +// billOfMaterials implements the bom utility. +func billOfMaterials(ctx *context, files ...string) error { + // Must be at least one root file. + if len(files) < 1 { + return failNoneRequested + } + + // Read the license graph from the license metadata files (*.meta_lic). + licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files) + if err != nil { + return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err) + } + if licenseGraph == nil { + return failNoLicenses + } + + // rs contains all notice resolutions. + rs := compliance.ResolveNotices(licenseGraph) + + ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs) + if err != nil { + return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err) + } + + for path := range ni.InstallPaths() { + fmt.Fprintln(ctx.stdout, ctx.strip(path)) + } + return nil +} diff --git a/make/tools/compliance/cmd/bom/bom_test.go b/make/tools/compliance/cmd/bom/bom_test.go new file mode 100644 index 0000000..87a3b50 --- /dev/null +++ b/make/tools/compliance/cmd/bom/bom_test.go @@ -0,0 +1,322 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bufio" + "bytes" + "fmt" + "os" + "strings" + "testing" + + "android/soong/tools/compliance" +) + +func TestMain(m *testing.M) { + // Change into the parent directory before running the tests + // so they can find the testdata directory. + if err := os.Chdir(".."); err != nil { + fmt.Printf("failed to change to testdata directory: %s\n", err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +func Test(t *testing.T) { + tests := []struct { + condition string + name string + outDir string + roots []string + stripPrefix string + expectedOut []string + }{ + { + condition: "firstparty", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + stripPrefix: "out/target/product/fictional", + expectedOut: []string{ + "/system/apex/highest.apex", + "/system/apex/highest.apex/bin/bin1", + "/system/apex/highest.apex/bin/bin2", + "/system/apex/highest.apex/lib/liba.so", + "/system/apex/highest.apex/lib/libb.so", + }, + }, + { + condition: "firstparty", + name: "container", + roots: []string{"container.zip.meta_lic"}, + stripPrefix: "out/target/product/fictional/data/", + expectedOut: []string{ + "container.zip", + "container.zip/bin1", + "container.zip/bin2", + "container.zip/liba.so", + "container.zip/libb.so", + }, + }, + { + condition: "firstparty", + name: "application", + roots: []string{"application.meta_lic"}, + stripPrefix: "out/target/product/fictional/bin/", + expectedOut: []string{"application"}, + }, + { + condition: "firstparty", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"bin/bin1"}, + }, + { + condition: "firstparty", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"lib/libd.so"}, + }, + { + condition: "notice", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "out/target/product/fictional/system/apex/highest.apex", + "out/target/product/fictional/system/apex/highest.apex/bin/bin1", + "out/target/product/fictional/system/apex/highest.apex/bin/bin2", + "out/target/product/fictional/system/apex/highest.apex/lib/liba.so", + "out/target/product/fictional/system/apex/highest.apex/lib/libb.so", + }, + }, + { + condition: "notice", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "out/target/product/fictional/data/container.zip", + "out/target/product/fictional/data/container.zip/bin1", + "out/target/product/fictional/data/container.zip/bin2", + "out/target/product/fictional/data/container.zip/liba.so", + "out/target/product/fictional/data/container.zip/libb.so", + }, + }, + { + condition: "notice", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{"out/target/product/fictional/bin/application"}, + }, + { + condition: "notice", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{"out/target/product/fictional/system/bin/bin1"}, + }, + { + condition: "notice", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{"out/target/product/fictional/system/lib/libd.so"}, + }, + { + condition: "reciprocal", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/apex/", + expectedOut: []string{ + "highest.apex", + "highest.apex/bin/bin1", + "highest.apex/bin/bin2", + "highest.apex/lib/liba.so", + "highest.apex/lib/libb.so", + }, + }, + { + condition: "reciprocal", + name: "container", + roots: []string{"container.zip.meta_lic"}, + stripPrefix: "out/target/product/fictional/data/", + expectedOut: []string{ + "container.zip", + "container.zip/bin1", + "container.zip/bin2", + "container.zip/liba.so", + "container.zip/libb.so", + }, + }, + { + condition: "reciprocal", + name: "application", + roots: []string{"application.meta_lic"}, + stripPrefix: "out/target/product/fictional/bin/", + expectedOut: []string{"application"}, + }, + { + condition: "reciprocal", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"bin/bin1"}, + }, + { + condition: "reciprocal", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"lib/libd.so"}, + }, + { + condition: "restricted", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/apex/", + expectedOut: []string{ + "highest.apex", + "highest.apex/bin/bin1", + "highest.apex/bin/bin2", + "highest.apex/lib/liba.so", + "highest.apex/lib/libb.so", + }, + }, + { + condition: "restricted", + name: "container", + roots: []string{"container.zip.meta_lic"}, + stripPrefix: "out/target/product/fictional/data/", + expectedOut: []string{ + "container.zip", + "container.zip/bin1", + "container.zip/bin2", + "container.zip/liba.so", + "container.zip/libb.so", + }, + }, + { + condition: "restricted", + name: "application", + roots: []string{"application.meta_lic"}, + stripPrefix: "out/target/product/fictional/bin/", + expectedOut: []string{"application"}, + }, + { + condition: "restricted", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"bin/bin1"}, + }, + { + condition: "restricted", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"lib/libd.so"}, + }, + { + condition: "proprietary", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/apex/", + expectedOut: []string{ + "highest.apex", + "highest.apex/bin/bin1", + "highest.apex/bin/bin2", + "highest.apex/lib/liba.so", + "highest.apex/lib/libb.so", + }, + }, + { + condition: "proprietary", + name: "container", + roots: []string{"container.zip.meta_lic"}, + stripPrefix: "out/target/product/fictional/data/", + expectedOut: []string{ + "container.zip", + "container.zip/bin1", + "container.zip/bin2", + "container.zip/liba.so", + "container.zip/libb.so", + }, + }, + { + condition: "proprietary", + name: "application", + roots: []string{"application.meta_lic"}, + stripPrefix: "out/target/product/fictional/bin/", + expectedOut: []string{"application"}, + }, + { + condition: "proprietary", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"bin/bin1"}, + }, + { + condition: "proprietary", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + stripPrefix: "out/target/product/fictional/system/", + expectedOut: []string{"lib/libd.so"}, + }, + } + 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), []string{tt.stripPrefix}} + + err := billOfMaterials(&ctx, rootFiles...) + if err != nil { + t.Fatalf("bom: error = %v, stderr = %v", err, stderr) + return + } + if stderr.Len() > 0 { + t.Errorf("bom: gotStderr = %v, want none", stderr) + } + + t.Logf("got stdout: %s", stdout.String()) + + t.Logf("want stdout: %s", strings.Join(tt.expectedOut, "\n")) + + out := bufio.NewScanner(stdout) + lineno := 0 + for out.Scan() { + line := out.Text() + if strings.TrimLeft(line, " ") == "" { + continue + } + if len(tt.expectedOut) <= lineno { + t.Errorf("bom: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut)) + } else if tt.expectedOut[lineno] != line { + t.Errorf("bom: 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]) + } + }) + } +} diff --git a/make/tools/compliance/cmd/checkshare/checkshare.go b/make/tools/compliance/cmd/checkshare/checkshare.go new file mode 100644 index 0000000..f7b4cd2 --- /dev/null +++ b/make/tools/compliance/cmd/checkshare/checkshare.go @@ -0,0 +1,178 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + + "android/soong/response" + "android/soong/tools/compliance" +) + +var ( + failConflicts = fmt.Errorf("conflicts") + failNoneRequested = fmt.Errorf("\nNo metadata files requested") + failNoLicenses = fmt.Errorf("No licenses") +) + +// byError orders conflicts by error string +type byError []compliance.SourceSharePrivacyConflict + +func (l byError) Len() int { return len(l) } +func (l byError) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() } + +func main() { + var expandedArgs []string + for _, arg := range os.Args[1:] { + if strings.HasPrefix(arg, "@") { + f, err := os.Open(strings.TrimPrefix(arg, "@")) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + respArgs, err := response.ReadRspFile(f) + f.Close() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + expandedArgs = append(expandedArgs, respArgs...) + } else { + expandedArgs = append(expandedArgs, arg) + } + } + + flags := flag.NewFlagSet("flags", flag.ExitOnError) + + flags.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...} + +Reports on stderr any targets where policy says that the source both +must and must not be shared. The error report indicates the target, the +license condition that has a source privacy policy, and the license +condition that has a source sharing policy. + +Any given target may appear multiple times with different combinations +of conflicting license conditions. + +If all the source code that policy says must be shared may be shared, +outputs "PASS" to stdout and exits with status 0. + +If policy says any source must both be shared and not be shared, +outputs "FAIL" to stdout and exits with status 1. +`, filepath.Base(os.Args[0])) + flags.PrintDefaults() + } + + outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") + + flags.Parse(expandedArgs) + + // Must specify at least one root target. + if flags.NArg() == 0 { + flags.Usage() + os.Exit(2) + } + + if len(*outputFile) == 0 { + flags.Usage() + fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") + os.Exit(2) + } else { + dir, err := filepath.Abs(filepath.Dir(*outputFile)) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) + os.Exit(1) + } + fi, err := os.Stat(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) + os.Exit(1) + } + if !fi.IsDir() { + fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) + os.Exit(1) + } + } + + var ofile io.Writer + ofile = os.Stdout + var obuf *bytes.Buffer + if *outputFile != "-" { + obuf = &bytes.Buffer{} + ofile = obuf + } + + err := checkShare(ofile, os.Stderr, compliance.FS, flags.Args()...) + if err != nil { + if err != failConflicts { + if err == failNoneRequested { + flags.Usage() + } + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + } + os.Exit(1) + } + if *outputFile != "-" { + err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) + os.Exit(1) + } + } + os.Exit(0) +} + +// checkShare implements the checkshare utility. +func checkShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error { + + if len(files) < 1 { + return failNoneRequested + } + + // Read the license graph from the license metadata files (*.meta_lic). + licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) + if err != nil { + return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %w\n", files, os.Getenv("PWD"), err) + } + if licenseGraph == nil { + return failNoLicenses + } + + // Apply policy to find conflicts and report them to stderr lexicographically ordered. + conflicts := compliance.ConflictingSharedPrivateSource(licenseGraph) + sort.Sort(byError(conflicts)) + for _, conflict := range conflicts { + fmt.Fprintln(stderr, conflict.Error()) + } + + // Indicate pass or fail on stdout. + if len(conflicts) > 0 { + fmt.Fprintln(stdout, "FAIL") + return failConflicts + } + fmt.Fprintln(stdout, "PASS") + return nil +} diff --git a/make/tools/compliance/cmd/checkshare/checkshare_test.go b/make/tools/compliance/cmd/checkshare/checkshare_test.go new file mode 100644 index 0000000..fdcab29 --- /dev/null +++ b/make/tools/compliance/cmd/checkshare/checkshare_test.go @@ -0,0 +1,300 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "fmt" + "os" + "strings" + "testing" + + "android/soong/tools/compliance" +) + +func TestMain(m *testing.M) { + // Change into the parent directory before running the tests + // so they can find the testdata directory. + if err := os.Chdir(".."); err != nil { + fmt.Printf("failed to change to testdata directory: %s\n", err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +type outcome struct { + target string + privacyCondition string + shareCondition string +} + +func (o *outcome) String() string { + return fmt.Sprintf("%s %s and must share from %s", o.target, o.privacyCondition, o.shareCondition) +} + +type outcomeList []*outcome + +func (ol outcomeList) String() string { + result := "" + for _, o := range ol { + result = result + o.String() + "\n" + } + return result +} + +func Test(t *testing.T) { + tests := []struct { + condition string + name string + outDir string + roots []string + expectedStdout string + expectedOutcomes outcomeList + }{ + { + condition: "firstparty", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "firstparty", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "firstparty", + name: "application", + roots: []string{"application.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "firstparty", + name: "binary", + roots: []string{"bin/bin2.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "firstparty", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "notice", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "notice", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "notice", + name: "application", + roots: []string{"application.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "notice", + name: "binary", + roots: []string{"bin/bin2.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "notice", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "reciprocal", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "reciprocal", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "reciprocal", + name: "application", + roots: []string{"application.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "reciprocal", + name: "binary", + roots: []string{"bin/bin2.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "reciprocal", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "restricted", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "restricted", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "restricted", + name: "application", + roots: []string{"application.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "restricted", + name: "binary", + roots: []string{"bin/bin2.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "restricted", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedStdout: "PASS", + }, + { + condition: "proprietary", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedStdout: "FAIL", + expectedOutcomes: outcomeList{ + &outcome{ + target: "testdata/proprietary/bin/bin2.meta_lic", + privacyCondition: "proprietary", + shareCondition: "restricted", + }, + }, + }, + { + condition: "proprietary", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedStdout: "FAIL", + expectedOutcomes: outcomeList{ + &outcome{ + target: "testdata/proprietary/bin/bin2.meta_lic", + privacyCondition: "proprietary", + shareCondition: "restricted", + }, + }, + }, + { + condition: "proprietary", + name: "application", + roots: []string{"application.meta_lic"}, + expectedStdout: "FAIL", + expectedOutcomes: outcomeList{ + &outcome{ + target: "testdata/proprietary/lib/liba.so.meta_lic", + privacyCondition: "proprietary", + shareCondition: "restricted", + }, + }, + }, + { + condition: "proprietary", + name: "binary", + roots: []string{"bin/bin2.meta_lic", "lib/libb.so.meta_lic"}, + expectedStdout: "FAIL", + expectedOutcomes: outcomeList{ + &outcome{ + target: "testdata/proprietary/bin/bin2.meta_lic", + privacyCondition: "proprietary", + shareCondition: "restricted", + }, + }, + }, + { + condition: "proprietary", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedStdout: "PASS", + }, + } + for _, tt := range tests { + t.Run(tt.condition+" "+tt.name, func(t *testing.T) { + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + + rootFiles := make([]string, 0, len(tt.roots)) + for _, r := range tt.roots { + rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) + } + err := checkShare(stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) + if err != nil && err != failConflicts { + t.Fatalf("checkshare: error = %v, stderr = %v", err, stderr) + return + } + var actualStdout string + for _, s := range strings.Split(stdout.String(), "\n") { + ts := strings.TrimLeft(s, " \t") + if len(ts) < 1 { + continue + } + if len(actualStdout) > 0 { + t.Errorf("checkshare: unexpected multiple output lines %q, want %q", actualStdout+"\n"+ts, tt.expectedStdout) + } + actualStdout = ts + } + if actualStdout != tt.expectedStdout { + t.Errorf("checkshare: unexpected stdout %q, want %q", actualStdout, tt.expectedStdout) + } + errList := strings.Split(stderr.String(), "\n") + actualOutcomes := make(outcomeList, 0, len(errList)) + for _, cstring := range errList { + ts := strings.TrimLeft(cstring, " \t") + if len(ts) < 1 { + continue + } + cFields := strings.Split(ts, " ") + actualOutcomes = append(actualOutcomes, &outcome{ + target: cFields[0], + privacyCondition: cFields[1], + shareCondition: cFields[6], + }) + } + if len(actualOutcomes) != len(tt.expectedOutcomes) { + t.Errorf("checkshare: unexpected got %d outcomes %s, want %d outcomes %s", + len(actualOutcomes), actualOutcomes, len(tt.expectedOutcomes), tt.expectedOutcomes) + return + } + for i := range actualOutcomes { + if actualOutcomes[i].String() != tt.expectedOutcomes[i].String() { + t.Errorf("checkshare: unexpected outcome #%d, got %q, want %q", + i+1, actualOutcomes[i], tt.expectedOutcomes[i]) + } + } + }) + } +} diff --git a/make/tools/compliance/cmd/dumpgraph/dumpgraph.go b/make/tools/compliance/cmd/dumpgraph/dumpgraph.go new file mode 100644 index 0000000..5625779 --- /dev/null +++ b/make/tools/compliance/cmd/dumpgraph/dumpgraph.go @@ -0,0 +1,266 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + + "android/soong/response" + "android/soong/tools/compliance" +) + +var ( + failNoneRequested = fmt.Errorf("\nNo license metadata files requested") + failNoLicenses = fmt.Errorf("No licenses found") +) + +type context struct { + graphViz bool + labelConditions bool + stripPrefix []string +} + +func (ctx context) strip(installPath string) string { + for _, prefix := range ctx.stripPrefix { + if strings.HasPrefix(installPath, prefix) { + p := strings.TrimPrefix(installPath, prefix) + if 0 == len(p) { + continue + } + return p + } + } + return installPath +} + +// newMultiString creates a flag that allows multiple values in an array. +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { + var f multiString + flags.Var(&f, name, usage) + return &f +} + +// multiString implements the flag `Value` interface for multiple strings. +type multiString []string + +func (ms *multiString) String() string { return strings.Join(*ms, ", ") } +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } + +func main() { + var expandedArgs []string + for _, arg := range os.Args[1:] { + if strings.HasPrefix(arg, "@") { + f, err := os.Open(strings.TrimPrefix(arg, "@")) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + respArgs, err := response.ReadRspFile(f) + f.Close() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + expandedArgs = append(expandedArgs, respArgs...) + } else { + expandedArgs = append(expandedArgs, arg) + } + } + + flags := flag.NewFlagSet("flags", flag.ExitOnError) + + flags.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} + +Outputs space-separated Target Dependency Annotations tuples for each +edge in the license graph. When -dot flag given, outputs the nodes and +edges in graphViz directed graph format. + +In plain text mode, multiple values within a field are colon-separated. +e.g. multiple annotations appear as annotation1:annotation2:annotation3 +or when -label_conditions is requested, Target and Dependency become +target:condition1:condition2 etc. + +Options: +`, filepath.Base(os.Args[0])) + flags.PrintDefaults() + } + + graphViz := flags.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.") + labelConditions := flags.Bool("label_conditions", false, "Whether to label target nodes with conditions.") + outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") + stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") + + flags.Parse(expandedArgs) + + // Must specify at least one root target. + if flags.NArg() == 0 { + flags.Usage() + os.Exit(2) + } + + if len(*outputFile) == 0 { + flags.Usage() + fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") + os.Exit(2) + } else { + dir, err := filepath.Abs(filepath.Dir(*outputFile)) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) + os.Exit(1) + } + fi, err := os.Stat(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) + os.Exit(1) + } + if !fi.IsDir() { + fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) + os.Exit(1) + } + } + + var ofile io.Writer + ofile = os.Stdout + var obuf *bytes.Buffer + if *outputFile != "-" { + obuf = &bytes.Buffer{} + ofile = obuf + } + + ctx := &context{*graphViz, *labelConditions, *stripPrefix} + + err := dumpGraph(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...) + if err != nil { + if err == failNoneRequested { + flags.Usage() + } + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + if *outputFile != "-" { + err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) + os.Exit(1) + } + } + os.Exit(0) +} + +// dumpGraph implements the dumpgraph utility. +func dumpGraph(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) error { + if len(files) < 1 { + return failNoneRequested + } + + // Read the license graph from the license metadata files (*.meta_lic). + licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) + if err != nil { + return fmt.Errorf("Unable to read license metadata file(s) %q: %w\n", files, err) + } + if licenseGraph == nil { + return failNoLicenses + } + + // Sort the edges of the graph. + edges := licenseGraph.Edges() + sort.Sort(edges) + + // nodes maps license metadata file names to graphViz node names when ctx.graphViz is true. + var nodes map[string]string + n := 0 + + // targetOut calculates the string to output for `target` separating conditions as needed using `sep`. + targetOut := func(target *compliance.TargetNode, sep string) string { + tOut := ctx.strip(target.Name()) + if ctx.labelConditions { + conditions := target.LicenseConditions().Names() + sort.Strings(conditions) + if len(conditions) > 0 { + tOut += sep + strings.Join(conditions, sep) + } + } + return tOut + } + + // makeNode maps `target` to a graphViz node name. + makeNode := func(target *compliance.TargetNode) { + tName := target.Name() + if _, ok := nodes[tName]; !ok { + nodeName := fmt.Sprintf("n%d", n) + nodes[tName] = nodeName + fmt.Fprintf(stdout, "\t%s [label=\"%s\"];\n", nodeName, targetOut(target, "\\n")) + n++ + } + } + + // If graphviz output, map targets to node names, and start the directed graph. + if ctx.graphViz { + nodes = make(map[string]string) + targets := licenseGraph.Targets() + sort.Sort(targets) + + fmt.Fprintf(stdout, "strict digraph {\n\trankdir=RL;\n") + for _, target := range targets { + makeNode(target) + } + } + + // Print the sorted edges to stdout ... + for _, e := range edges { + // sort the annotations for repeatability/stability + annotations := e.Annotations().AsList() + sort.Strings(annotations) + + tName := e.Target().Name() + dName := e.Dependency().Name() + + if ctx.graphViz { + // ... one edge per line labelled with \\n-separated annotations. + tNode := nodes[tName] + dNode := nodes[dName] + fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", dNode, tNode, strings.Join(annotations, "\\n")) + } else { + // ... one edge per line with annotations in a colon-separated tuple. + fmt.Fprintf(stdout, "%s %s %s\n", targetOut(e.Target(), ":"), targetOut(e.Dependency(), ":"), strings.Join(annotations, ":")) + } + } + + // If graphViz output, rank the root nodes together, and complete the directed graph. + if ctx.graphViz { + fmt.Fprintf(stdout, "\t{rank=same;") + for _, f := range files { + fName := f + if !strings.HasSuffix(fName, ".meta_lic") { + fName += ".meta_lic" + } + if fNode, ok := nodes[fName]; ok { + fmt.Fprintf(stdout, " %s", fNode) + } + } + fmt.Fprintf(stdout, "}\n}\n") + } + return nil +} diff --git a/make/tools/compliance/cmd/dumpgraph/dumpgraph_test.go b/make/tools/compliance/cmd/dumpgraph/dumpgraph_test.go new file mode 100644 index 0000000..d1deed3 --- /dev/null +++ b/make/tools/compliance/cmd/dumpgraph/dumpgraph_test.go @@ -0,0 +1,1273 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "fmt" + "os" + "strings" + "testing" + + "android/soong/tools/compliance" +) + +func TestMain(m *testing.M) { + // Change into the parent directory before running the tests + // so they can find the testdata directory. + if err := os.Chdir(".."); err != nil { + fmt.Printf("failed to change to testdata directory: %s\n", err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +func Test_plaintext(t *testing.T) { + tests := []struct { + condition string + name string + outDir string + roots []string + ctx context + expectedOut []string + }{ + { + condition: "firstparty", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static", + "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic", + "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic static", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic static", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic static", + }, + }, + { + condition: "firstparty", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic static", + "bin/bin1.meta_lic lib/libc.a.meta_lic static", + "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", + "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", + "highest.apex.meta_lic bin/bin1.meta_lic static", + "highest.apex.meta_lic bin/bin2.meta_lic static", + "highest.apex.meta_lic lib/liba.so.meta_lic static", + "highest.apex.meta_lic lib/libb.so.meta_lic static", + }, + }, + { + condition: "firstparty", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static", + "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic", + "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static", + }, + }, + { + condition: "firstparty", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static", + "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic", + "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic static", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic static", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic static", + }, + }, + { + condition: "firstparty", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/application.meta_lic testdata/firstparty/bin/bin3.meta_lic toolchain", + "testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", + "testdata/firstparty/application.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic", + }, + }, + { + condition: "firstparty", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static", + }, + }, + { + condition: "firstparty", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{}, + }, + { + condition: "notice", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static", + "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic", + "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic", + "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic static", + "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic static", + "testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic static", + "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic static", + }, + }, + { + condition: "notice", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic static", + "bin/bin1.meta_lic lib/libc.a.meta_lic static", + "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", + "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", + "highest.apex.meta_lic bin/bin1.meta_lic static", + "highest.apex.meta_lic bin/bin2.meta_lic static", + "highest.apex.meta_lic lib/liba.so.meta_lic static", + "highest.apex.meta_lic lib/libb.so.meta_lic static", + }, + }, + { + condition: "notice", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static", + "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic", + "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static", + }, + }, + { + condition: "notice", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static", + "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic", + "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic", + "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic static", + "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic static", + "testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic static", + "testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic static", + }, + }, + { + condition: "notice", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/notice/application.meta_lic testdata/notice/bin/bin3.meta_lic toolchain", + "testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic static", + "testdata/notice/application.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic", + }, + }, + { + condition: "notice", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static", + }, + }, + { + condition: "notice", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{}, + }, + { + condition: "reciprocal", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static", + "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic", + "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic static", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic static", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic static", + "bin/bin1.meta_lic lib/libc.a.meta_lic static", + "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", + "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", + "highest.apex.meta_lic bin/bin1.meta_lic static", + "highest.apex.meta_lic bin/bin2.meta_lic static", + "highest.apex.meta_lic lib/liba.so.meta_lic static", + "highest.apex.meta_lic lib/libb.so.meta_lic static", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal static", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static", + "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic", + "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal static", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static", + }, + }, + { + condition: "reciprocal", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static", + "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic", + "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic static", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic static", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static", + }, + }, + { + condition: "reciprocal", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/application.meta_lic testdata/reciprocal/bin/bin3.meta_lic toolchain", + "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", + "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic", + }, + }, + { + condition: "reciprocal", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static", + }, + }, + { + condition: "reciprocal", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{}, + }, + { + condition: "restricted", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic static", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic static", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic static", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic static", + }, + }, + { + condition: "restricted", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic static", + "bin/bin1.meta_lic lib/libc.a.meta_lic static", + "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", + "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", + "highest.apex.meta_lic bin/bin1.meta_lic static", + "highest.apex.meta_lic bin/bin2.meta_lic static", + "highest.apex.meta_lic lib/liba.so.meta_lic static", + "highest.apex.meta_lic lib/libb.so.meta_lic static", + }, + }, + { + condition: "restricted", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking static", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static", + "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic", + "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking static", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static", + }, + }, + { + condition: "restricted", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic", + "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic static", + "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic static", + "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic static", + "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic static", + }, + }, + { + condition: "restricted", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/application.meta_lic testdata/restricted/bin/bin3.meta_lic toolchain", + "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic static", + "testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic", + }, + }, + { + condition: "restricted", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static", + }, + }, + { + condition: "restricted", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{}, + }, + { + condition: "proprietary", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic static", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic static", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic static", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic static", + "bin/bin1.meta_lic lib/libc.a.meta_lic static", + "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", + "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", + "highest.apex.meta_lic bin/bin1.meta_lic static", + "highest.apex.meta_lic bin/bin2.meta_lic static", + "highest.apex.meta_lic lib/liba.so.meta_lic static", + "highest.apex.meta_lic lib/libb.so.meta_lic static", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary static", + "bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted dynamic", + "bin/bin2.meta_lic:by_exception_only:proprietary lib/libd.so.meta_lic:notice dynamic", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary static", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static", + }, + }, + { + condition: "proprietary", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic static", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic static", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic static", + }, + }, + { + condition: "proprietary", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/application.meta_lic testdata/proprietary/bin/bin3.meta_lic toolchain", + "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", + "testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic", + }, + }, + { + condition: "proprietary", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static", + }, + }, + { + condition: "proprietary", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{}, + }, + } + for _, tt := range tests { + t.Run(tt.condition+" "+tt.name, func(t *testing.T) { + expectedOut := &bytes.Buffer{} + for _, eo := range tt.expectedOut { + expectedOut.WriteString(eo) + expectedOut.WriteString("\n") + } + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + + rootFiles := make([]string, 0, len(tt.roots)) + for _, r := range tt.roots { + rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) + } + err := dumpGraph(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) + if err != nil { + t.Fatalf("dumpgraph: error = %v, stderr = %v", err, stderr) + return + } + if stderr.Len() > 0 { + t.Errorf("dumpgraph: gotStderr = %v, want none", stderr) + } + out := stdout.String() + expected := expectedOut.String() + if out != expected { + outList := strings.Split(out, "\n") + expectedList := strings.Split(expected, "\n") + startLine := 0 + for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] { + startLine++ + } + t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v", + out, expected, startLine+1, outList[startLine], expectedList[startLine]) + } + }) + } +} + +type testContext struct { + nextNode int + nodes map[string]string +} + +type matcher interface { + matchString(*testContext) string + typeString() string +} + +type targetMatcher struct { + target string + conditions []string +} + +func (tm *targetMatcher) matchString(ctx *testContext) string { + m := tm.target + if len(tm.conditions) > 0 { + m += "\\n" + strings.Join(tm.conditions, "\\n") + } + m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];" + return m +} + +func (tm *targetMatcher) typeString() string { + return "target" +} + +type edgeMatcher struct { + target string + dep string + annotations []string +} + +func (em *edgeMatcher) matchString(ctx *testContext) string { + return ctx.nodes[em.dep] + " -> " + ctx.nodes[em.target] + " [label=\"" + strings.Join(em.annotations, "\\n") + "\"];" +} + +func (tm *edgeMatcher) typeString() string { + return "edge" +} + +type getMatcher func(*testContext) matcher + +func matchTarget(target string, conditions ...string) getMatcher { + return func(ctx *testContext) matcher { + ctx.nodes[target] = fmt.Sprintf("n%d", ctx.nextNode) + ctx.nextNode++ + return &targetMatcher{target, append([]string{}, conditions...)} + } +} + +func matchEdge(target, dep string, annotations ...string) getMatcher { + return func(ctx *testContext) matcher { + if _, ok := ctx.nodes[target]; !ok { + panic(fmt.Errorf("no node for target %v in %v -> %v [label=\"%s\"];", target, dep, target, strings.Join(annotations, "\\n"))) + } + if _, ok := ctx.nodes[dep]; !ok { + panic(fmt.Errorf("no node for dep %v in %v -> %v [label=\"%s\"];", target, dep, target, strings.Join(annotations, "\\n"))) + } + return &edgeMatcher{target, dep, append([]string{}, annotations...)} + } +} + +func Test_graphviz(t *testing.T) { + tests := []struct { + condition string + name string + outDir string + roots []string + ctx context + expectedOut []getMatcher + }{ + { + condition: "firstparty", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/bin/bin1.meta_lic"), + matchTarget("testdata/firstparty/bin/bin2.meta_lic"), + matchTarget("testdata/firstparty/highest.apex.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), + matchTarget("testdata/firstparty/lib/libd.so.meta_lic"), + matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "static"), + matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "static"), + matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "firstparty", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("lib/libd.so.meta_lic"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "firstparty", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "notice"), + matchTarget("lib/libb.so.meta_lic", "notice"), + matchTarget("lib/libc.a.meta_lic", "notice"), + matchTarget("lib/libd.so.meta_lic", "notice"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "firstparty", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/bin/bin1.meta_lic"), + matchTarget("testdata/firstparty/bin/bin2.meta_lic"), + matchTarget("testdata/firstparty/container.zip.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), + matchTarget("testdata/firstparty/lib/libd.so.meta_lic"), + matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "static"), + matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "static"), + matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "firstparty", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/application.meta_lic"), + matchTarget("testdata/firstparty/bin/bin3.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), + matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/bin/bin3.meta_lic", "toolchain"), + matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"), + }, + }, + { + condition: "firstparty", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/bin/bin1.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), + matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"), + }, + }, + { + condition: "firstparty", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{matchTarget("testdata/firstparty/lib/libd.so.meta_lic")}, + }, + { + condition: "notice", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/bin/bin1.meta_lic"), + matchTarget("testdata/notice/bin/bin2.meta_lic"), + matchTarget("testdata/notice/highest.apex.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libb.so.meta_lic"), + matchTarget("testdata/notice/lib/libc.a.meta_lic"), + matchTarget("testdata/notice/lib/libd.so.meta_lic"), + matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin1.meta_lic", "static"), + matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin2.meta_lic", "static"), + matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "notice", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("lib/libd.so.meta_lic"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "notice", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "notice"), + matchTarget("lib/libb.so.meta_lic", "notice"), + matchTarget("lib/libc.a.meta_lic", "notice"), + matchTarget("lib/libd.so.meta_lic", "notice"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "notice", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/bin/bin1.meta_lic"), + matchTarget("testdata/notice/bin/bin2.meta_lic"), + matchTarget("testdata/notice/container.zip.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libb.so.meta_lic"), + matchTarget("testdata/notice/lib/libc.a.meta_lic"), + matchTarget("testdata/notice/lib/libd.so.meta_lic"), + matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin1.meta_lic", "static"), + matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin2.meta_lic", "static"), + matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "notice", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/application.meta_lic"), + matchTarget("testdata/notice/bin/bin3.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libb.so.meta_lic"), + matchEdge("testdata/notice/application.meta_lic", "testdata/notice/bin/bin3.meta_lic", "toolchain"), + matchEdge("testdata/notice/application.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/notice/application.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"), + }, + }, + { + condition: "notice", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/bin/bin1.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libc.a.meta_lic"), + matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"), + }, + }, + { + condition: "notice", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{matchTarget("testdata/notice/lib/libd.so.meta_lic")}, + }, + { + condition: "reciprocal", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), + matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), + matchTarget("testdata/reciprocal/highest.apex.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), + matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"), + matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "static"), + matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "static"), + matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("lib/libd.so.meta_lic"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "reciprocal"), + matchTarget("lib/libb.so.meta_lic", "notice"), + matchTarget("lib/libc.a.meta_lic", "reciprocal"), + matchTarget("lib/libd.so.meta_lic", "notice"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "reciprocal", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), + matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), + matchTarget("testdata/reciprocal/container.zip.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), + matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"), + matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "static"), + matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "static"), + matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "reciprocal", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/application.meta_lic"), + matchTarget("testdata/reciprocal/bin/bin3.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), + matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/bin/bin3.meta_lic", "toolchain"), + matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"), + }, + }, + { + condition: "reciprocal", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), + matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"), + }, + }, + { + condition: "reciprocal", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{matchTarget("testdata/reciprocal/lib/libd.so.meta_lic")}, + }, + { + condition: "restricted", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/bin/bin1.meta_lic"), + matchTarget("testdata/restricted/bin/bin2.meta_lic"), + matchTarget("testdata/restricted/highest.apex.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libb.so.meta_lic"), + matchTarget("testdata/restricted/lib/libc.a.meta_lic"), + matchTarget("testdata/restricted/lib/libd.so.meta_lic"), + matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "static"), + matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "static"), + matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("lib/libd.so.meta_lic"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "restricted_allows_dynamic_linking"), + matchTarget("lib/libb.so.meta_lic", "restricted"), + matchTarget("lib/libc.a.meta_lic", "reciprocal"), + matchTarget("lib/libd.so.meta_lic", "notice"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "restricted", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/bin/bin1.meta_lic"), + matchTarget("testdata/restricted/bin/bin2.meta_lic"), + matchTarget("testdata/restricted/container.zip.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libb.so.meta_lic"), + matchTarget("testdata/restricted/lib/libc.a.meta_lic"), + matchTarget("testdata/restricted/lib/libd.so.meta_lic"), + matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "static"), + matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "static"), + matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "restricted", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/application.meta_lic"), + matchTarget("testdata/restricted/bin/bin3.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libb.so.meta_lic"), + matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/bin/bin3.meta_lic", "toolchain"), + matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"), + }, + }, + { + condition: "restricted", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/bin/bin1.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libc.a.meta_lic"), + matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"), + }, + }, + { + condition: "restricted", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{matchTarget("testdata/restricted/lib/libd.so.meta_lic")}, + }, + { + condition: "proprietary", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/bin/bin1.meta_lic"), + matchTarget("testdata/proprietary/bin/bin2.meta_lic"), + matchTarget("testdata/proprietary/highest.apex.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), + matchTarget("testdata/proprietary/lib/libd.so.meta_lic"), + matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "static"), + matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "static"), + matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("lib/libd.so.meta_lic"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "by_exception_only", "proprietary"), + matchTarget("lib/libb.so.meta_lic", "restricted"), + matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"), + matchTarget("lib/libd.so.meta_lic", "notice"), + matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), + matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), + matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), + matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), + matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "proprietary", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/bin/bin1.meta_lic"), + matchTarget("testdata/proprietary/bin/bin2.meta_lic"), + matchTarget("testdata/proprietary/container.zip.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), + matchTarget("testdata/proprietary/lib/libd.so.meta_lic"), + matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"), + matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"), + matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "dynamic"), + matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "static"), + matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "static"), + matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "static"), + }, + }, + { + condition: "proprietary", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/application.meta_lic"), + matchTarget("testdata/proprietary/bin/bin3.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), + matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/bin/bin3.meta_lic", "toolchain"), + matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"), + }, + }, + { + condition: "proprietary", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/bin/bin1.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), + matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), + matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"), + }, + }, + { + condition: "proprietary", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{matchTarget("testdata/proprietary/lib/libd.so.meta_lic")}, + }, + } + for _, tt := range tests { + t.Run(tt.condition+" "+tt.name, func(t *testing.T) { + ctx := &testContext{0, make(map[string]string)} + + expectedOut := &bytes.Buffer{} + for _, eo := range tt.expectedOut { + m := eo(ctx) + expectedOut.WriteString(m.matchString(ctx)) + expectedOut.WriteString("\n") + } + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + + rootFiles := make([]string, 0, len(tt.roots)) + for _, r := range tt.roots { + rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) + } + tt.ctx.graphViz = true + err := dumpGraph(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) + if err != nil { + t.Fatalf("dumpgraph: error = %v, stderr = %v", err, stderr) + return + } + if stderr.Len() > 0 { + t.Errorf("dumpgraph: gotStderr = %v, want none", stderr) + } + outList := strings.Split(stdout.String(), "\n") + outLine := 0 + if outList[outLine] != "strict digraph {" { + t.Errorf("dumpgraph: got 1st line %v, want strict digraph {", outList[outLine]) + } + outLine++ + if strings.HasPrefix(strings.TrimLeft(outList[outLine], " \t"), "rankdir") { + outLine++ + } + endOut := len(outList) + for endOut > 0 && strings.TrimLeft(outList[endOut-1], " \t") == "" { + endOut-- + } + if outList[endOut-1] != "}" { + t.Errorf("dumpgraph: got last line %v, want }", outList[endOut-1]) + } + endOut-- + if strings.HasPrefix(strings.TrimLeft(outList[endOut-1], " \t"), "{rank=same") { + endOut-- + } + expectedList := strings.Split(expectedOut.String(), "\n") + for len(expectedList) > 0 && expectedList[len(expectedList)-1] == "" { + expectedList = expectedList[0 : len(expectedList)-1] + } + matchLine := 0 + + for outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], " \t") == expectedList[matchLine] { + outLine++ + matchLine++ + } + if outLine < endOut || matchLine < len(expectedList) { + if outLine >= endOut { + t.Errorf("dumpgraph: missing lines at end of graph, want %d lines %v", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], "\n")) + } else if matchLine >= len(expectedList) { + t.Errorf("dumpgraph: unexpected lines at end of graph starting line %d, got %v, want nothing", outLine+1, strings.Join(outList[outLine:], "\n")) + } else { + t.Errorf("dumpgraph: at line %d, got %v, want %v", outLine+1, strings.Join(outList[outLine:], "\n"), strings.Join(expectedList[matchLine:], "\n")) + } + } + }) + } +} diff --git a/make/tools/compliance/cmd/dumpresolutions/dumpresolutions.go b/make/tools/compliance/cmd/dumpresolutions/dumpresolutions.go new file mode 100644 index 0000000..dc0cf88 --- /dev/null +++ b/make/tools/compliance/cmd/dumpresolutions/dumpresolutions.go @@ -0,0 +1,312 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + + "android/soong/response" + "android/soong/tools/compliance" +) + +var ( + failNoneRequested = fmt.Errorf("\nNo license metadata files requested") + failNoLicenses = fmt.Errorf("No licenses found") +) + +type context struct { + conditions []compliance.LicenseCondition + graphViz bool + labelConditions bool + stripPrefix []string +} + +func (ctx context) strip(installPath string) string { + for _, prefix := range ctx.stripPrefix { + if strings.HasPrefix(installPath, prefix) { + p := strings.TrimPrefix(installPath, prefix) + if 0 == len(p) { + continue + } + return p + } + } + return installPath +} + +// newMultiString creates a flag that allows multiple values in an array. +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { + var f multiString + flags.Var(&f, name, usage) + return &f +} + +// multiString implements the flag `Value` interface for multiple strings. +type multiString []string + +func (ms *multiString) String() string { return strings.Join(*ms, ", ") } +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } + +func main() { + var expandedArgs []string + for _, arg := range os.Args[1:] { + if strings.HasPrefix(arg, "@") { + f, err := os.Open(strings.TrimPrefix(arg, "@")) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + respArgs, err := response.ReadRspFile(f) + f.Close() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + expandedArgs = append(expandedArgs, respArgs...) + } else { + expandedArgs = append(expandedArgs, arg) + } + } + + flags := flag.NewFlagSet("flags", flag.ExitOnError) + + flags.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} + +Outputs a space-separated Target ActsOn Origin Condition tuple for each +resolution in the graph. When -dot flag given, outputs nodes and edges +in graphviz directed graph format. + +If one or more '-c condition' conditions are given, outputs the +resolution for the union of the conditions. Otherwise, outputs the +resolution for all conditions. + +In plain text mode, when '-label_conditions' is requested, the Target +and Origin have colon-separated license conditions appended: +i.e. target:condition1:condition2 etc. + +Options: +`, filepath.Base(os.Args[0])) + flags.PrintDefaults() + } + + conditions := newMultiString(flags, "c", "License condition to resolve. (may be given multiple times)") + graphViz := flags.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.") + labelConditions := flags.Bool("label_conditions", false, "Whether to label target nodes with conditions.") + outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") + stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") + + flags.Parse(expandedArgs) + + // Must specify at least one root target. + if flags.NArg() == 0 { + flags.Usage() + os.Exit(2) + } + + if len(*outputFile) == 0 { + flags.Usage() + fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") + os.Exit(2) + } else { + dir, err := filepath.Abs(filepath.Dir(*outputFile)) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) + os.Exit(1) + } + fi, err := os.Stat(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) + os.Exit(1) + } + if !fi.IsDir() { + fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) + os.Exit(1) + } + } + + var ofile io.Writer + ofile = os.Stdout + var obuf *bytes.Buffer + if *outputFile != "-" { + obuf = &bytes.Buffer{} + ofile = obuf + } + + lcs := make([]compliance.LicenseCondition, 0, len(*conditions)) + for _, name := range *conditions { + lcs = append(lcs, compliance.RecognizedConditionNames[name]) + } + ctx := &context{ + conditions: lcs, + graphViz: *graphViz, + labelConditions: *labelConditions, + stripPrefix: *stripPrefix, + } + _, err := dumpResolutions(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...) + if err != nil { + if err == failNoneRequested { + flags.Usage() + } + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + if *outputFile != "-" { + err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) + os.Exit(1) + } + } + os.Exit(0) +} + +// dumpResolutions implements the dumpresolutions utility. +func dumpResolutions(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) (*compliance.LicenseGraph, error) { + if len(files) < 1 { + return nil, failNoneRequested + } + + // Read the license graph from the license metadata files (*.meta_lic). + licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) + if err != nil { + return nil, fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err) + } + if licenseGraph == nil { + return nil, failNoLicenses + } + + compliance.ResolveTopDownConditions(licenseGraph) + cs := compliance.AllLicenseConditions + if len(ctx.conditions) > 0 { + cs = compliance.NewLicenseConditionSet() + for _, c := range ctx.conditions { + cs = cs.Plus(c) + } + } + + resolutions := compliance.WalkResolutionsForCondition(licenseGraph, cs) + + // nodes maps license metadata file names to graphViz node names when graphViz requested. + nodes := make(map[string]string) + n := 0 + + // targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed. + targetOut := func(target *compliance.TargetNode, sep string) string { + tOut := ctx.strip(target.Name()) + if ctx.labelConditions { + conditions := target.LicenseConditions().Names() + if len(conditions) > 0 { + tOut += sep + strings.Join(conditions, sep) + } + } + return tOut + } + + // makeNode maps `target` to a graphViz node name. + makeNode := func(target *compliance.TargetNode) { + tName := target.Name() + if _, ok := nodes[tName]; !ok { + nodeName := fmt.Sprintf("n%d", n) + nodes[tName] = nodeName + fmt.Fprintf(stdout, "\t%s [label=\"%s\"];\n", nodeName, targetOut(target, "\\n")) + n++ + } + } + + // outputResolution prints a resolution in the requested format to `stdout`, where one can read + // a resolution as `tname` resolves `oname`'s conditions named in `cnames`. + // `tname` is the name of the target the resolution applies to. + // `cnames` is the list of conditions to resolve. + outputResolution := func(tname, aname string, cnames []string) { + if ctx.graphViz { + // ... one edge per line labelled with \\n-separated annotations. + tNode := nodes[tname] + aNode := nodes[aname] + fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", tNode, aNode, strings.Join(cnames, "\\n")) + } else { + // ... one edge per line with names in a colon-separated tuple. + fmt.Fprintf(stdout, "%s %s %s\n", tname, aname, strings.Join(cnames, ":")) + } + } + + // Sort the resolutions by targetname for repeatability/stability. + targets := resolutions.AttachesTo() + sort.Sort(targets) + + // If graphviz output, start the directed graph. + if ctx.graphViz { + fmt.Fprintf(stdout, "strict digraph {\n\trankdir=LR;\n") + for _, target := range targets { + makeNode(target) + rl := resolutions.Resolutions(target) + sort.Sort(rl) + for _, r := range rl { + makeNode(r.ActsOn()) + } + } + } + + // Output the sorted targets. + for _, target := range targets { + var tname string + if ctx.graphViz { + tname = target.Name() + } else { + tname = targetOut(target, ":") + } + + rl := resolutions.Resolutions(target) + sort.Sort(rl) + for _, r := range rl { + var aname string + if ctx.graphViz { + aname = r.ActsOn().Name() + } else { + aname = targetOut(r.ActsOn(), ":") + } + + // cnames accumulates the list of condition names originating at a single origin that apply to `target`. + cnames := r.Resolves().Names() + + // Output 1 line for each attachesTo+actsOn combination. + outputResolution(tname, aname, cnames) + } + } + // If graphViz output, rank the root nodes together, and complete the directed graph. + if ctx.graphViz { + fmt.Fprintf(stdout, "\t{rank=same;") + for _, f := range files { + fName := f + if !strings.HasSuffix(fName, ".meta_lic") { + fName += ".meta_lic" + } + if fNode, ok := nodes[fName]; ok { + fmt.Fprintf(stdout, " %s", fNode) + } + } + fmt.Fprintf(stdout, "}\n}\n") + } + return licenseGraph, nil +} diff --git a/make/tools/compliance/cmd/dumpresolutions/dumpresolutions_test.go b/make/tools/compliance/cmd/dumpresolutions/dumpresolutions_test.go new file mode 100644 index 0000000..63fd157 --- /dev/null +++ b/make/tools/compliance/cmd/dumpresolutions/dumpresolutions_test.go @@ -0,0 +1,3360 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "fmt" + "os" + "strings" + "testing" + + "android/soong/tools/compliance" +) + +func TestMain(m *testing.M) { + // Change into the parent directory before running the tests + // so they can find the testdata directory. + if err := os.Chdir(".."); err != nil { + fmt.Printf("failed to change to testdata directory: %s\n", err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +func Test_plaintext(t *testing.T) { + tests := []struct { + condition string + name string + outDir string + roots []string + ctx context + expectedOut []string + }{ + { + condition: "firstparty", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", + "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic notice", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", + "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", + "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", + }, + }, + { + condition: "firstparty", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin1.meta_lic lib/liba.so.meta_lic notice", + "bin/bin1.meta_lic lib/libc.a.meta_lic notice", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + "highest.apex.meta_lic lib/liba.so.meta_lic notice", + "highest.apex.meta_lic lib/libb.so.meta_lic notice", + "highest.apex.meta_lic lib/libc.a.meta_lic notice", + "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", + "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", + }, + }, + { + condition: "firstparty", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin1.meta_lic lib/liba.so.meta_lic notice", + "bin/bin1.meta_lic lib/libc.a.meta_lic notice", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + "highest.apex.meta_lic lib/liba.so.meta_lic notice", + "highest.apex.meta_lic lib/libb.so.meta_lic notice", + "highest.apex.meta_lic lib/libc.a.meta_lic notice", + "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", + "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", + }, + }, + { + condition: "firstparty", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []string{}, + }, + { + condition: "firstparty", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []string{}, + }, + { + condition: "firstparty", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: append(compliance.ImpliesPrivate.AsList(), compliance.ImpliesShared.AsList()...), + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []string{}, + }, + { + condition: "firstparty", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice", + "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice", + "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice", + "lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice", + "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice", + }, + }, + { + condition: "firstparty", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", + "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic notice", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", + "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", + "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", + }, + }, + { + condition: "firstparty", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic notice", + "testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + }, + }, + { + condition: "firstparty", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", + "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", + }, + }, + { + condition: "firstparty", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{ + "testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice", + }, + }, + { + condition: "notice", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice", + "testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice", + "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic notice", + "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic notice", + "testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic notice", + "testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic notice", + "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libc.a.meta_lic notice", + "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice", + }, + }, + { + condition: "notice", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin1.meta_lic lib/liba.so.meta_lic notice", + "bin/bin1.meta_lic lib/libc.a.meta_lic notice", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + "highest.apex.meta_lic lib/liba.so.meta_lic notice", + "highest.apex.meta_lic lib/libb.so.meta_lic notice", + "highest.apex.meta_lic lib/libc.a.meta_lic notice", + "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", + "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", + }, + }, + { + condition: "notice", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin1.meta_lic lib/liba.so.meta_lic notice", + "bin/bin1.meta_lic lib/libc.a.meta_lic notice", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + "highest.apex.meta_lic lib/liba.so.meta_lic notice", + "highest.apex.meta_lic lib/libb.so.meta_lic notice", + "highest.apex.meta_lic lib/libc.a.meta_lic notice", + "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", + "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", + }, + }, + { + condition: "notice", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []string{}, + }, + { + condition: "notice", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []string{}, + }, + { + condition: "notice", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []string{}, + }, + { + condition: "notice", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice", + "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice", + "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice", + "lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice", + "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice", + }, + }, + { + condition: "notice", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice", + "testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice", + "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic notice", + "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic notice", + "testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic notice", + "testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic notice", + "testdata/notice/container.zip.meta_lic testdata/notice/lib/libc.a.meta_lic notice", + "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice", + }, + }, + { + condition: "notice", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/notice/application.meta_lic testdata/notice/application.meta_lic notice", + "testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + }, + }, + { + condition: "notice", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice", + "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice", + }, + }, + { + condition: "notice", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{ + "testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice", + }, + }, + { + condition: "reciprocal", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", + "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic notice", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", + "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", + "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal", + "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + "highest.apex.meta_lic lib/liba.so.meta_lic reciprocal", + "highest.apex.meta_lic lib/libb.so.meta_lic notice", + "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal", + "lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal", + "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + "highest.apex.meta_lic lib/libb.so.meta_lic notice", + "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal", + "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal", + "highest.apex.meta_lic lib/liba.so.meta_lic reciprocal", + "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal", + "lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []string{}, + }, + { + condition: "reciprocal", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal", + "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal", + "highest.apex.meta_lic lib/liba.so.meta_lic reciprocal", + "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal", + "lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal", + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal", + "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice", + "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice", + "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal", + "lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal", + "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice", + }, + }, + { + condition: "reciprocal", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", + "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic notice", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", + "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", + "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", + }, + }, + { + condition: "reciprocal", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic notice", + "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + }, + }, + { + condition: "reciprocal", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", + "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", + }, + }, + { + condition: "reciprocal", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{ + "testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice", + }, + }, + { + condition: "restricted", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic notice:restricted:restricted_allows_dynamic_linking", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", + "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "restricted", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "bin/bin1.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "bin/bin2.meta_lic bin/bin2.meta_lic notice:restricted", + "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "highest.apex.meta_lic bin/bin2.meta_lic notice:restricted", + "highest.apex.meta_lic highest.apex.meta_lic notice:restricted:restricted_allows_dynamic_linking", + "highest.apex.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "highest.apex.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "restricted", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin2.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + }, + }, + { + condition: "restricted", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking", + "bin/bin1.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "bin/bin2.meta_lic bin/bin2.meta_lic restricted", + "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking", + "highest.apex.meta_lic bin/bin2.meta_lic restricted", + "highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_allows_dynamic_linking", + "highest.apex.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "highest.apex.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "restricted", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []string{}, + }, + { + condition: "restricted", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking", + "bin/bin1.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "bin/bin2.meta_lic bin/bin2.meta_lic restricted", + "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking", + "highest.apex.meta_lic bin/bin2.meta_lic restricted", + "highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_allows_dynamic_linking", + "highest.apex.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "highest.apex.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "restricted", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_allows_dynamic_linking", + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking restricted_allows_dynamic_linking", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_allows_dynamic_linking", + "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted", + "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_allows_dynamic_linking", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted", + "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted:restricted_allows_dynamic_linking", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking restricted_allows_dynamic_linking", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted", + "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_allows_dynamic_linking", + "lib/liba.so.meta_lic:restricted_allows_dynamic_linking lib/liba.so.meta_lic:restricted_allows_dynamic_linking restricted_allows_dynamic_linking", + "lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted", + }, + }, + { + condition: "restricted", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", + "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", + "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", + "testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic notice:restricted:restricted_allows_dynamic_linking", + "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", + "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "restricted", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic notice:restricted:restricted_allows_dynamic_linking", + "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted:restricted_allows_dynamic_linking", + }, + }, + { + condition: "restricted", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking", + "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking", + }, + }, + { + condition: "restricted", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{ + "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice", + }, + }, + { + condition: "proprietary", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic notice:restricted", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", + "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", + "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "bin/bin1.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only", + "bin/bin1.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only", + "bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only", + "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only", + "highest.apex.meta_lic highest.apex.meta_lic notice:restricted", + "highest.apex.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only", + "highest.apex.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only", + "lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only", + "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic bin/bin1.meta_lic notice", + "highest.apex.meta_lic highest.apex.meta_lic notice", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []string{ + "bin/bin2.meta_lic bin/bin2.meta_lic restricted", + "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic bin/bin2.meta_lic restricted", + "highest.apex.meta_lic highest.apex.meta_lic restricted", + "highest.apex.meta_lic lib/libb.so.meta_lic restricted", + "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic proprietary", + "bin/bin1.meta_lic lib/libc.a.meta_lic proprietary", + "bin/bin2.meta_lic bin/bin2.meta_lic proprietary", + "highest.apex.meta_lic bin/bin2.meta_lic proprietary", + "highest.apex.meta_lic lib/liba.so.meta_lic proprietary", + "highest.apex.meta_lic lib/libc.a.meta_lic proprietary", + "lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []string{ + "bin/bin1.meta_lic lib/liba.so.meta_lic proprietary", + "bin/bin1.meta_lic lib/libc.a.meta_lic proprietary", + "bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary", + "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary", + "highest.apex.meta_lic highest.apex.meta_lic restricted", + "highest.apex.meta_lic lib/liba.so.meta_lic proprietary", + "highest.apex.meta_lic lib/libb.so.meta_lic restricted", + "highest.apex.meta_lic lib/libc.a.meta_lic proprietary", + "lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary", + "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, + expectedOut: []string{ + "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", + "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", + "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", + "bin/bin2.meta_lic:proprietary:by_exception_only bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only", + "bin/bin2.meta_lic:proprietary:by_exception_only lib/libb.so.meta_lic:restricted restricted", + "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", + "highest.apex.meta_lic:notice bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only", + "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted", + "highest.apex.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", + "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted", + "highest.apex.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", + "lib/liba.so.meta_lic:proprietary:by_exception_only lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", + "lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted", + }, + }, + { + condition: "proprietary", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", + "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic notice:restricted", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", + "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", + "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", + }, + }, + { + condition: "proprietary", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic notice:restricted", + "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic restricted:proprietary:by_exception_only", + }, + }, + { + condition: "proprietary", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", + "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", + }, + }, + { + condition: "proprietary", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []string{ + "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice", + }, + }, + } + for _, tt := range tests { + t.Run(tt.condition+" "+tt.name, func(t *testing.T) { + expectedOut := &bytes.Buffer{} + for _, eo := range tt.expectedOut { + expectedOut.WriteString(eo) + expectedOut.WriteString("\n") + } + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + + rootFiles := make([]string, 0, len(tt.roots)) + for _, r := range tt.roots { + rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) + } + _, err := dumpResolutions(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) + if err != nil { + t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr) + return + } + if stderr.Len() > 0 { + t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr) + } + out := stdout.String() + expected := expectedOut.String() + if out != expected { + outList := strings.Split(out, "\n") + expectedList := strings.Split(expected, "\n") + startLine := 0 + for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] { + startLine++ + } + t.Errorf("dumpresoliutions: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v", + out, expected, startLine+1, outList[startLine], expectedList[startLine]) + } + }) + } +} + +type testContext struct { + nextNode int + nodes map[string]string +} + +type matcher interface { + matchString(*testContext, *compliance.LicenseGraph) string + typeString() string +} + +type targetMatcher struct { + target string + conditions []string +} + +// newTestCondition constructs a test license condition in the license graph. +func newTestCondition(lg *compliance.LicenseGraph, conditionName ...string) compliance.LicenseConditionSet { + cs := compliance.NewLicenseConditionSet() + for _, name := range conditionName { + cs = cs.Plus(compliance.RecognizedConditionNames[name]) + } + if cs.IsEmpty() && len(conditionName) != 0 { + panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName)) + } + return cs +} + +func (tm *targetMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string { + cs := newTestCondition(lg, tm.conditions...) + m := tm.target + if !cs.IsEmpty() { + m += "\\n" + strings.Join(cs.Names(), "\\n") + } + m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];" + return m +} + +func (tm *targetMatcher) typeString() string { + return "target" +} + +type resolutionMatcher struct { + appliesTo string + actsOn string + conditions []string +} + +func (rm *resolutionMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string { + cs := newTestCondition(lg, rm.conditions...) + return ctx.nodes[rm.appliesTo] + " -> " + ctx.nodes[rm.actsOn] + + " [label=\"" + strings.Join(cs.Names(), "\\n") + "\"];" +} + +func (rm *resolutionMatcher) typeString() string { + return "resolution" +} + +type getMatcher func(*testContext) matcher + +func matchTarget(target string, conditions ...string) getMatcher { + return func(ctx *testContext) matcher { + ctx.nodes[target] = fmt.Sprintf("n%d", ctx.nextNode) + ctx.nextNode++ + return &targetMatcher{target, append([]string{}, conditions...)} + } +} + +func matchResolution(appliesTo, actsOn string, conditions ...string) getMatcher { + return func(ctx *testContext) matcher { + if _, ok := ctx.nodes[appliesTo]; !ok { + ctx.nodes[appliesTo] = fmt.Sprintf("unknown%d", ctx.nextNode) + ctx.nextNode++ + } + if _, ok := ctx.nodes[actsOn]; !ok { + ctx.nodes[actsOn] = fmt.Sprintf("unknown%d", ctx.nextNode) + ctx.nextNode++ + } + return &resolutionMatcher{appliesTo, actsOn, append([]string{}, conditions...)} + } +} + +func Test_graphviz(t *testing.T) { + tests := []struct { + condition string + name string + outDir string + roots []string + ctx context + expectedOut []getMatcher + }{ + { + condition: "firstparty", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/bin/bin1.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), + matchTarget("testdata/firstparty/bin/bin2.meta_lic"), + matchTarget("testdata/firstparty/highest.apex.meta_lic"), + matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin2.meta_lic", + "testdata/firstparty/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/highest.apex.meta_lic", + "testdata/firstparty/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/highest.apex.meta_lic", + "testdata/firstparty/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/highest.apex.meta_lic", + "testdata/firstparty/highest.apex.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/highest.apex.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/highest.apex.meta_lic", + "testdata/firstparty/lib/libb.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/highest.apex.meta_lic", + "testdata/firstparty/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/lib/liba.so.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/lib/libb.so.meta_lic", + "testdata/firstparty/lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "firstparty", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "firstparty", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), + stripPrefix: []string{"testdata/firstparty/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "firstparty", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "notice"), + matchTarget("lib/libc.a.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/libb.so.meta_lic", "notice"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/bin/bin1.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), + matchTarget("testdata/firstparty/bin/bin2.meta_lic"), + matchTarget("testdata/firstparty/container.zip.meta_lic"), + matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin2.meta_lic", + "testdata/firstparty/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/container.zip.meta_lic", + "testdata/firstparty/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/container.zip.meta_lic", + "testdata/firstparty/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/container.zip.meta_lic", + "testdata/firstparty/container.zip.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/container.zip.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/container.zip.meta_lic", + "testdata/firstparty/lib/libb.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/container.zip.meta_lic", + "testdata/firstparty/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/lib/liba.so.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/lib/libb.so.meta_lic", + "testdata/firstparty/lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/application.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchResolution( + "testdata/firstparty/application.meta_lic", + "testdata/firstparty/application.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/application.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/bin/bin1.meta_lic"), + matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), + matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/firstparty/bin/bin1.meta_lic", + "testdata/firstparty/lib/libc.a.meta_lic", + "notice"), + }, + }, + { + condition: "firstparty", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/firstparty/lib/libd.so.meta_lic"), + matchResolution( + "testdata/firstparty/lib/libd.so.meta_lic", + "testdata/firstparty/lib/libd.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/bin/bin1.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libc.a.meta_lic"), + matchTarget("testdata/notice/bin/bin2.meta_lic"), + matchTarget("testdata/notice/highest.apex.meta_lic"), + matchTarget("testdata/notice/lib/libb.so.meta_lic"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin2.meta_lic", + "testdata/notice/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/notice/highest.apex.meta_lic", + "testdata/notice/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/notice/highest.apex.meta_lic", + "testdata/notice/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/notice/highest.apex.meta_lic", + "testdata/notice/highest.apex.meta_lic", + "notice"), + matchResolution( + "testdata/notice/highest.apex.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/highest.apex.meta_lic", + "testdata/notice/lib/libb.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/highest.apex.meta_lic", + "testdata/notice/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/notice/lib/liba.so.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/lib/libb.so.meta_lic", + "testdata/notice/lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "notice", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "notice", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), + stripPrefix: []string{"testdata/notice/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "notice", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "notice"), + matchTarget("lib/libc.a.meta_lic", "notice"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/libb.so.meta_lic", "notice"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "notice"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/bin/bin1.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libc.a.meta_lic"), + matchTarget("testdata/notice/bin/bin2.meta_lic"), + matchTarget("testdata/notice/container.zip.meta_lic"), + matchTarget("testdata/notice/lib/libb.so.meta_lic"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin2.meta_lic", + "testdata/notice/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/notice/container.zip.meta_lic", + "testdata/notice/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/notice/container.zip.meta_lic", + "testdata/notice/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/notice/container.zip.meta_lic", + "testdata/notice/container.zip.meta_lic", + "notice"), + matchResolution( + "testdata/notice/container.zip.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/container.zip.meta_lic", + "testdata/notice/lib/libb.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/container.zip.meta_lic", + "testdata/notice/lib/libc.a.meta_lic", + "notice"), + matchResolution( + "testdata/notice/lib/liba.so.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/lib/libb.so.meta_lic", + "testdata/notice/lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/application.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchResolution( + "testdata/notice/application.meta_lic", + "testdata/notice/application.meta_lic", + "notice"), + matchResolution( + "testdata/notice/application.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/bin/bin1.meta_lic"), + matchTarget("testdata/notice/lib/liba.so.meta_lic"), + matchTarget("testdata/notice/lib/libc.a.meta_lic"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/lib/liba.so.meta_lic", + "notice"), + matchResolution( + "testdata/notice/bin/bin1.meta_lic", + "testdata/notice/lib/libc.a.meta_lic", + "notice"), + }, + }, + { + condition: "notice", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/notice/lib/libd.so.meta_lic"), + matchResolution( + "testdata/notice/lib/libd.so.meta_lic", + "testdata/notice/lib/libd.so.meta_lic", + "notice"), + }, + }, + { + condition: "reciprocal", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), + matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), + matchTarget("testdata/reciprocal/highest.apex.meta_lic"), + matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/bin/bin2.meta_lic", + "testdata/reciprocal/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/highest.apex.meta_lic", + "testdata/reciprocal/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/highest.apex.meta_lic", + "testdata/reciprocal/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/highest.apex.meta_lic", + "testdata/reciprocal/highest.apex.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/highest.apex.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/highest.apex.meta_lic", + "testdata/reciprocal/lib/libb.so.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/highest.apex.meta_lic", + "testdata/reciprocal/lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/lib/liba.so.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/lib/libb.so.meta_lic", + "testdata/reciprocal/lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "reciprocal", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), + stripPrefix: []string{"testdata/reciprocal/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + }, + }, + { + condition: "reciprocal", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "reciprocal"), + matchTarget("lib/libc.a.meta_lic", "reciprocal"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("highest.apex.meta_lic", "notice"), + matchTarget("lib/libb.so.meta_lic", "notice"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "reciprocal", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), + matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), + matchTarget("testdata/reciprocal/container.zip.meta_lic"), + matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/bin/bin2.meta_lic", + "testdata/reciprocal/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/container.zip.meta_lic", + "testdata/reciprocal/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/container.zip.meta_lic", + "testdata/reciprocal/bin/bin2.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/container.zip.meta_lic", + "testdata/reciprocal/container.zip.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/container.zip.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/container.zip.meta_lic", + "testdata/reciprocal/lib/libb.so.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/container.zip.meta_lic", + "testdata/reciprocal/lib/libc.a.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/lib/liba.so.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/lib/libb.so.meta_lic", + "testdata/reciprocal/lib/libb.so.meta_lic", + "notice"), + }, + }, + { + condition: "reciprocal", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/application.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchResolution( + "testdata/reciprocal/application.meta_lic", + "testdata/reciprocal/application.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/application.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + }, + }, + { + condition: "reciprocal", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), + matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), + matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/lib/liba.so.meta_lic", + "reciprocal"), + matchResolution( + "testdata/reciprocal/bin/bin1.meta_lic", + "testdata/reciprocal/lib/libc.a.meta_lic", + "reciprocal"), + }, + }, + { + condition: "reciprocal", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"), + matchResolution( + "testdata/reciprocal/lib/libd.so.meta_lic", + "testdata/reciprocal/lib/libd.so.meta_lic", + "notice"), + }, + }, + { + condition: "restricted", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/bin/bin1.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libc.a.meta_lic"), + matchTarget("testdata/restricted/bin/bin2.meta_lic"), + matchTarget("testdata/restricted/lib/libb.so.meta_lic"), + matchTarget("testdata/restricted/highest.apex.meta_lic"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/bin/bin2.meta_lic", + "testdata/restricted/bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "testdata/restricted/bin/bin2.meta_lic", + "testdata/restricted/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/restricted/highest.apex.meta_lic", + "testdata/restricted/bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/highest.apex.meta_lic", + "testdata/restricted/bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "testdata/restricted/highest.apex.meta_lic", + "testdata/restricted/highest.apex.meta_lic", + "restricted", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/highest.apex.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/highest.apex.meta_lic", + "testdata/restricted/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/restricted/highest.apex.meta_lic", + "testdata/restricted/lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/lib/liba.so.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/lib/libb.so.meta_lic", + "testdata/restricted/lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []getMatcher{}, + }, + { + condition: "restricted", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), + stripPrefix: []string{"testdata/restricted/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "restricted", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "restricted_allows_dynamic_linking"), + matchTarget("lib/libc.a.meta_lic", "reciprocal"), + matchTarget("bin/bin2.meta_lic", "notice"), + matchTarget("lib/libb.so.meta_lic", "restricted"), + matchTarget("highest.apex.meta_lic", "notice"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "restricted", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/bin/bin1.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libc.a.meta_lic"), + matchTarget("testdata/restricted/bin/bin2.meta_lic"), + matchTarget("testdata/restricted/lib/libb.so.meta_lic"), + matchTarget("testdata/restricted/container.zip.meta_lic"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/bin/bin2.meta_lic", + "testdata/restricted/bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "testdata/restricted/bin/bin2.meta_lic", + "testdata/restricted/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/restricted/container.zip.meta_lic", + "testdata/restricted/bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/container.zip.meta_lic", + "testdata/restricted/bin/bin2.meta_lic", + "restricted", + "notice"), + matchResolution( + "testdata/restricted/container.zip.meta_lic", + "testdata/restricted/container.zip.meta_lic", + "restricted", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/container.zip.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/container.zip.meta_lic", + "testdata/restricted/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/restricted/container.zip.meta_lic", + "testdata/restricted/lib/libc.a.meta_lic", + "reciprocal", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/lib/liba.so.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/lib/libb.so.meta_lic", + "testdata/restricted/lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "restricted", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/application.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchResolution( + "testdata/restricted/application.meta_lic", + "testdata/restricted/application.meta_lic", + "restricted", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/application.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking", + "restricted"), + }, + }, + { + condition: "restricted", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/bin/bin1.meta_lic"), + matchTarget("testdata/restricted/lib/liba.so.meta_lic"), + matchTarget("testdata/restricted/lib/libc.a.meta_lic"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/bin/bin1.meta_lic", + "restricted_allows_dynamic_linking", + "notice"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/lib/liba.so.meta_lic", + "restricted_allows_dynamic_linking"), + matchResolution( + "testdata/restricted/bin/bin1.meta_lic", + "testdata/restricted/lib/libc.a.meta_lic", + "restricted_allows_dynamic_linking", + "reciprocal"), + }, + }, + { + condition: "restricted", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/restricted/lib/libd.so.meta_lic"), + matchResolution( + "testdata/restricted/lib/libd.so.meta_lic", + "testdata/restricted/lib/libd.so.meta_lic", + "notice"), + }, + }, + { + condition: "proprietary", + name: "apex", + roots: []string{"highest.apex.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/bin/bin1.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), + matchTarget("testdata/proprietary/bin/bin2.meta_lic"), + matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), + matchTarget("testdata/proprietary/highest.apex.meta_lic"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin2.meta_lic", + "testdata/proprietary/bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin2.meta_lic", + "testdata/proprietary/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/proprietary/highest.apex.meta_lic", + "testdata/proprietary/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/proprietary/highest.apex.meta_lic", + "testdata/proprietary/bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/highest.apex.meta_lic", + "testdata/proprietary/highest.apex.meta_lic", + "restricted", + "notice"), + matchResolution( + "testdata/proprietary/highest.apex.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/highest.apex.meta_lic", + "testdata/proprietary/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/proprietary/highest.apex.meta_lic", + "testdata/proprietary/lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/lib/liba.so.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/lib/libb.so.meta_lic", + "testdata/proprietary/lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "by_exception_only", + "restricted", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_notice", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "notice"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_share", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.AsList(), + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin2.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesPrivate.AsList(), + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "proprietary"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "proprietary"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "proprietary"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_share_private", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{ + conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), + stripPrefix: []string{"testdata/proprietary/"}, + }, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic"), + matchTarget("lib/liba.so.meta_lic"), + matchTarget("lib/libc.a.meta_lic"), + matchTarget("bin/bin2.meta_lic"), + matchTarget("lib/libb.so.meta_lic"), + matchTarget("highest.apex.meta_lic"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "proprietary"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "proprietary"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "proprietary"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "proprietary", + name: "apex_trimmed_labelled", + roots: []string{"highest.apex.meta_lic"}, + ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, + expectedOut: []getMatcher{ + matchTarget("bin/bin1.meta_lic", "notice"), + matchTarget("lib/liba.so.meta_lic", "by_exception_only", "proprietary"), + matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"), + matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"), + matchTarget("lib/libb.so.meta_lic", "restricted"), + matchTarget("highest.apex.meta_lic", "notice"), + matchResolution( + "bin/bin1.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "bin/bin1.meta_lic", + "lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "bin/bin1.meta_lic", + "lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "bin/bin2.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin1.meta_lic", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "highest.apex.meta_lic", + "restricted", + "notice"), + matchResolution( + "highest.apex.meta_lic", + "lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "highest.apex.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "highest.apex.meta_lic", + "lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "lib/liba.so.meta_lic", + "lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "lib/libb.so.meta_lic", + "lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "proprietary", + name: "container", + roots: []string{"container.zip.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/bin/bin1.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), + matchTarget("testdata/proprietary/bin/bin2.meta_lic"), + matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), + matchTarget("testdata/proprietary/container.zip.meta_lic"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin2.meta_lic", + "testdata/proprietary/bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin2.meta_lic", + "testdata/proprietary/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/proprietary/container.zip.meta_lic", + "testdata/proprietary/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/proprietary/container.zip.meta_lic", + "testdata/proprietary/bin/bin2.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/container.zip.meta_lic", + "testdata/proprietary/container.zip.meta_lic", + "restricted", + "notice"), + matchResolution( + "testdata/proprietary/container.zip.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/container.zip.meta_lic", + "testdata/proprietary/lib/libb.so.meta_lic", + "restricted"), + matchResolution( + "testdata/proprietary/container.zip.meta_lic", + "testdata/proprietary/lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/lib/liba.so.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/lib/libb.so.meta_lic", + "testdata/proprietary/lib/libb.so.meta_lic", + "restricted"), + }, + }, + { + condition: "proprietary", + name: "application", + roots: []string{"application.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/application.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchResolution( + "testdata/proprietary/application.meta_lic", + "testdata/proprietary/application.meta_lic", + "notice", + "restricted"), + matchResolution( + "testdata/proprietary/application.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "restricted", + "by_exception_only", + "proprietary"), + }, + }, + { + condition: "proprietary", + name: "binary", + roots: []string{"bin/bin1.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/bin/bin1.meta_lic"), + matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), + matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/bin/bin1.meta_lic", + "notice"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/lib/liba.so.meta_lic", + "by_exception_only", + "proprietary"), + matchResolution( + "testdata/proprietary/bin/bin1.meta_lic", + "testdata/proprietary/lib/libc.a.meta_lic", + "by_exception_only", + "proprietary"), + }, + }, + { + condition: "proprietary", + name: "library", + roots: []string{"lib/libd.so.meta_lic"}, + expectedOut: []getMatcher{ + matchTarget("testdata/proprietary/lib/libd.so.meta_lic"), + matchResolution( + "testdata/proprietary/lib/libd.so.meta_lic", + "testdata/proprietary/lib/libd.so.meta_lic", + "notice"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.condition+" "+tt.name, func(t *testing.T) { + ctx := &testContext{0, make(map[string]string)} + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + + rootFiles := make([]string, 0, len(tt.roots)) + for _, r := range tt.roots { + rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) + } + tt.ctx.graphViz = true + lg, err := dumpResolutions(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) + if err != nil { + t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr) + return + } + if stderr.Len() > 0 { + t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr) + } + + expectedOut := &bytes.Buffer{} + for _, eo := range tt.expectedOut { + m := eo(ctx) + expectedOut.WriteString(m.matchString(ctx, lg)) + expectedOut.WriteString("\n") + } + + outList := strings.Split(stdout.String(), "\n") + outLine := 0 + if outList[outLine] != "strict digraph {" { + t.Errorf("dumpresolutions: got 1st line %v, want strict digraph {", outList[outLine]) + } + outLine++ + if strings.HasPrefix(strings.TrimLeft(outList[outLine], " \t"), "rankdir") { + outLine++ + } + endOut := len(outList) + for endOut > 0 && strings.TrimLeft(outList[endOut-1], " \t") == "" { + endOut-- + } + if outList[endOut-1] != "}" { + t.Errorf("dumpresolutions: got last line %v, want }", outList[endOut-1]) + } + endOut-- + if strings.HasPrefix(strings.TrimLeft(outList[endOut-1], " \t"), "{rank=same") { + endOut-- + } + expectedList := strings.Split(expectedOut.String(), "\n") + for len(expectedList) > 0 && expectedList[len(expectedList)-1] == "" { + expectedList = expectedList[0 : len(expectedList)-1] + } + matchLine := 0 + + for outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], " \t") == expectedList[matchLine] { + outLine++ + matchLine++ + } + if outLine < endOut || matchLine < len(expectedList) { + if outLine >= endOut { + t.Errorf("dumpresolutions: missing lines at end of graph, want %d lines %v", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], "\n")) + } else if matchLine >= len(expectedList) { + t.Errorf("dumpresolutions: unexpected lines at end of graph starting line %d, got %v, want nothing", outLine+1, strings.Join(outList[outLine:], "\n")) + } else { + t.Errorf("dumpresolutions: at line %d, got %v, want %v", outLine+1, strings.Join(outList[outLine:], "\n"), strings.Join(expectedList[matchLine:], "\n")) + } + } + }) + } +} diff --git a/make/tools/compliance/cmd/htmlnotice/htmlnotice.go b/make/tools/compliance/cmd/htmlnotice/htmlnotice.go new file mode 100644 index 0000000..1a49610 --- /dev/null +++ b/make/tools/compliance/cmd/htmlnotice/htmlnotice.go @@ -0,0 +1,281 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "compress/gzip" + "flag" + "fmt" + "html" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + + "android/soong/response" + "android/soong/tools/compliance" + + "github.com/google/blueprint/deptools" +) + +var ( + failNoneRequested = fmt.Errorf("\nNo license metadata files requested") + failNoLicenses = fmt.Errorf("No licenses found") +) + +type context struct { + stdout io.Writer + stderr io.Writer + rootFS fs.FS + includeTOC bool + product string + stripPrefix []string + title string + deps *[]string +} + +func (ctx context) strip(installPath string) string { + for _, prefix := range ctx.stripPrefix { + if strings.HasPrefix(installPath, prefix) { + p := strings.TrimPrefix(installPath, prefix) + if 0 == len(p) { + p = ctx.product + } + if 0 == len(p) { + continue + } + return p + } + } + return installPath +} + +// newMultiString creates a flag that allows multiple values in an array. +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { + var f multiString + flags.Var(&f, name, usage) + return &f +} + +// multiString implements the flag `Value` interface for multiple strings. +type multiString []string + +func (ms *multiString) String() string { return strings.Join(*ms, ", ") } +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } + +func main() { + var expandedArgs []string + for _, arg := range os.Args[1:] { + if strings.HasPrefix(arg, "@") { + f, err := os.Open(strings.TrimPrefix(arg, "@")) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + respArgs, err := response.ReadRspFile(f) + f.Close() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + expandedArgs = append(expandedArgs, respArgs...) + } else { + expandedArgs = append(expandedArgs, arg) + } + } + + flags := flag.NewFlagSet("flags", flag.ExitOnError) + + flags.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} + +Outputs an html NOTICE.html or gzipped NOTICE.html.gz file if the -o filename +ends with ".gz". + +Options: +`, filepath.Base(os.Args[0])) + flags.PrintDefaults() + } + + outputFile := flags.String("o", "-", "Where to write the NOTICE text file. (default stdout)") + depsFile := flags.String("d", "", "Where to write the deps file") + includeTOC := flags.Bool("toc", true, "Whether to include a table of contents.") + product := flags.String("product", "", "The name of the product for which the notice is generated.") + stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") + title := flags.String("title", "", "The title of the notice file.") + + flags.Parse(expandedArgs) + + // Must specify at least one root target. + if flags.NArg() == 0 { + flags.Usage() + os.Exit(2) + } + + if len(*outputFile) == 0 { + flags.Usage() + fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") + os.Exit(2) + } else { + dir, err := filepath.Abs(filepath.Dir(*outputFile)) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) + os.Exit(1) + } + fi, err := os.Stat(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) + os.Exit(1) + } + if !fi.IsDir() { + fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) + os.Exit(1) + } + } + + var ofile io.Writer + var closer io.Closer + ofile = os.Stdout + var obuf *bytes.Buffer + if *outputFile != "-" { + obuf = &bytes.Buffer{} + ofile = obuf + } + if strings.HasSuffix(*outputFile, ".gz") { + ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression) + closer = ofile.(io.Closer) + } + + var deps []string + + ctx := &context{ofile, os.Stderr, compliance.FS, *includeTOC, *product, *stripPrefix, *title, &deps} + + err := htmlNotice(ctx, flags.Args()...) + if err != nil { + if err == failNoneRequested { + flags.Usage() + } + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + if closer != nil { + closer.Close() + } + + if *outputFile != "-" { + err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err) + os.Exit(1) + } + } + if *depsFile != "" { + err := deptools.WriteDepFile(*depsFile, *outputFile, deps) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err) + os.Exit(1) + } + } + os.Exit(0) +} + +// htmlNotice implements the htmlnotice utility. +func htmlNotice(ctx *context, files ...string) error { + // Must be at least one root file. + if len(files) < 1 { + return failNoneRequested + } + + // Read the license graph from the license metadata files (*.meta_lic). + licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files) + if err != nil { + return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err) + } + if licenseGraph == nil { + return failNoLicenses + } + + // rs contains all notice resolutions. + rs := compliance.ResolveNotices(licenseGraph) + + ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs) + if err != nil { + return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err) + } + + fmt.Fprintln(ctx.stdout, "") + fmt.Fprintln(ctx.stdout, "") + fmt.Fprintln(ctx.stdout, "") + if len(ctx.title) > 0 { + fmt.Fprintf(ctx.stdout, "%s\n", html.EscapeString(ctx.title)) + } else if len(ctx.product) > 0 { + fmt.Fprintf(ctx.stdout, "%s\n", html.EscapeString(ctx.product)) + } + fmt.Fprintln(ctx.stdout, "") + fmt.Fprintln(ctx.stdout, "") + + if len(ctx.title) > 0 { + fmt.Fprintf(ctx.stdout, "

%s

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

%s

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

      (.*)

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

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

      " +} + +type toc struct{} + +func (m toc) isMatch(line string) bool { + return tocTag.MatchString(line) +} + +func (m toc) String() string { + return `
        ` +} + +type target struct { + name string +} + +func (m target) isMatch(line string) bool { + groups := installTarget.FindStringSubmatch(line) + if len(groups) != 2 { + return false + } + return strings.HasPrefix(groups[1], "out/") && strings.HasSuffix(groups[1], "/"+html.EscapeString(m.name)) +} + +func (m target) String() string { + return `
      • ` + html.EscapeString(m.name) + `` +} + +type uses struct { + name string +} + +func (m uses) isMatch(line string) bool { + groups := libReference.FindStringSubmatch(line) + if len(groups) != 2 { + return false + } + return groups[1] == html.EscapeString(m.name) +} + +func (m uses) String() string { + return `
      • ` + html.EscapeString(m.name) + `` +} + +type hr struct{} + +func (m hr) isMatch(line string) bool { + return horizontalRule.MatchString(line) +} + +func (m hr) String() string { + return "
        " +} + +type library struct { + name string +} + +func (m library) isMatch(line string) bool { + groups := libraryName.FindStringSubmatch(line) + if len(groups) != 2 { + return false + } + return groups[1] == html.EscapeString(m.name) +} + +func (m library) String() string { + return " " + html.EscapeString(m.name) + " used by:" +} + +type usedBy struct { + name string +} + +func (m usedBy) isMatch(line string) bool { + groups := usedByTarget.FindStringSubmatch(line) + if len(groups) != 2 { + return false + } + return strings.HasPrefix(groups[1], "out/") && strings.HasSuffix(groups[1], "/"+html.EscapeString(m.name)) +} + +func (m usedBy) String() string { + return "
      • out/.../" + html.EscapeString(m.name) +} + +func matchesText(line, text string) bool { + groups := licenseText.FindStringSubmatch(line) + if len(groups) != 2 { + return false + } + return groups[1] == html.EscapeString(text) +} + +func expectedText(text string) string { + return `
        ` + html.EscapeString(text)
        +}
        +
        +type firstParty struct{}
        +
        +func (m firstParty) isMatch(line string) bool {
        +	return matchesText(line, "&&&First Party License&&&")
        +}
        +
        +func (m firstParty) String() string {
        +	return expectedText("&&&First Party License&&&")
        +}
        +
        +type notice struct{}
        +
        +func (m notice) isMatch(line string) bool {
        +	return matchesText(line, "%%%Notice License%%%")
        +}
        +
        +func (m notice) String() string {
        +	return expectedText("%%%Notice License%%%")
        +}
        +
        +type reciprocal struct{}
        +
        +func (m reciprocal) isMatch(line string) bool {
        +	return matchesText(line, "$$$Reciprocal License$$$")
        +}
        +
        +func (m reciprocal) String() string {
        +	return expectedText("$$$Reciprocal License$$$")
        +}
        +
        +type restricted struct{}
        +
        +func (m restricted) isMatch(line string) bool {
        +	return matchesText(line, "###Restricted License###")
        +}
        +
        +func (m restricted) String() string {
        +	return expectedText("###Restricted License###")
        +}
        +
        +type proprietary struct{}
        +
        +func (m proprietary) isMatch(line string) bool {
        +	return matchesText(line, "@@@Proprietary License@@@")
        +}
        +
        +func (m proprietary) String() string {
        +	return expectedText("@@@Proprietary License@@@")
        +}
        +
        +type matcherList []matcher
        +
        +func (l matcherList) String() string {
        +	var sb strings.Builder
        +	for _, m := range l {
        +		s := m.String()
        +		if s[:3] == s[len(s)-3:] {
        +			fmt.Fprintln(&sb)
        +		}
        +		fmt.Fprintf(&sb, "%s\n", s)
        +		if s[:3] == s[len(s)-3:] {
        +			fmt.Fprintln(&sb)
        +		}
        +	}
        +	return sb.String()
        +}
        diff --git a/make/tools/compliance/cmd/listshare/listshare.go b/make/tools/compliance/cmd/listshare/listshare.go
        new file mode 100644
        index 0000000..31bd1b2
        --- /dev/null
        +++ b/make/tools/compliance/cmd/listshare/listshare.go
        @@ -0,0 +1,182 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"flag"
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"os"
        +	"path/filepath"
        +	"sort"
        +	"strings"
        +
        +	"android/soong/response"
        +	"android/soong/tools/compliance"
        +)
        +
        +var (
        +	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        +	failNoLicenses    = fmt.Errorf("No licenses found")
        +)
        +
        +func main() {
        +	var expandedArgs []string
        +	for _, arg := range os.Args[1:] {
        +		if strings.HasPrefix(arg, "@") {
        +			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +
        +			respArgs, err := response.ReadRspFile(f)
        +			f.Close()
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +			expandedArgs = append(expandedArgs, respArgs...)
        +		} else {
        +			expandedArgs = append(expandedArgs, arg)
        +		}
        +	}
        +
        +	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        +
        +	flags.Usage = func() {
        +		fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...}
        +
        +Outputs a csv file with 1 project per line in the first field followed
        +by target:condition pairs describing why the project must be shared.
        +
        +Each target is the path to a generated license metadata file for a
        +Soong module or Make target, and the license condition is either
        +restricted (e.g. GPL) or reciprocal (e.g. MPL).
        +`, filepath.Base(os.Args[0]))
        +	}
        +
        +	outputFile := flags.String("o", "-", "Where to write the list of projects to share. (default stdout)")
        +
        +	flags.Parse(expandedArgs)
        +
        +	// Must specify at least one root target.
        +	if flags.NArg() == 0 {
        +		flags.Usage()
        +		os.Exit(2)
        +	}
        +
        +	if len(*outputFile) == 0 {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        +		os.Exit(2)
        +	} else {
        +		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +		fi, err := os.Stat(dir)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        +			os.Exit(1)
        +		}
        +		if !fi.IsDir() {
        +			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        +			os.Exit(1)
        +		}
        +	}
        +
        +	var ofile io.Writer
        +	ofile = os.Stdout
        +	var obuf *bytes.Buffer
        +	if *outputFile != "-" {
        +		obuf = &bytes.Buffer{}
        +		ofile = obuf
        +	}
        +
        +	err := listShare(ofile, os.Stderr, compliance.FS, flags.Args()...)
        +	if err != nil {
        +		if err == failNoneRequested {
        +			flags.Usage()
        +		}
        +		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        +		os.Exit(1)
        +	}
        +	if *outputFile != "-" {
        +		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
        +			os.Exit(1)
        +		}
        +	}
        +	os.Exit(0)
        +}
        +
        +// listShare implements the listshare utility.
        +func listShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error {
        +	// Must be at least one root file.
        +	if len(files) < 1 {
        +		return failNoneRequested
        +	}
        +
        +	// Read the license graph from the license metadata files (*.meta_lic).
        +	licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %v\n", files, os.Getenv("PWD"), err)
        +	}
        +	if licenseGraph == nil {
        +		return failNoLicenses
        +	}
        +
        +	// shareSource contains all source-sharing resolutions.
        +	shareSource := compliance.ResolveSourceSharing(licenseGraph)
        +
        +	// Group the resolutions by project.
        +	presolution := make(map[string]compliance.LicenseConditionSet)
        +	for _, target := range shareSource.AttachesTo() {
        +		rl := shareSource.Resolutions(target)
        +		sort.Sort(rl)
        +		for _, r := range rl {
        +			for _, p := range r.ActsOn().Projects() {
        +				if _, ok := presolution[p]; !ok {
        +					presolution[p] = r.Resolves()
        +					continue
        +				}
        +				presolution[p] = presolution[p].Union(r.Resolves())
        +			}
        +		}
        +	}
        +
        +	// Sort the projects for repeatability/stability.
        +	projects := make([]string, 0, len(presolution))
        +	for p := range presolution {
        +		projects = append(projects, p)
        +	}
        +	sort.Strings(projects)
        +
        +	// Output the sorted projects and the source-sharing license conditions that each project resolves.
        +	for _, p := range projects {
        +		if presolution[p].IsEmpty() {
        +			fmt.Fprintf(stdout, "%s\n", p)
        +		} else {
        +			fmt.Fprintf(stdout, "%s,%s\n", p, strings.Join(presolution[p].Names(), ","))
        +		}
        +	}
        +
        +	return nil
        +}
        diff --git a/make/tools/compliance/cmd/listshare/listshare_test.go b/make/tools/compliance/cmd/listshare/listshare_test.go
        new file mode 100644
        index 0000000..c1e38be
        --- /dev/null
        +++ b/make/tools/compliance/cmd/listshare/listshare_test.go
        @@ -0,0 +1,509 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"fmt"
        +	"os"
        +	"strings"
        +	"testing"
        +
        +	"android/soong/tools/compliance"
        +)
        +
        +func TestMain(m *testing.M) {
        +	// Change into the parent directory before running the tests
        +	// so they can find the testdata directory.
        +	if err := os.Chdir(".."); err != nil {
        +		fmt.Printf("failed to change to testdata directory: %s\n", err)
        +		os.Exit(1)
        +	}
        +	os.Exit(m.Run())
        +}
        +
        +func Test(t *testing.T) {
        +	type projectShare struct {
        +		project    string
        +		conditions []string
        +	}
        +	tests := []struct {
        +		condition   string
        +		name        string
        +		outDir      string
        +		roots       []string
        +		expectedOut []projectShare
        +	}{
        +		{
        +			condition:   "firstparty",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "device/library",
        +					conditions: []string{"reciprocal"},
        +				},
        +				{
        +					project: "static/library",
        +					conditions: []string{
        +						"reciprocal",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "device/library",
        +					conditions: []string{"reciprocal"},
        +				},
        +				{
        +					project: "static/library",
        +					conditions: []string{
        +						"reciprocal",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "device/library",
        +					conditions: []string{"reciprocal"},
        +				},
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project: "device/library",
        +					conditions: []string{
        +						"reciprocal",
        +					},
        +				},
        +				{
        +					project: "static/library",
        +					conditions: []string{
        +						"reciprocal",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition:   "reciprocal",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "base/library",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "device/library",
        +					conditions: []string{"restricted_allows_dynamic_linking"},
        +				},
        +				{
        +					project:    "dynamic/binary",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project: "highest/apex",
        +					conditions: []string{
        +						"restricted",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project: "static/binary",
        +					conditions: []string{
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project: "static/library",
        +					conditions: []string{
        +						"reciprocal",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "base/library",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project: "container/zip",
        +					conditions: []string{
        +						"restricted",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project:    "device/library",
        +					conditions: []string{"restricted_allows_dynamic_linking"},
        +				},
        +				{
        +					project:    "dynamic/binary",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project: "static/binary",
        +					conditions: []string{
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project: "static/library",
        +					conditions: []string{
        +						"reciprocal",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project: "device/library",
        +					conditions: []string{
        +						"restricted",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project: "distributable/application",
        +					conditions: []string{
        +						"restricted",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project: "device/library",
        +					conditions: []string{
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project: "static/binary",
        +					conditions: []string{
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +				{
        +					project: "static/library",
        +					conditions: []string{
        +						"reciprocal",
        +						"restricted_allows_dynamic_linking",
        +					},
        +				},
        +			},
        +		},
        +		{
        +			condition:   "restricted",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "base/library",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "dynamic/binary",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "highest/apex",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "base/library",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "container/zip",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "dynamic/binary",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "device/library",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "distributable/application",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []projectShare{},
        +		},
        +		{
        +			condition: "regressgpl1",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "bin/threelibraries",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "container/zip",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +		{
        +			condition: "regressgpl1",
        +			name:      "containerplus",
        +			roots:     []string{"container.zip.meta_lic", "lib/libapache.so.meta_lic", "lib/libc++.so.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "bin/threelibraries",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "container/zip",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/apache",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/c++",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +		{
        +			condition: "regressgpl2",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "bin/threelibraries",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "container/zip",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/apache",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/c++",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/gpl",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +		{
        +			condition: "regressgpl2",
        +			name:      "containerplus",
        +			roots:     []string{"container.zip.meta_lic", "lib/libapache.so.meta_lic", "lib/libc++.so.meta_lic"},
        +			expectedOut: []projectShare{
        +				{
        +					project:    "bin/threelibraries",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "container/zip",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/apache",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/c++",
        +					conditions: []string{"restricted"},
        +				},
        +				{
        +					project:    "lib/gpl",
        +					conditions: []string{"restricted"},
        +				},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        +			expectedOut := &bytes.Buffer{}
        +			for _, p := range tt.expectedOut {
        +				expectedOut.WriteString(p.project)
        +				for _, lc := range p.conditions {
        +					expectedOut.WriteString(",")
        +					expectedOut.WriteString(lc)
        +				}
        +				expectedOut.WriteString("\n")
        +			}
        +
        +			stdout := &bytes.Buffer{}
        +			stderr := &bytes.Buffer{}
        +
        +			rootFiles := make([]string, 0, len(tt.roots))
        +			for _, r := range tt.roots {
        +				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        +			}
        +			err := listShare(stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)
        +			if err != nil {
        +				t.Fatalf("listshare: error = %v, stderr = %v", err, stderr)
        +				return
        +			}
        +			if stderr.Len() > 0 {
        +				t.Errorf("listshare: gotStderr = %v, want none", stderr)
        +			}
        +			out := stdout.String()
        +			expected := expectedOut.String()
        +			if out != expected {
        +				outList := strings.Split(out, "\n")
        +				expectedList := strings.Split(expected, "\n")
        +				startLine := 0
        +				for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {
        +					startLine++
        +				}
        +				t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
        +					out, expected, startLine+1, outList[startLine], expectedList[startLine])
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/cmd/rtrace/rtrace.go b/make/tools/compliance/cmd/rtrace/rtrace.go
        new file mode 100644
        index 0000000..667cdce
        --- /dev/null
        +++ b/make/tools/compliance/cmd/rtrace/rtrace.go
        @@ -0,0 +1,259 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"flag"
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"os"
        +	"path/filepath"
        +	"sort"
        +	"strings"
        +
        +	"android/soong/response"
        +	"android/soong/tools/compliance"
        +)
        +
        +var (
        +	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        +	failNoSources     = fmt.Errorf("\nNo projects or metadata files to trace back from")
        +	failNoLicenses    = fmt.Errorf("No licenses found")
        +)
        +
        +type context struct {
        +	sources     []string
        +	stripPrefix []string
        +}
        +
        +func (ctx context) strip(installPath string) string {
        +	for _, prefix := range ctx.stripPrefix {
        +		if strings.HasPrefix(installPath, prefix) {
        +			p := strings.TrimPrefix(installPath, prefix)
        +			if 0 == len(p) {
        +				continue
        +			}
        +			return p
        +		}
        +	}
        +	return installPath
        +}
        +
        +// newMultiString creates a flag that allows multiple values in an array.
        +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
        +	var f multiString
        +	flags.Var(&f, name, usage)
        +	return &f
        +}
        +
        +// multiString implements the flag `Value` interface for multiple strings.
        +type multiString []string
        +
        +func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
        +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
        +
        +func main() {
        +	var expandedArgs []string
        +	for _, arg := range os.Args[1:] {
        +		if strings.HasPrefix(arg, "@") {
        +			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +
        +			respArgs, err := response.ReadRspFile(f)
        +			f.Close()
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +			expandedArgs = append(expandedArgs, respArgs...)
        +		} else {
        +			expandedArgs = append(expandedArgs, arg)
        +		}
        +	}
        +
        +	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        +
        +	flags.Usage = func() {
        +		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        +
        +Outputs a space-separated Target ActsOn Origin Condition tuple for each
        +resolution in the graph. When -dot flag given, outputs nodes and edges
        +in graphviz directed graph format.
        +
        +If one or more '-c condition' conditions are given, outputs the
        +resolution for the union of the conditions. Otherwise, outputs the
        +resolution for all conditions.
        +
        +In plain text mode, when '-label_conditions' is requested, the Target
        +and Origin have colon-separated license conditions appended:
        +i.e. target:condition1:condition2 etc.
        +
        +Options:
        +`, filepath.Base(os.Args[0]))
        +		flags.PrintDefaults()
        +	}
        +
        +	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
        +	sources := newMultiString(flags, "rtrace", "Projects or metadata files to trace back from. (required; multiple allowed)")
        +	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
        +
        +	flags.Parse(expandedArgs)
        +
        +	// Must specify at least one root target.
        +	if flags.NArg() == 0 {
        +		flags.Usage()
        +		os.Exit(2)
        +	}
        +
        +	if len(*sources) == 0 {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "\nMust specify at least 1 --rtrace source.\n")
        +		os.Exit(2)
        +	}
        +
        +	if len(*outputFile) == 0 {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        +		os.Exit(2)
        +	} else {
        +		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +		fi, err := os.Stat(dir)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        +			os.Exit(1)
        +		}
        +		if !fi.IsDir() {
        +			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        +			os.Exit(1)
        +		}
        +	}
        +
        +	var ofile io.Writer
        +	ofile = os.Stdout
        +	var obuf *bytes.Buffer
        +	if *outputFile != "-" {
        +		obuf = &bytes.Buffer{}
        +		ofile = obuf
        +	}
        +
        +	ctx := &context{
        +		sources:     *sources,
        +		stripPrefix: *stripPrefix,
        +	}
        +	_, err := traceRestricted(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)
        +	if err != nil {
        +		if err == failNoneRequested {
        +			flags.Usage()
        +		}
        +		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        +		os.Exit(1)
        +	}
        +	if *outputFile != "-" {
        +		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
        +			os.Exit(1)
        +		}
        +	}
        +	os.Exit(0)
        +}
        +
        +// traceRestricted implements the rtrace utility.
        +func traceRestricted(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) (*compliance.LicenseGraph, error) {
        +	if len(files) < 1 {
        +		return nil, failNoneRequested
        +	}
        +
        +	if len(ctx.sources) < 1 {
        +		return nil, failNoSources
        +	}
        +
        +	// Read the license graph from the license metadata files (*.meta_lic).
        +	licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
        +	if err != nil {
        +		return nil, fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        +	}
        +	if licenseGraph == nil {
        +		return nil, failNoLicenses
        +	}
        +
        +	sourceMap := make(map[string]struct{})
        +	for _, source := range ctx.sources {
        +		sourceMap[source] = struct{}{}
        +	}
        +
        +	compliance.TraceTopDownConditions(licenseGraph, func(tn *compliance.TargetNode) compliance.LicenseConditionSet {
        +		if _, isPresent := sourceMap[tn.Name()]; isPresent {
        +			return compliance.ImpliesRestricted
        +		}
        +		for _, project := range tn.Projects() {
        +			if _, isPresent := sourceMap[project]; isPresent {
        +				return compliance.ImpliesRestricted
        +			}
        +		}
        +		return compliance.NewLicenseConditionSet()
        +	})
        +
        +	// targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed.
        +	targetOut := func(target *compliance.TargetNode, sep string) string {
        +		tOut := ctx.strip(target.Name())
        +		return tOut
        +	}
        +
        +	// outputResolution prints a resolution in the requested format to `stdout`, where one can read
        +	// a resolution as `tname` resolves conditions named in `cnames`.
        +	// `tname` is the name of the target the resolution traces back to.
        +	// `cnames` is the list of conditions to resolve.
        +	outputResolution := func(tname string, cnames []string) {
        +		// ... one edge per line with names in a colon-separated tuple.
        +		fmt.Fprintf(stdout, "%s %s\n", tname, strings.Join(cnames, ":"))
        +	}
        +
        +	// Sort the resolutions by targetname for repeatability/stability.
        +	actions := compliance.WalkResolutionsForCondition(licenseGraph, compliance.ImpliesShared).AllActions()
        +	targets := make(compliance.TargetNodeList, 0, len(actions))
        +	for tn := range actions {
        +		if tn.LicenseConditions().MatchesAnySet(compliance.ImpliesRestricted) {
        +			targets = append(targets, tn)
        +		}
        +	}
        +	sort.Sort(targets)
        +
        +	// Output the sorted targets.
        +	for _, target := range targets {
        +		var tname string
        +		tname = targetOut(target, ":")
        +
        +		// cnames accumulates the list of condition names originating at a single origin that apply to `target`.
        +		cnames := target.LicenseConditions().Names()
        +
        +		// Output 1 line for each attachesTo+actsOn combination.
        +		outputResolution(tname, cnames)
        +	}
        +	fmt.Fprintf(stdout, "restricted conditions trace to %d targets\n", len(targets))
        +	if 0 == len(targets) {
        +		fmt.Fprintln(stdout, "  (check for typos in project names or metadata files)")
        +	}
        +	return licenseGraph, nil
        +}
        diff --git a/make/tools/compliance/cmd/rtrace/rtrace_test.go b/make/tools/compliance/cmd/rtrace/rtrace_test.go
        new file mode 100644
        index 0000000..cbe9461
        --- /dev/null
        +++ b/make/tools/compliance/cmd/rtrace/rtrace_test.go
        @@ -0,0 +1,319 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"fmt"
        +	"os"
        +	"strings"
        +	"testing"
        +
        +	"android/soong/tools/compliance"
        +)
        +
        +func TestMain(m *testing.M) {
        +	// Change into the parent directory before running the tests
        +	// so they can find the testdata directory.
        +	if err := os.Chdir(".."); err != nil {
        +		fmt.Printf("failed to change to testdata directory: %s\n", err)
        +		os.Exit(1)
        +	}
        +	os.Exit(m.Run())
        +}
        +
        +func Test_plaintext(t *testing.T) {
        +	tests := []struct {
        +		condition   string
        +		name        string
        +		outDir      string
        +		roots       []string
        +		ctx         context
        +		expectedOut []string
        +	}{
        +		{
        +			condition: "firstparty",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "apex_trimmed",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/firstparty/bin/bin1.meta_lic"},
        +				stripPrefix: []string{"testdata/firstparty/"},
        +			},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "apex_trimmed",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/notice/bin/bin1.meta_lic"},
        +				stripPrefix: []string{"testdata/notice/"},
        +			},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "apex_trimmed",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/reciprocal/bin/bin1.meta_lic"},
        +				stripPrefix: []string{"testdata/reciprocal/"},
        +			},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{
        +				"testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
        +				"testdata/restricted/lib/libb.so.meta_lic restricted",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "apex_trimmed_bin1",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/restricted/bin/bin1.meta_lic"},
        +				stripPrefix: []string{"testdata/restricted/"},
        +			},
        +			expectedOut: []string{"lib/liba.so.meta_lic restricted_allows_dynamic_linking"},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "apex_trimmed_bin2",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/restricted/bin/bin2.meta_lic"},
        +				stripPrefix: []string{"testdata/restricted/"},
        +			},
        +			expectedOut: []string{"lib/libb.so.meta_lic restricted"},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []string{
        +				"testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
        +				"testdata/restricted/lib/libb.so.meta_lic restricted",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []string{"testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking"},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{"testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking"},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{"testdata/proprietary/lib/libb.so.meta_lic restricted"},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "apex_trimmed_bin1",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/proprietary/bin/bin1.meta_lic"},
        +				stripPrefix: []string{"testdata/proprietary/"},
        +			},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "apex_trimmed_bin2",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			ctx: context{
        +				sources:     []string{"testdata/proprietary/bin/bin2.meta_lic"},
        +				stripPrefix: []string{"testdata/proprietary/"},
        +			},
        +			expectedOut: []string{"lib/libb.so.meta_lic restricted"},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []string{"testdata/proprietary/lib/libb.so.meta_lic restricted"},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        +			expectedOut := &bytes.Buffer{}
        +			for _, eo := range tt.expectedOut {
        +				expectedOut.WriteString(eo)
        +				expectedOut.WriteString("\n")
        +			}
        +			fmt.Fprintf(expectedOut, "restricted conditions trace to %d targets\n", len(tt.expectedOut))
        +			if 0 == len(tt.expectedOut) {
        +				fmt.Fprintln(expectedOut, "  (check for typos in project names or metadata files)")
        +			}
        +
        +			stdout := &bytes.Buffer{}
        +			stderr := &bytes.Buffer{}
        +
        +			rootFiles := make([]string, 0, len(tt.roots))
        +			for _, r := range tt.roots {
        +				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        +			}
        +			if len(tt.ctx.sources) < 1 {
        +				tt.ctx.sources = rootFiles
        +			}
        +			_, err := traceRestricted(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)
        +			t.Logf("rtrace: stderr = %v", stderr)
        +			t.Logf("rtrace: stdout = %v", stdout)
        +			if err != nil {
        +				t.Fatalf("rtrace: error = %v", err)
        +				return
        +			}
        +			if stderr.Len() > 0 {
        +				t.Errorf("rtrace: gotStderr = %v, want none", stderr)
        +			}
        +			out := stdout.String()
        +			expected := expectedOut.String()
        +			if out != expected {
        +				outList := strings.Split(out, "\n")
        +				expectedList := strings.Split(expected, "\n")
        +				startLine := 0
        +				for startLine < len(outList) && startLine < len(expectedList) && outList[startLine] == expectedList[startLine] {
        +					startLine++
        +				}
        +				t.Errorf("rtrace: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
        +					out, expected, startLine+1, outList[startLine], expectedList[startLine])
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/cmd/shippedlibs/shippedlibs.go b/make/tools/compliance/cmd/shippedlibs/shippedlibs.go
        new file mode 100644
        index 0000000..add6dd6
        --- /dev/null
        +++ b/make/tools/compliance/cmd/shippedlibs/shippedlibs.go
        @@ -0,0 +1,165 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"flag"
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"os"
        +	"path/filepath"
        +	"strings"
        +
        +	"android/soong/response"
        +	"android/soong/tools/compliance"
        +)
        +
        +var (
        +	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        +	failNoLicenses    = fmt.Errorf("No licenses found")
        +)
        +
        +type context struct {
        +	stdout io.Writer
        +	stderr io.Writer
        +	rootFS fs.FS
        +}
        +
        +func main() {
        +	var expandedArgs []string
        +	for _, arg := range os.Args[1:] {
        +		if strings.HasPrefix(arg, "@") {
        +			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +
        +			respArgs, err := response.ReadRspFile(f)
        +			f.Close()
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +			expandedArgs = append(expandedArgs, respArgs...)
        +		} else {
        +			expandedArgs = append(expandedArgs, arg)
        +		}
        +	}
        +
        +	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        +
        +	outputFile := flags.String("o", "-", "Where to write the library list. (default stdout)")
        +
        +	flags.Usage = func() {
        +		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        +
        +Outputs a list of libraries used in the shipped images.
        +
        +Options:
        +`, filepath.Base(os.Args[0]))
        +		flags.PrintDefaults()
        +	}
        +
        +	err := flags.Parse(expandedArgs)
        +	if err != nil {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "%v\n", err)
        +	}
        +
        +	// Must specify at least one root target.
        +	if flags.NArg() == 0 {
        +		flags.Usage()
        +		os.Exit(2)
        +	}
        +
        +	if len(*outputFile) == 0 {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        +		os.Exit(2)
        +	} else {
        +		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +		fi, err := os.Stat(dir)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        +			os.Exit(1)
        +		}
        +		if !fi.IsDir() {
        +			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        +			os.Exit(1)
        +		}
        +	}
        +
        +	var ofile io.Writer
        +	ofile = os.Stdout
        +	if *outputFile != "-" {
        +		ofile = &bytes.Buffer{}
        +	}
        +
        +	ctx := &context{ofile, os.Stderr, compliance.FS}
        +
        +	err = shippedLibs(ctx, flags.Args()...)
        +	if err != nil {
        +		if err == failNoneRequested {
        +			flags.Usage()
        +		}
        +		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        +		os.Exit(1)
        +	}
        +	if *outputFile != "-" {
        +		err := os.WriteFile(*outputFile, ofile.(*bytes.Buffer).Bytes(), 0666)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +	}
        +	os.Exit(0)
        +}
        +
        +// shippedLibs implements the shippedlibs utility.
        +func shippedLibs(ctx *context, files ...string) error {
        +	// Must be at least one root file.
        +	if len(files) < 1 {
        +		return failNoneRequested
        +	}
        +
        +	// Read the license graph from the license metadata files (*.meta_lic).
        +	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        +	}
        +	if licenseGraph == nil {
        +		return failNoLicenses
        +	}
        +
        +	// rs contains all notice resolutions.
        +	rs := compliance.ResolveNotices(licenseGraph)
        +
        +	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
        +	}
        +
        +	for lib := range ni.Libraries() {
        +		fmt.Fprintln(ctx.stdout, lib)
        +	}
        +	return nil
        +}
        diff --git a/make/tools/compliance/cmd/shippedlibs/shippedlibs_test.go b/make/tools/compliance/cmd/shippedlibs/shippedlibs_test.go
        new file mode 100644
        index 0000000..983747c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/shippedlibs/shippedlibs_test.go
        @@ -0,0 +1,241 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bufio"
        +	"bytes"
        +	"fmt"
        +	"os"
        +	"strings"
        +	"testing"
        +
        +	"android/soong/tools/compliance"
        +)
        +
        +func TestMain(m *testing.M) {
        +	// Change into the parent directory before running the tests
        +	// so they can find the testdata directory.
        +	if err := os.Chdir(".."); err != nil {
        +		fmt.Printf("failed to change to testdata directory: %s\n", err)
        +		os.Exit(1)
        +	}
        +	os.Exit(m.Run())
        +}
        +
        +func Test(t *testing.T) {
        +	tests := []struct {
        +		condition   string
        +		name        string
        +		outDir      string
        +		roots       []string
        +		expectedOut []string
        +	}{
        +		{
        +			condition:   "firstparty",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{"Android"},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []string{"Android"},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []string{"Android"},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{"Android"},
        +		},
        +		{
        +			condition:   "firstparty",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{"Android"},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []string{"Android", "Device"},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "notice",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{"External"},
        +		},
        +		{
        +			condition:   "reciprocal",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "reciprocal",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "reciprocal",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []string{"Android", "Device"},
        +		},
        +		{
        +			condition:   "reciprocal",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "reciprocal",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{"External"},
        +		},
        +		{
        +			condition:   "restricted",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "restricted",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "restricted",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []string{"Android", "Device"},
        +		},
        +		{
        +			condition:   "restricted",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "restricted",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{"External"},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "apex",
        +			roots:       []string{"highest.apex.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "container",
        +			roots:       []string{"container.zip.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "application",
        +			roots:       []string{"application.meta_lic"},
        +			expectedOut: []string{"Android", "Device"},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "binary",
        +			roots:       []string{"bin/bin1.meta_lic"},
        +			expectedOut: []string{"Android", "Device", "External"},
        +		},
        +		{
        +			condition:   "proprietary",
        +			name:        "library",
        +			roots:       []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []string{"External"},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        +			stdout := &bytes.Buffer{}
        +			stderr := &bytes.Buffer{}
        +
        +			rootFiles := make([]string, 0, len(tt.roots))
        +			for _, r := range tt.roots {
        +				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        +			}
        +
        +			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir)}
        +
        +			err := shippedLibs(&ctx, rootFiles...)
        +			if err != nil {
        +				t.Fatalf("shippedLibs: error = %v, stderr = %v", err, stderr)
        +				return
        +			}
        +			if stderr.Len() > 0 {
        +				t.Errorf("shippedLibs: gotStderr = %v, want none", stderr)
        +			}
        +
        +			t.Logf("got stdout: %s", stdout.String())
        +
        +			t.Logf("want stdout: %s", strings.Join(tt.expectedOut, "\n"))
        +
        +			out := bufio.NewScanner(stdout)
        +			lineno := 0
        +			for out.Scan() {
        +				line := out.Text()
        +				if strings.TrimLeft(line, " ") == "" {
        +					continue
        +				}
        +				if len(tt.expectedOut) <= lineno {
        +					t.Errorf("shippedLibs: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
        +				} else if tt.expectedOut[lineno] != line {
        +					t.Errorf("shippedLibs: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno])
        +				}
        +				lineno++
        +			}
        +			for ; lineno < len(tt.expectedOut); lineno++ {
        +				t.Errorf("shippedLibs: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno])
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/cmd/testdata/README.md b/make/tools/compliance/cmd/testdata/README.md
        new file mode 100644
        index 0000000..07564c2
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/README.md
        @@ -0,0 +1,326 @@
        +## Test data
        +
        +Each non-regression directory under testdata/ defines a similar build graph.
        +All have the same structure, but different versions of the graph have different
        +license metadata.
        +
        +The regression* directories can have whatever structure is required for the
        +specific test case.
        +
        +### Testdata build graph structure:
        +
        +The structure is meant to simulate some common scenarios:
        +
        +*   a `lib/` directory with some libraries
        +*   a `bin/` directory with some executables
        +*   one of the binaries, `bin3`, is a toolchain executable like a compiler
        +*   an `application` built with the `bin3` compiler and linking a couple libraries
        +*   a pure aggregation `continer.zip` that merely bundles files together, and
        +*   an apex file (more like an apk file) with some binaries and libraries.
        +
        +The testdata starts with a `firstparty/` version containng only first-party
        +licenses, and each subsequent directory introduces more restrictive conditions:
        +
        +*   `notice/` starts with `firstparty/` adds third-party notice conditions
        +*   `reciprocal/` starts with `notice/` and adds some reciprocal conditions
        +*   `restricted/` starts with `reciprocal/` and adds some restricted conditions
        +*   `proprietary/` starts with `restricted/` and add some privacy conditions
        +
        +#### a `lib/` directory with some libraries
        +
        +```dot
        +strict digraph {
        +	liba [label="lib/liba.so.meta_lic"];
        +	libb [label="lib/libb.so.meta_lic"];
        +	libc [label="lib/libc.a.meta_lic"];
        +	libd [label="lib/libd.so.meta_lic"];
        +}
        +```
        +
        +#### a `bin/` directory with some executables
        +
        +strict digraph {
        +	rankdir=LR;
        +	bin1 [label="bin/bin1.meta_lic"];
        +	bin2 [label="bin/bin2.meta_lic"];
        +	bin3 [label="bin/bin3.meta_lic\ntoolchain"];
        +	liba [label="lib/liba.so.meta_lic"];
        +	libb [label="lib/libb.so.meta_lic"];
        +	libc [label="lib/libc.a.meta_lic"];
        +	libd [label="lib/libd.so.meta_lic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	{rank=same; bin1 bin2 bin3}
        +}
        +
        +#### an `application` built with the `bin3` compiler and linking a couple libraries
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	app [label="application.meta_lic"];
        +	bin3 [label="bin/bin3.meta_lic"];
        +	liba [label="lib/liba.so.meta_lic"];
        +	libb [label="lib/libb.so.meta_lic"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	{rank=same; app}
        +}
        +```
        +
        +#### a pure aggregation `container.zip` that merely bundles files together
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	bin1 [label="bin/bin1.meta_lic"];
        +	bin2 [label="bin/bin2.meta_lic"];
        +	container [label="container.zip.meta_lic"];
        +	liba [label="lib/liba.so.meta_lic"];
        +	libb [label="lib/libb.so.meta_lic"];
        +	libc [label="lib/libc.a.meta_lic"];
        +	libd [label="lib/libd.so.meta_lic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	{rank=same; container}
        +}
        +```
        +
        +#### an apex file (more like an apk file) with some binaries and libraries
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	apex [label="highest.apex.meta_lic"];
        +	bin1 [label="bin/bin1.meta_lic"];
        +	bin2 [label="bin/bin2.meta_lic"];
        +	bin3 [label="bin/bin3.meta_lic"];
        +	liba [label="lib/liba.so.meta_lic"];
        +	libb [label="lib/libb.so.meta_lic"];
        +	libc [label="lib/libc.a.meta_lic"];
        +	libd [label="lib/libd.so.meta_lic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; apex}
        +}
        +```
        +
        +#### the whole build graph
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	apex [label="highest.apex.meta_lic"];
        +	app [label="application.meta_lic"];
        +	bin1 [label="bin/bin1.meta_lic"];
        +	bin2 [label="bin/bin2.meta_lic"];
        +	bin3 [label="bin/bin3.meta_lic"];
        +	container [label="container.zip.meta_lic"];
        +	liba [label="lib/liba.so.meta_lic"];
        +	libb [label="lib/libb.so.meta_lic"];
        +	libc [label="lib/libc.a.meta_lic"];
        +	libd [label="lib/libd.so.meta_lic"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; app container apex}
        +}
        +```
        +
        +
        +### firstparty/ testdata starts with all first-party licensing
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	app [label="firstparty/application.meta_lic"];
        +	bin1 [label="firstparty/bin/bin1.meta_lic"];
        +	bin2 [label="firstparty/bin/bin2.meta_lic"];
        +	bin3 [label="firstparty/bin/bin3.meta_lic"];
        +	container [label="firstparty/container.zip.meta_lic"];
        +	apex [label="firstparty/highest.apex.meta_lic"];
        +	liba [label="firstparty/lib/liba.so.meta_lic"];
        +	libb [label="firstparty/lib/libb.so.meta_lic"];
        +	libc [label="firstparty/lib/libc.a.meta_lic"];
        +	lib [label="firstparty/lib/libd.so.meta_lic"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; app container apex}
        +}
        +```
        +
        +### notice/ testdata introduces third-party notice conditions
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	app [label="notice/application.meta_lic"];
        +	bin1 [label="notice/bin/bin1.meta_lic"];
        +	bin2 [label="notice/bin/bin2.meta_lic"];
        +	bin3 [label="notice/bin/bin3.meta_lic\nnotice"];
        +	container [label="notice/container.zip.meta_lic"];
        +	apex [label="notice/highest.apex.meta_lic"];
        +	liba [label="notice/lib/liba.so.meta_lic\nnotice"];
        +	libb [label="notice/lib/libb.so.meta_lic"];
        +	libc [label="notice/lib/libc.a.meta_lic\nnotice"];
        +	libd [label="notice/lib/libd.so.meta_lic\nnotice"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; app container apex}
        +}
        +```
        +
        +### reciprocal/ testdata introduces third-party reciprocal sharing conditions
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	app [label="reciprocal/application.meta_lic"];
        +	bin1 [label="reciprocal/bin/bin1.meta_lic"];
        +	bin2 [label="reciprocal/bin/bin2.meta_lic"];
        +	bin3 [label="reciprocal/bin/bin3.meta_lic\nnotice"];
        +	container [label="reciprocal/container.zip.meta_lic"];
        +	apex [label="reciprocal/highest.apex.meta_lic"];
        +	liba [label="reciprocal/lib/liba.so.meta_lic\nreciprocal"];
        +	libb [label="reciprocal/lib/libb.so.meta_lic"];
        +	libc [label="reciprocal/lib/libc.a.meta_lic\nreciprocal"];
        +	libd [label="reciprocal/lib/libd.so.meta_lic\nnotice"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; app container apex}
        +}
        +```
        +
        +### restricted/ testdata introduces restricted source sharing conditions
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	app [label="restricted/application.meta_lic"];
        +	bin1 [label="restricted/bin/bin1.meta_lic"];
        +	bin2 [label="restricted/bin/bin2.meta_lic"];
        +	bin3 [label="restricted/bin/bin3.meta_lic\nrestricted"];
        +	container [label="restricted/container.zip.meta_lic"];
        +	apex [label="restricted/highest.apex.meta_lic"];
        +	liba [label="restricted/lib/liba.so.meta_lic\nrestricted"];
        +	libb [label="restricted/lib/libb.so.meta_lic\nrestricted"];
        +	libc [label="restricted/lib/libc.a.meta_lic\nreciprocal"];
        +	libd [label="restricted/lib/libd.so.meta_lic\nnotice"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; app container apex}
        +}
        +```
        +
        +### proprietary/ testdata introduces privacy conditions
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	app [label="proprietary/application.meta_lic"];
        +	bin1 [label="proprietary/bin/bin1.meta_lic"];
        +	bin2 [label="proprietary/bin/bin2.meta_lic\nby_exception_only\nproprietary"];
        +	bin3 [label="proprietary/bin/bin3.meta_lic\nrestricted"];
        +	container [label="proprietary/container.zip.meta_lic"];
        +	apex [label="proprietary/highest.apex.meta_lic"];
        +	liba [label="proprietary/lib/liba.so.meta_lic\nby_exception_only\nproprietary"];
        +	libb [label="proprietary/lib/libb.so.meta_lic\nrestricted"];
        +	libc [label="proprietary/lib/libc.a.meta_lic\nby_exception_only\nproprietary"];
        +	libd [label="proprietary/lib/libd.so.meta_lic\nnotice"];
        +	app -> bin3 [label="toolchain"];
        +	app -> liba [label="static"];
        +	app -> libb [label="dynamic"];
        +	bin1 -> liba [label="static"];
        +	bin1 -> libc [label="static"];
        +	bin2 -> libb [label="dynamic"];
        +	bin2 -> libd [label="dynamic"];
        +	container -> bin1 [label="static"];
        +	container -> bin2 [label="static"];
        +	container -> liba [label="static"];
        +	container -> libb [label="static"];
        +	apex -> bin1 [label="static"];
        +	apex -> bin2 [label="static"];
        +	apex -> liba [label="static"];
        +	apex -> libb [label="static"];
        +	{rank=same; app container apex}
        +}
        +```
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/FIRST_PARTY_LICENSE b/make/tools/compliance/cmd/testdata/firstparty/FIRST_PARTY_LICENSE
        new file mode 100644
        index 0000000..a7e7e64
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/FIRST_PARTY_LICENSE
        @@ -0,0 +1 @@
        +&&&First Party License&&&
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/application.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/application.meta_lic
        new file mode 100644
        index 0000000..ac3338f
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/application.meta_lic
        @@ -0,0 +1,24 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "distributable/application"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        +installed:  "out/target/product/fictional/bin/application"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +deps:  {
        +  file:  "testdata/firstparty/bin/bin3.meta_lic"
        +  annotations:  "toolchain"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..3007129
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "static/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libc.a"
        +deps:  {
        +  file:  "testdata/firstparty/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/libc.a.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..89bc6a4
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "dynamic/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/lib/libd.so"
        +deps:  {
        +  file:  "testdata/firstparty/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/libd.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..a81c764
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "standalone/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic
        new file mode 100644
        index 0000000..9f6a679
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic
        @@ -0,0 +1,36 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/firstparty/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic
        new file mode 100644
        index 0000000..abad5f1
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic
        @@ -0,0 +1,44 @@
        +package_name:  "Android"
        +projects:  "highest/apex"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        +installed:  "out/target/product/fictional/system/apex/highest.apex"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/liba.so"
        +  container_path:  "/lib/liba.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/libb.so"
        +  container_path:  "/lib/libb.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin1"
        +  container_path:  "/bin/bin1"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin2"
        +  container_path:  "/bin/bin2"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/firstparty/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/firstparty/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic
        new file mode 100644
        index 0000000..2985719
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +projects:  "device/library"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        +installed:  "out/target/product/fictional/system/lib/liba.so"
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic
        new file mode 100644
        index 0000000..e60ef73
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +projects:  "base/library"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        +installed:  "out/target/product/fictional/system/lib/libb.so"
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic
        new file mode 100644
        index 0000000..24d3f0d
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic
        @@ -0,0 +1,7 @@
        +package_name:  "Android"
        +projects:  "static/library"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        diff --git a/make/tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic b/make/tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic
        new file mode 100644
        index 0000000..f7e537c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "Android"
        +projects:  "dynamic/library"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        +installed:  "out/target/product/fictional/system/lib/libd.so"
        diff --git a/make/tools/compliance/cmd/testdata/notice/NOTICE_LICENSE b/make/tools/compliance/cmd/testdata/notice/NOTICE_LICENSE
        new file mode 100644
        index 0000000..752b249
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/NOTICE_LICENSE
        @@ -0,0 +1 @@
        +%%%Notice License%%%
        diff --git a/make/tools/compliance/cmd/testdata/notice/application.meta_lic b/make/tools/compliance/cmd/testdata/notice/application.meta_lic
        new file mode 100644
        index 0000000..8ce0a98
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/application.meta_lic
        @@ -0,0 +1,24 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "distributable/application"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        +installed:  "out/target/product/fictional/bin/application"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +deps:  {
        +  file:  "testdata/notice/bin/bin3.meta_lic"
        +  annotations:  "toolchain"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..6d173a4
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "static/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libc.a"
        +deps:  {
        +  file:  "testdata/notice/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/libc.a.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..a9e9c71
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "dynamic/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/lib/libd.so"
        +deps:  {
        +  file:  "testdata/notice/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/libd.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..bb9a3d5
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Compiler"
        +module_classes: "EXECUTABLES"
        +projects:  "standalone/binary"
        +license_kinds:  "SPDX-license-identifier-NCSA"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        diff --git a/make/tools/compliance/cmd/testdata/notice/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/notice/container.zip.meta_lic
        new file mode 100644
        index 0000000..e9c0511
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/container.zip.meta_lic
        @@ -0,0 +1,36 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/notice/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/notice/highest.apex.meta_lic b/make/tools/compliance/cmd/testdata/notice/highest.apex.meta_lic
        new file mode 100644
        index 0000000..2abb76e
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/highest.apex.meta_lic
        @@ -0,0 +1,44 @@
        +package_name:  "Android"
        +projects:  "highest/apex"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        +installed:  "out/target/product/fictional/system/apex/highest.apex"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/liba.so"
        +  container_path:  "/lib/liba.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/libb.so"
        +  container_path:  "/lib/libb.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin1"
        +  container_path:  "/bin/bin1"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin2"
        +  container_path:  "/bin/bin2"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/notice/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/notice/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic b/make/tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic
        new file mode 100644
        index 0000000..7fed5d7
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Device"
        +projects:  "device/library"
        +license_kinds:  "SPDX-license-identifier-BSD"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        +installed:  "out/target/product/fictional/system/lib/liba.so"
        diff --git a/make/tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic b/make/tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic
        new file mode 100644
        index 0000000..e60ef73
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +projects:  "base/library"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        +installed:  "out/target/product/fictional/system/lib/libb.so"
        diff --git a/make/tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic b/make/tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic
        new file mode 100644
        index 0000000..8dbc41b
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic
        @@ -0,0 +1,7 @@
        +package_name:  "External"
        +projects:  "static/library"
        +license_kinds:  "SPDX-license-identifier-MIT"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        diff --git a/make/tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic b/make/tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic
        new file mode 100644
        index 0000000..e6a060c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "External"
        +projects:  "dynamic/library"
        +license_kinds:  "SPDX-license-identifier-MIT"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        +installed:  "out/target/product/fictional/system/lib/libd.so"
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/PROPRIETARY_LICENSE b/make/tools/compliance/cmd/testdata/proprietary/PROPRIETARY_LICENSE
        new file mode 100644
        index 0000000..5d0eb09
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/PROPRIETARY_LICENSE
        @@ -0,0 +1 @@
        +@@@Proprietary License@@@
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/application.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/application.meta_lic
        new file mode 100644
        index 0000000..f307c5c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/application.meta_lic
        @@ -0,0 +1,24 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "distributable/application"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        +installed:  "out/target/product/fictional/bin/application"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +deps:  {
        +  file:  "testdata/proprietary/bin/bin3.meta_lic"
        +  annotations:  "toolchain"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..e0394da
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "static/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libc.a"
        +deps:  {
        +  file:  "testdata/proprietary/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/libc.a.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..da64aa6
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic
        @@ -0,0 +1,20 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "dynamic/binary"
        +license_kinds:  "legacy_proprietary"
        +license_conditions:  "proprietary"
        +license_conditions:  "by_exception_only"
        +license_texts:  "testdata/proprietary/PROPRIETARY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/lib/libd.so"
        +deps:  {
        +  file:  "testdata/proprietary/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/libd.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..7ef14e9
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Compiler"
        +module_classes: "EXECUTABLES"
        +projects:  "standalone/binary"
        +license_kinds:  "SPDX-license-identifier-LGPL-2.0"
        +license_conditions:  "restricted"
        +license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic
        new file mode 100644
        index 0000000..d6605f4
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic
        @@ -0,0 +1,36 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/proprietary/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic
        new file mode 100644
        index 0000000..27ced10
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic
        @@ -0,0 +1,44 @@
        +package_name:  "Android"
        +projects:  "highest/apex"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        +installed:  "out/target/product/fictional/system/apex/highest.apex"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/liba.so"
        +  container_path:  "/lib/liba.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/libb.so"
        +  container_path:  "/lib/libb.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin1"
        +  container_path:  "/bin/bin1"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin2"
        +  container_path:  "/bin/bin2"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/proprietary/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/proprietary/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic
        new file mode 100644
        index 0000000..ceb0f9f
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic
        @@ -0,0 +1,10 @@
        +package_name:  "Device"
        +projects:  "device/library"
        +license_kinds:  "legacy_proprietary"
        +license_conditions:  "proprietary"
        +license_conditions:  "by_exception_only"
        +license_texts:  "testdata/proprietary/PROPRIETARY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        +installed:  "out/target/product/fictional/system/lib/liba.so"
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic
        new file mode 100644
        index 0000000..739d357
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +projects:  "base/library"
        +license_kinds:  "SPDX-license-identifier-GPL-2.0"
        +license_conditions:  "restricted"
        +license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        +installed:  "out/target/product/fictional/system/lib/libb.so"
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic
        new file mode 100644
        index 0000000..5440ea7
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "External"
        +projects:  "static/library"
        +license_kinds:  "legacy_proprietary"
        +license_conditions:  "proprietary"
        +license_conditions:  "by_exception_only"
        +license_texts:  "testdata/proprietary/PROPRIETARY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        diff --git a/make/tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic b/make/tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic
        new file mode 100644
        index 0000000..e6a060c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "External"
        +projects:  "dynamic/library"
        +license_kinds:  "SPDX-license-identifier-MIT"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        +installed:  "out/target/product/fictional/system/lib/libd.so"
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/RECIPROCAL_LICENSE b/make/tools/compliance/cmd/testdata/reciprocal/RECIPROCAL_LICENSE
        new file mode 100644
        index 0000000..82c2019
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/RECIPROCAL_LICENSE
        @@ -0,0 +1 @@
        +$$$Reciprocal License$$$
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/application.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/application.meta_lic
        new file mode 100644
        index 0000000..60233cb
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/application.meta_lic
        @@ -0,0 +1,24 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "distributable/application"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        +installed:  "out/target/product/fictional/bin/application"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +deps:  {
        +  file:  "testdata/reciprocal/bin/bin3.meta_lic"
        +  annotations:  "toolchain"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..54d552f
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "static/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libc.a"
        +deps:  {
        +  file:  "testdata/reciprocal/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/libc.a.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..a28cb91
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "dynamic/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/lib/libd.so"
        +deps:  {
        +  file:  "testdata/reciprocal/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/libd.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..bb9a3d5
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Compiler"
        +module_classes: "EXECUTABLES"
        +projects:  "standalone/binary"
        +license_kinds:  "SPDX-license-identifier-NCSA"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic
        new file mode 100644
        index 0000000..feb08fe
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic
        @@ -0,0 +1,36 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/reciprocal/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic
        new file mode 100644
        index 0000000..185d04a
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic
        @@ -0,0 +1,44 @@
        +package_name:  "Android"
        +projects:  "highest/apex"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        +installed:  "out/target/product/fictional/system/apex/highest.apex"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/liba.so"
        +  container_path:  "/lib/liba.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/libb.so"
        +  container_path:  "/lib/libb.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin1"
        +  container_path:  "/bin/bin1"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin2"
        +  container_path:  "/bin/bin2"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/reciprocal/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/reciprocal/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic
        new file mode 100644
        index 0000000..dd05155
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Device"
        +projects:  "device/library"
        +license_kinds:  "SPDX-license-identifier-MPL"
        +license_conditions:  "reciprocal"
        +license_texts:  "testdata/reciprocal/RECIPROCAL_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        +installed:  "out/target/product/fictional/system/lib/liba.so"
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic
        new file mode 100644
        index 0000000..e60ef73
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +projects:  "base/library"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        +installed:  "out/target/product/fictional/system/lib/libb.so"
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic
        new file mode 100644
        index 0000000..f794305
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic
        @@ -0,0 +1,7 @@
        +package_name:  "External"
        +projects:  "static/library"
        +license_kinds:  "SPDX-license-identifier-MPL"
        +license_conditions:  "reciprocal"
        +license_texts:  "testdata/reciprocal/RECIPROCAL_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        diff --git a/make/tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic b/make/tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic
        new file mode 100644
        index 0000000..e6a060c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "External"
        +projects:  "dynamic/library"
        +license_kinds:  "SPDX-license-identifier-MIT"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        +installed:  "out/target/product/fictional/system/lib/libd.so"
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/README.md b/make/tools/compliance/cmd/testdata/regressgpl1/README.md
        new file mode 100644
        index 0000000..6ab872d
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/README.md
        @@ -0,0 +1,32 @@
        +## Shipped versus non-shipped libraries with restricted license
        +
        +### Testdata build graph structure:
        +
        +A restricted licensed library sandwiched between a notice library and a notice
        +binary. The source-code for the libraries only needs to be shared if shipped
        +alongside the container with the binaries.
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	bin1 [label="bin/bin1.meta_lic\nnotice"];
        +	bin2 [label="bin/bin2.meta_lic\nnotice"];
        +	bin3 [label="bin/bin3.meta_lic\nnotice"];
        +	container [label="container.zip.meta_lic\nnotice"];
        +	libapache [label="lib/libapache.so.meta_lic\nnotice"];
        +	libcxx [label="lib/libc++.so.meta_lic\nnotice"];
        +	libgpl [label="lib/libgpl.so.meta_lic\nrestricted"];
        +	container -> bin1[label="static"];
        +	container -> bin2 [label="static"];
        +	container -> bin3 [label="static"];
        +	bin1 -> libcxx [label="dynamic"];
        +	bin2 -> libapache [label="dynamic"];
        +	bin2 -> libcxx [label="dynamic"];
        +	bin3 -> libapache [label="dynamic"];
        +	bin3 -> libcxx [label="dynamic"];
        +	bin3 -> libgpl [label="dynamic"];
        +	libapache -> libcxx [label="dynamic"];
        +	libgpl -> libcxx [label="dynamic"];
        +	{rank=same; container}
        +}
        +```
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..4afd240
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic
        @@ -0,0 +1,14 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "bin/onelibrary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..4e56fa3
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "bin/twolibraries"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +sources:  "out/target/product/fictional/system/lib/libapache.so"
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libapache.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..16290d3
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic
        @@ -0,0 +1,23 @@
        +package_name:  "Compiler"
        +module_classes: "EXECUTABLES"
        +projects:  "bin/threelibraries"
        +license_kinds:  "SPDX-license-identifier-NCSA"
        +license_conditions:  "notice"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +sources:  "out/target/product/fictional/system/lib/libapache.so"
        +sources:  "out/target/product/fictional/system/lib/libgpl.so"
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libapache.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libgpl.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic
        new file mode 100644
        index 0000000..21b6d5a
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic
        @@ -0,0 +1,32 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +sources:  "out/target/product/fictional/system/lib/libapache.so"
        +deps:  {
        +  file:  "testdata/regressgpl1/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl1/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl1/bin/bin3.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic
        new file mode 100644
        index 0000000..9184501
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic
        @@ -0,0 +1,14 @@
        +package_name:  "Android"
        +projects:  "lib/apache"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a"
        +installed:  "out/target/product/fictional/system/lib/libapache.so"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic
        new file mode 100644
        index 0000000..b789377
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "Device"
        +projects:  "lib/c++"
        +license_kinds:  "SPDX-license-identifier-BSD"
        +license_conditions:  "notice"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a"
        +installed:  "out/target/product/fictional/system/lib/libc++.so"
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic
        new file mode 100644
        index 0000000..a3afa2b
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic
        @@ -0,0 +1,12 @@
        +package_name:  "External"
        +projects:  "lib/gpl"
        +license_kinds:  "SPDX-license-identifier-GPL-2.0"
        +license_conditions:  "restricted"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so"
        +installed:  "out/target/product/fictional/system/lib/libgpl.so"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +deps:  {
        +  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/README.md b/make/tools/compliance/cmd/testdata/regressgpl2/README.md
        new file mode 100644
        index 0000000..9da0f4d
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/README.md
        @@ -0,0 +1,35 @@
        +## Libraries shipped inside container with restricted license
        +
        +### Testdata build graph structure:
        +
        +A restricted licensed library sandwiched between a notice library and a notice
        +binary. The source-code for the libraries needs to be shared when shipped as
        +part of the container with the binaries.
        +
        +```dot
        +strict digraph {
        +	rankdir=LR;
        +	bin1 [label="bin/bin1.meta_lic\nnotice"];
        +	bin2 [label="bin/bin2.meta_lic\nnotice"];
        +	bin3 [label="bin/bin3.meta_lic\nnotice"];
        +	container [label="container.zip.meta_lic\nnotice"];
        +	libapache [label="lib/libapache.so.meta_lic\nnotice"];
        +	libcxx [label="lib/libc++.so.meta_lic\nnotice"];
        +	libgpl [label="lib/libgpl.so.meta_lic\nrestricted"];
        +	container -> bin1[label="static"];
        +	container -> bin2 [label="static"];
        +	container -> bin3 [label="static"];
        +	container -> libapache [label="static"];
        +	container -> libcxx [label="static"];
        +	container -> libgpl [label="static"];
        +	bin1 -> libcxx [label="dynamic"];
        +	bin2 -> libapache [label="dynamic"];
        +	bin2 -> libcxx [label="dynamic"];
        +	bin3 -> libapache [label="dynamic"];
        +	bin3 -> libcxx [label="dynamic"];
        +	bin3 -> libgpl [label="dynamic"];
        +	libapache -> libcxx [label="dynamic"];
        +	libgpl -> libcxx [label="dynamic"];
        +	{rank=same; container}
        +}
        +```
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..839fd9c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic
        @@ -0,0 +1,14 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "bin/onelibrary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..96baaae
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "bin/twolibraries"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +sources:  "out/target/product/fictional/system/lib/libapache.so"
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..3cd8602
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic
        @@ -0,0 +1,23 @@
        +package_name:  "Compiler"
        +module_classes: "EXECUTABLES"
        +projects:  "bin/threelibraries"
        +license_kinds:  "SPDX-license-identifier-NCSA"
        +license_conditions:  "notice"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +sources:  "out/target/product/fictional/system/lib/libapache.so"
        +sources:  "out/target/product/fictional/system/lib/libgpl.so"
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libgpl.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic
        new file mode 100644
        index 0000000..d32bf94
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic
        @@ -0,0 +1,44 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +sources:  "out/target/product/fictional/system/lib/libapache.so"
        +deps:  {
        +  file:  "testdata/regressgpl2/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/bin/bin3.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libgpl.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic
        new file mode 100644
        index 0000000..ae47340
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic
        @@ -0,0 +1,14 @@
        +package_name:  "Android"
        +projects:  "lib/apache"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "build/soong/licenses/LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a"
        +installed:  "out/target/product/fictional/system/lib/libapache.so"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic
        new file mode 100644
        index 0000000..b789377
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "Device"
        +projects:  "lib/c++"
        +license_kinds:  "SPDX-license-identifier-BSD"
        +license_conditions:  "notice"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a"
        +installed:  "out/target/product/fictional/system/lib/libc++.so"
        diff --git a/make/tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic b/make/tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic
        new file mode 100644
        index 0000000..4e78697
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic
        @@ -0,0 +1,12 @@
        +package_name:  "External"
        +projects:  "lib/gpl"
        +license_kinds:  "SPDX-license-identifier-GPL-2.0"
        +license_conditions:  "restricted"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so"
        +installed:  "out/target/product/fictional/system/lib/libgpl.so"
        +sources:  "out/target/product/fictional/system/lib/libc++.so"
        +deps:  {
        +  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/restricted/RESTRICTED_LICENSE b/make/tools/compliance/cmd/testdata/restricted/RESTRICTED_LICENSE
        new file mode 100644
        index 0000000..16a2819
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/RESTRICTED_LICENSE
        @@ -0,0 +1 @@
        +###Restricted License###
        diff --git a/make/tools/compliance/cmd/testdata/restricted/application.meta_lic b/make/tools/compliance/cmd/testdata/restricted/application.meta_lic
        new file mode 100644
        index 0000000..7ef536d
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/application.meta_lic
        @@ -0,0 +1,24 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "distributable/application"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        +installed:  "out/target/product/fictional/bin/application"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin3"
        +deps:  {
        +  file:  "testdata/restricted/bin/bin3.meta_lic"
        +  annotations:  "toolchain"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic b/make/tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic
        new file mode 100644
        index 0000000..ef0d0c0
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "static/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        +installed:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/lib/liba.a"
        +sources:  "out/target/product/fictional/system/lib/libc.a"
        +deps:  {
        +  file:  "testdata/restricted/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/libc.a.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic b/make/tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic
        new file mode 100644
        index 0000000..331d5ac
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic
        @@ -0,0 +1,19 @@
        +package_name:  "Android"
        +module_classes: "EXECUTABLES"
        +projects:  "dynamic/binary"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        +installed:  "out/target/product/fictional/system/bin/bin2"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/lib/libd.so"
        +deps:  {
        +  file:  "testdata/restricted/lib/libb.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/libd.so.meta_lic"
        +  annotations:  "dynamic"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic b/make/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
        new file mode 100644
        index 0000000..7ef14e9
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Compiler"
        +module_classes: "EXECUTABLES"
        +projects:  "standalone/binary"
        +license_kinds:  "SPDX-license-identifier-LGPL-2.0"
        +license_conditions:  "restricted"
        +license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        +installed:  "out/target/product/fictional/system/bin/bin3"
        diff --git a/make/tools/compliance/cmd/testdata/restricted/container.zip.meta_lic b/make/tools/compliance/cmd/testdata/restricted/container.zip.meta_lic
        new file mode 100644
        index 0000000..47e0e24
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/container.zip.meta_lic
        @@ -0,0 +1,36 @@
        +package_name:  "Android"
        +projects:  "container/zip"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        +installed:  "out/target/product/fictional/data/container.zip"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/"
        +  container_path:  "/"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/"
        +  container_path:  "/"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/restricted/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic b/make/tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic
        new file mode 100644
        index 0000000..3042309
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic
        @@ -0,0 +1,44 @@
        +package_name:  "Android"
        +projects:  "highest/apex"
        +license_kinds:  "SPDX-license-identifier-Apache-2.0"
        +license_conditions:  "notice"
        +license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        +is_container:  true
        +built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        +installed:  "out/target/product/fictional/system/apex/highest.apex"
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/liba.so"
        +  container_path:  "/lib/liba.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/lib/libb.so"
        +  container_path:  "/lib/libb.so"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin1"
        +  container_path:  "/bin/bin1"
        +}
        +install_map {
        +  from_path:  "out/target/product/fictional/system/bin/bin2"
        +  container_path:  "/bin/bin2"
        +}
        +sources:  "out/target/product/fictional/system/lib/liba.so"
        +sources:  "out/target/product/fictional/system/lib/libb.so"
        +sources:  "out/target/product/fictional/system/bin/bin1"
        +sources:  "out/target/product/fictional/system/bin/bin2"
        +deps:  {
        +  file:  "testdata/restricted/bin/bin1.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/bin/bin2.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/liba.so.meta_lic"
        +  annotations:  "static"
        +}
        +deps:  {
        +  file:  "testdata/restricted/lib/libb.so.meta_lic"
        +  annotations:  "static"
        +}
        diff --git a/make/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic b/make/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
        new file mode 100644
        index 0000000..a505d4a
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Device"
        +projects:  "device/library"
        +license_kinds:  "SPDX-license-identifier-LGPL-2.0"
        +license_conditions:  "restricted"
        +license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        +installed:  "out/target/product/fictional/system/lib/liba.so"
        diff --git a/make/tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic b/make/tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic
        new file mode 100644
        index 0000000..739d357
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic
        @@ -0,0 +1,9 @@
        +package_name:  "Android"
        +projects:  "base/library"
        +license_kinds:  "SPDX-license-identifier-GPL-2.0"
        +license_conditions:  "restricted"
        +license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        +installed:  "out/target/product/fictional/system/lib/libb.so"
        diff --git a/make/tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic b/make/tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic
        new file mode 100644
        index 0000000..f794305
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic
        @@ -0,0 +1,7 @@
        +package_name:  "External"
        +projects:  "static/library"
        +license_kinds:  "SPDX-license-identifier-MPL"
        +license_conditions:  "reciprocal"
        +license_texts:  "testdata/reciprocal/RECIPROCAL_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        diff --git a/make/tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic b/make/tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic
        new file mode 100644
        index 0000000..e6a060c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic
        @@ -0,0 +1,8 @@
        +package_name:  "External"
        +projects:  "dynamic/library"
        +license_kinds:  "SPDX-license-identifier-MIT"
        +license_conditions:  "notice"
        +license_texts:  "testdata/notice/NOTICE_LICENSE"
        +is_container:  false
        +built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        +installed:  "out/target/product/fictional/system/lib/libd.so"
        diff --git a/make/tools/compliance/cmd/textnotice/textnotice.go b/make/tools/compliance/cmd/textnotice/textnotice.go
        new file mode 100644
        index 0000000..9beaf58
        --- /dev/null
        +++ b/make/tools/compliance/cmd/textnotice/textnotice.go
        @@ -0,0 +1,236 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"compress/gzip"
        +	"flag"
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"os"
        +	"path/filepath"
        +	"strings"
        +
        +	"android/soong/response"
        +	"android/soong/tools/compliance"
        +
        +	"github.com/google/blueprint/deptools"
        +)
        +
        +var (
        +	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        +	failNoLicenses    = fmt.Errorf("No licenses found")
        +)
        +
        +type context struct {
        +	stdout      io.Writer
        +	stderr      io.Writer
        +	rootFS      fs.FS
        +	product     string
        +	stripPrefix []string
        +	title       string
        +	deps        *[]string
        +}
        +
        +func (ctx context) strip(installPath string) string {
        +	for _, prefix := range ctx.stripPrefix {
        +		if strings.HasPrefix(installPath, prefix) {
        +			p := strings.TrimPrefix(installPath, prefix)
        +			if 0 == len(p) {
        +				p = ctx.product
        +			}
        +			if 0 == len(p) {
        +				continue
        +			}
        +			return p
        +		}
        +	}
        +	return installPath
        +}
        +
        +// newMultiString creates a flag that allows multiple values in an array.
        +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
        +	var f multiString
        +	flags.Var(&f, name, usage)
        +	return &f
        +}
        +
        +// multiString implements the flag `Value` interface for multiple strings.
        +type multiString []string
        +
        +func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
        +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
        +
        +func main() {
        +	var expandedArgs []string
        +	for _, arg := range os.Args[1:] {
        +		if strings.HasPrefix(arg, "@") {
        +			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +
        +			respArgs, err := response.ReadRspFile(f)
        +			f.Close()
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +			expandedArgs = append(expandedArgs, respArgs...)
        +		} else {
        +			expandedArgs = append(expandedArgs, arg)
        +		}
        +	}
        +
        +	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        +
        +	flags.Usage = func() {
        +		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        +
        +Outputs a text NOTICE file.
        +
        +Options:
        +`, filepath.Base(os.Args[0]))
        +		flags.PrintDefaults()
        +	}
        +
        +	outputFile := flags.String("o", "-", "Where to write the NOTICE text file. (default stdout)")
        +	depsFile := flags.String("d", "", "Where to write the deps file")
        +	product := flags.String("product", "", "The name of the product for which the notice is generated.")
        +	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
        +	title := flags.String("title", "", "The title of the notice file.")
        +
        +	flags.Parse(expandedArgs)
        +
        +	// Must specify at least one root target.
        +	if flags.NArg() == 0 {
        +		flags.Usage()
        +		os.Exit(2)
        +	}
        +
        +	if len(*outputFile) == 0 {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        +		os.Exit(2)
        +	} else {
        +		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +		fi, err := os.Stat(dir)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        +			os.Exit(1)
        +		}
        +		if !fi.IsDir() {
        +			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        +			os.Exit(1)
        +		}
        +	}
        +
        +	var ofile io.Writer
        +	var closer io.Closer
        +	ofile = os.Stdout
        +	var obuf *bytes.Buffer
        +	if *outputFile != "-" {
        +		obuf = &bytes.Buffer{}
        +		ofile = obuf
        +	}
        +	if strings.HasSuffix(*outputFile, ".gz") {
        +		ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)
        +		closer = ofile.(io.Closer)
        +	}
        +
        +	var deps []string
        +
        +	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
        +
        +	err := textNotice(ctx, flags.Args()...)
        +	if err != nil {
        +		if err == failNoneRequested {
        +			flags.Usage()
        +		}
        +		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        +		os.Exit(1)
        +	}
        +	if closer != nil {
        +		closer.Close()
        +	}
        +
        +	if *outputFile != "-" {
        +		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +	}
        +	if *depsFile != "" {
        +		err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
        +			os.Exit(1)
        +		}
        +	}
        +	os.Exit(0)
        +}
        +
        +// textNotice implements the textNotice utility.
        +func textNotice(ctx *context, files ...string) error {
        +	// Must be at least one root file.
        +	if len(files) < 1 {
        +		return failNoneRequested
        +	}
        +
        +	// Read the license graph from the license metadata files (*.meta_lic).
        +	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        +	}
        +	if licenseGraph == nil {
        +		return failNoLicenses
        +	}
        +
        +	// rs contains all notice resolutions.
        +	rs := compliance.ResolveNotices(licenseGraph)
        +
        +	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
        +	}
        +
        +	if len(ctx.title) > 0 {
        +		fmt.Fprintf(ctx.stdout, "%s\n\n", ctx.title)
        +	}
        +	for h := range ni.Hashes() {
        +		fmt.Fprintln(ctx.stdout, "==============================================================================")
        +		for _, libName := range ni.HashLibs(h) {
        +			fmt.Fprintf(ctx.stdout, "%s used by:\n", libName)
        +			for _, installPath := range ni.HashLibInstalls(h, libName) {
        +				fmt.Fprintf(ctx.stdout, "  %s\n", ctx.strip(installPath))
        +			}
        +			fmt.Fprintln(ctx.stdout)
        +		}
        +		ctx.stdout.Write(ni.HashText(h))
        +		fmt.Fprintln(ctx.stdout)
        +	}
        +
        +	*ctx.deps = ni.InputNoticeFiles()
        +
        +	return nil
        +}
        diff --git a/make/tools/compliance/cmd/textnotice/textnotice_test.go b/make/tools/compliance/cmd/textnotice/textnotice_test.go
        new file mode 100644
        index 0000000..e661a44
        --- /dev/null
        +++ b/make/tools/compliance/cmd/textnotice/textnotice_test.go
        @@ -0,0 +1,719 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bufio"
        +	"bytes"
        +	"fmt"
        +	"os"
        +	"reflect"
        +	"regexp"
        +	"strings"
        +	"testing"
        +
        +	"android/soong/tools/compliance"
        +)
        +
        +var (
        +	horizontalRule = regexp.MustCompile("^===[=]*===$")
        +)
        +
        +func TestMain(m *testing.M) {
        +	// Change into the parent directory before running the tests
        +	// so they can find the testdata directory.
        +	if err := os.Chdir(".."); err != nil {
        +		fmt.Printf("failed to change to testdata directory: %s\n", err)
        +		os.Exit(1)
        +	}
        +	os.Exit(m.Run())
        +}
        +
        +func Test(t *testing.T) {
        +	tests := []struct {
        +		condition    string
        +		name         string
        +		outDir       string
        +		roots        []string
        +		stripPrefix  string
        +		expectedOut  []matcher
        +		expectedDeps []string
        +	}{
        +		{
        +			condition: "firstparty",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				usedBy{"highest.apex/lib/liba.so"},
        +				usedBy{"highest.apex/lib/libb.so"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/bin2"},
        +				usedBy{"container.zip/liba.so"},
        +				usedBy{"container.zip/libb.so"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"application"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"bin/bin1"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"lib/libd.so"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				usedBy{"highest.apex/lib/libb.so"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/lib/liba.so"},
        +				library{"External"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/bin2"},
        +				usedBy{"container.zip/libb.so"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/liba.so"},
        +				library{"External"},
        +				usedBy{"container.zip/bin1"},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"application"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"application"},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"bin/bin1"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"bin/bin1"},
        +				library{"External"},
        +				usedBy{"bin/bin1"},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"External"},
        +				usedBy{"lib/libd.so"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				usedBy{"highest.apex/lib/libb.so"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/lib/liba.so"},
        +				library{"External"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/bin2"},
        +				usedBy{"container.zip/libb.so"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/liba.so"},
        +				library{"External"},
        +				usedBy{"container.zip/bin1"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"application"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"application"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"bin/bin1"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"bin/bin1"},
        +				library{"External"},
        +				usedBy{"bin/bin1"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"External"},
        +				usedBy{"lib/libd.so"},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				firstParty{},
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				usedBy{"highest.apex/lib/libb.so"},
        +				library{"Device"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/lib/liba.so"},
        +				restricted{},
        +				hr{},
        +				library{"External"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/bin2"},
        +				firstParty{},
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip/bin2"},
        +				usedBy{"container.zip/libb.so"},
        +				library{"Device"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/liba.so"},
        +				restricted{},
        +				hr{},
        +				library{"External"},
        +				usedBy{"container.zip/bin1"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"application"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"application"},
        +				restricted{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"bin/bin1"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"bin/bin1"},
        +				restricted{},
        +				hr{},
        +				library{"External"},
        +				usedBy{"bin/bin1"},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"External"},
        +				usedBy{"lib/libd.so"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				usedBy{"highest.apex/lib/libb.so"},
        +				restricted{},
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				firstParty{},
        +				hr{},
        +				library{"Android"},
        +				usedBy{"highest.apex/bin/bin2"},
        +				library{"Device"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				usedBy{"highest.apex/lib/liba.so"},
        +				library{"External"},
        +				usedBy{"highest.apex/bin/bin1"},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip/bin2"},
        +				usedBy{"container.zip/libb.so"},
        +				restricted{},
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip"},
        +				usedBy{"container.zip/bin1"},
        +				firstParty{},
        +				hr{},
        +				library{"Android"},
        +				usedBy{"container.zip/bin2"},
        +				library{"Device"},
        +				usedBy{"container.zip/bin1"},
        +				usedBy{"container.zip/liba.so"},
        +				library{"External"},
        +				usedBy{"container.zip/bin1"},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"application"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"application"},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"Android"},
        +				usedBy{"bin/bin1"},
        +				firstParty{},
        +				hr{},
        +				library{"Device"},
        +				usedBy{"bin/bin1"},
        +				library{"External"},
        +				usedBy{"bin/bin1"},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				hr{},
        +				library{"External"},
        +				usedBy{"lib/libd.so"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        +			stdout := &bytes.Buffer{}
        +			stderr := &bytes.Buffer{}
        +
        +			rootFiles := make([]string, 0, len(tt.roots))
        +			for _, r := range tt.roots {
        +				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        +			}
        +
        +			var deps []string
        +
        +			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, "", &deps}
        +
        +			err := textNotice(&ctx, rootFiles...)
        +			if err != nil {
        +				t.Fatalf("textnotice: error = %v, stderr = %v", err, stderr)
        +				return
        +			}
        +			if stderr.Len() > 0 {
        +				t.Errorf("textnotice: gotStderr = %v, want none", stderr)
        +			}
        +
        +			t.Logf("got stdout: %s", stdout.String())
        +
        +			t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
        +
        +			out := bufio.NewScanner(stdout)
        +			lineno := 0
        +			for out.Scan() {
        +				line := out.Text()
        +				if strings.TrimLeft(line, " ") == "" {
        +					continue
        +				}
        +				if len(tt.expectedOut) <= lineno {
        +					t.Errorf("unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
        +				} else if !tt.expectedOut[lineno].isMatch(line) {
        +					t.Errorf("unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
        +				}
        +				lineno++
        +			}
        +			for ; lineno < len(tt.expectedOut); lineno++ {
        +				t.Errorf("textnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
        +			}
        +
        +			t.Logf("got deps: %q", deps)
        +
        +			t.Logf("want deps: %q", tt.expectedDeps)
        +
        +			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
        +				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
        +					strings.Join(w, "\n"), strings.Join(g, "\n"))
        +			}
        +		})
        +	}
        +}
        +
        +type matcher interface {
        +	isMatch(line string) bool
        +	String() string
        +}
        +
        +type hr struct{}
        +
        +func (m hr) isMatch(line string) bool {
        +	return horizontalRule.MatchString(line)
        +}
        +
        +func (m hr) String() string {
        +	return " ================================================== "
        +}
        +
        +type library struct {
        +	name string
        +}
        +
        +func (m library) isMatch(line string) bool {
        +	return strings.HasPrefix(line, m.name+" ")
        +}
        +
        +func (m library) String() string {
        +	return m.name + " used by:"
        +}
        +
        +type usedBy struct {
        +	name string
        +}
        +
        +func (m usedBy) isMatch(line string) bool {
        +	return len(line) > 0 && line[0] == ' ' && strings.HasPrefix(strings.TrimLeft(line, " "), "out/") && strings.HasSuffix(line, "/"+m.name)
        +}
        +
        +func (m usedBy) String() string {
        +	return "  out/.../" + m.name
        +}
        +
        +type firstParty struct{}
        +
        +func (m firstParty) isMatch(line string) bool {
        +	return strings.HasPrefix(strings.TrimLeft(line, " "), "&&&First Party License&&&")
        +}
        +
        +func (m firstParty) String() string {
        +	return "&&&First Party License&&&"
        +}
        +
        +type notice struct{}
        +
        +func (m notice) isMatch(line string) bool {
        +	return strings.HasPrefix(strings.TrimLeft(line, " "), "%%%Notice License%%%")
        +}
        +
        +func (m notice) String() string {
        +	return "%%%Notice License%%%"
        +}
        +
        +type reciprocal struct{}
        +
        +func (m reciprocal) isMatch(line string) bool {
        +	return strings.HasPrefix(strings.TrimLeft(line, " "), "$$$Reciprocal License$$$")
        +}
        +
        +func (m reciprocal) String() string {
        +	return "$$$Reciprocal License$$$"
        +}
        +
        +type restricted struct{}
        +
        +func (m restricted) isMatch(line string) bool {
        +	return strings.HasPrefix(strings.TrimLeft(line, " "), "###Restricted License###")
        +}
        +
        +func (m restricted) String() string {
        +	return "###Restricted License###"
        +}
        +
        +type proprietary struct{}
        +
        +func (m proprietary) isMatch(line string) bool {
        +	return strings.HasPrefix(strings.TrimLeft(line, " "), "@@@Proprietary License@@@")
        +}
        +
        +func (m proprietary) String() string {
        +	return "@@@Proprietary License@@@"
        +}
        +
        +type matcherList []matcher
        +
        +func (l matcherList) String() string {
        +	var sb strings.Builder
        +	for _, m := range l {
        +		s := m.String()
        +		if s[:3] == s[len(s)-3:] {
        +			fmt.Fprintln(&sb)
        +		}
        +		fmt.Fprintf(&sb, "%s\n", s)
        +		if s[:3] == s[len(s)-3:] {
        +			fmt.Fprintln(&sb)
        +		}
        +	}
        +	return sb.String()
        +}
        diff --git a/make/tools/compliance/cmd/xmlnotice/xmlnotice.go b/make/tools/compliance/cmd/xmlnotice/xmlnotice.go
        new file mode 100644
        index 0000000..2097b7c
        --- /dev/null
        +++ b/make/tools/compliance/cmd/xmlnotice/xmlnotice.go
        @@ -0,0 +1,244 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bytes"
        +	"compress/gzip"
        +	"encoding/xml"
        +	"flag"
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"os"
        +	"path/filepath"
        +	"strings"
        +
        +	"android/soong/response"
        +	"android/soong/tools/compliance"
        +
        +	"github.com/google/blueprint/deptools"
        +)
        +
        +var (
        +	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        +	failNoLicenses    = fmt.Errorf("No licenses found")
        +)
        +
        +type context struct {
        +	stdout      io.Writer
        +	stderr      io.Writer
        +	rootFS      fs.FS
        +	product     string
        +	stripPrefix []string
        +	title       string
        +	deps        *[]string
        +}
        +
        +func (ctx context) strip(installPath string) string {
        +	for _, prefix := range ctx.stripPrefix {
        +		if strings.HasPrefix(installPath, prefix) {
        +			p := strings.TrimPrefix(installPath, prefix)
        +			if 0 == len(p) {
        +				p = ctx.product
        +			}
        +			if 0 == len(p) {
        +				continue
        +			}
        +			return p
        +		}
        +	}
        +	return installPath
        +}
        +
        +// newMultiString creates a flag that allows multiple values in an array.
        +func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
        +	var f multiString
        +	flags.Var(&f, name, usage)
        +	return &f
        +}
        +
        +// multiString implements the flag `Value` interface for multiple strings.
        +type multiString []string
        +
        +func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
        +func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
        +
        +func main() {
        +	var expandedArgs []string
        +	for _, arg := range os.Args[1:] {
        +		if strings.HasPrefix(arg, "@") {
        +			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +
        +			respArgs, err := response.ReadRspFile(f)
        +			f.Close()
        +			if err != nil {
        +				fmt.Fprintln(os.Stderr, err.Error())
        +				os.Exit(1)
        +			}
        +			expandedArgs = append(expandedArgs, respArgs...)
        +		} else {
        +			expandedArgs = append(expandedArgs, arg)
        +		}
        +	}
        +
        +	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        +
        +	flags.Usage = func() {
        +		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        +
        +Outputs an xml NOTICE.xml or gzipped NOTICE.xml.gz file if the -o filename ends
        +with ".gz".
        +
        +Options:
        +`, filepath.Base(os.Args[0]))
        +		flags.PrintDefaults()
        +	}
        +
        +	outputFile := flags.String("o", "-", "Where to write the NOTICE xml or xml.gz file. (default stdout)")
        +	depsFile := flags.String("d", "", "Where to write the deps file")
        +	product := flags.String("product", "", "The name of the product for which the notice is generated.")
        +	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
        +	title := flags.String("title", "", "The title of the notice file.")
        +
        +	flags.Parse(expandedArgs)
        +
        +	// Must specify at least one root target.
        +	if flags.NArg() == 0 {
        +		flags.Usage()
        +		os.Exit(2)
        +	}
        +
        +	if len(*outputFile) == 0 {
        +		flags.Usage()
        +		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        +		os.Exit(2)
        +	} else {
        +		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +		fi, err := os.Stat(dir)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        +			os.Exit(1)
        +		}
        +		if !fi.IsDir() {
        +			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        +			os.Exit(1)
        +		}
        +	}
        +
        +	var ofile io.Writer
        +	var closer io.Closer
        +	ofile = os.Stdout
        +	var obuf *bytes.Buffer
        +	if *outputFile != "-" {
        +		obuf = &bytes.Buffer{}
        +		ofile = obuf
        +	}
        +	if strings.HasSuffix(*outputFile, ".gz") {
        +		ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)
        +		closer = ofile.(io.Closer)
        +	}
        +
        +	var deps []string
        +
        +	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
        +
        +	err := xmlNotice(ctx, flags.Args()...)
        +	if err != nil {
        +		if err == failNoneRequested {
        +			flags.Usage()
        +		}
        +		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        +		os.Exit(1)
        +	}
        +	if closer != nil {
        +		closer.Close()
        +	}
        +
        +	if *outputFile != "-" {
        +		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
        +			os.Exit(1)
        +		}
        +	}
        +	if *depsFile != "" {
        +		err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
        +		if err != nil {
        +			fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
        +			os.Exit(1)
        +		}
        +	}
        +	os.Exit(0)
        +}
        +
        +// xmlNotice implements the xmlnotice utility.
        +func xmlNotice(ctx *context, files ...string) error {
        +	// Must be at least one root file.
        +	if len(files) < 1 {
        +		return failNoneRequested
        +	}
        +
        +	// Read the license graph from the license metadata files (*.meta_lic).
        +	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        +	}
        +	if licenseGraph == nil {
        +		return failNoLicenses
        +	}
        +
        +	// rs contains all notice resolutions.
        +	rs := compliance.ResolveNotices(licenseGraph)
        +
        +	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
        +	if err != nil {
        +		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
        +	}
        +
        +	fmt.Fprintln(ctx.stdout, "")
        +	fmt.Fprintln(ctx.stdout, "")
        +
        +	for installPath := range ni.InstallPaths() {
        +		p := ctx.strip(installPath)
        +		for _, h := range ni.InstallHashes(installPath) {
        +			for _, lib := range ni.InstallHashLibs(installPath, h) {
        +				fmt.Fprintf(ctx.stdout, "")
        +				xml.EscapeText(ctx.stdout, []byte(p))
        +				fmt.Fprintln(ctx.stdout, "")
        +			}
        +		}
        +	}
        +	for h := range ni.Hashes() {
        +		fmt.Fprintf(ctx.stdout, "\n\n")
        +	}
        +	fmt.Fprintln(ctx.stdout, "")
        +
        +	*ctx.deps = ni.InputNoticeFiles()
        +
        +	return nil
        +}
        diff --git a/make/tools/compliance/cmd/xmlnotice/xmlnotice_test.go b/make/tools/compliance/cmd/xmlnotice/xmlnotice_test.go
        new file mode 100644
        index 0000000..731e783
        --- /dev/null
        +++ b/make/tools/compliance/cmd/xmlnotice/xmlnotice_test.go
        @@ -0,0 +1,637 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package main
        +
        +import (
        +	"bufio"
        +	"bytes"
        +	"encoding/xml"
        +	"fmt"
        +	"os"
        +	"reflect"
        +	"regexp"
        +	"strings"
        +	"testing"
        +
        +	"android/soong/tools/compliance"
        +)
        +
        +var (
        +	installTarget = regexp.MustCompile(`^([^<]+)`)
        +	licenseText = regexp.MustCompile(`^`)
        +)
        +
        +func TestMain(m *testing.M) {
        +	// Change into the parent directory before running the tests
        +	// so they can find the testdata directory.
        +	if err := os.Chdir(".."); err != nil {
        +		fmt.Printf("failed to change to testdata directory: %s\n", err)
        +		os.Exit(1)
        +	}
        +	os.Exit(m.Run())
        +}
        +
        +func Test(t *testing.T) {
        +	tests := []struct {
        +		condition    string
        +		name         string
        +		outDir       string
        +		roots        []string
        +		stripPrefix  string
        +		expectedOut  []matcher
        +		expectedDeps []string
        +	}{
        +		{
        +			condition: "firstparty",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"highest.apex", "Android"},
        +				target{"highest.apex/bin/bin1", "Android"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/lib/liba.so", "Android"},
        +				target{"highest.apex/lib/libb.so", "Android"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"container.zip", "Android"},
        +				target{"container.zip/bin1", "Android"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/liba.so", "Android"},
        +				target{"container.zip/libb.so", "Android"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"application", "Android"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"bin/bin1", "Android"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "firstparty",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"lib/libd.so", "Android"},
        +				firstParty{},
        +			},
        +			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"highest.apex", "Android"},
        +				target{"highest.apex/bin/bin1", "Android"},
        +				target{"highest.apex/bin/bin1", "Device"},
        +				target{"highest.apex/bin/bin1", "External"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/lib/liba.so", "Device"},
        +				target{"highest.apex/lib/libb.so", "Android"},
        +				firstParty{},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"container.zip", "Android"},
        +				target{"container.zip/bin1", "Android"},
        +				target{"container.zip/bin1", "Device"},
        +				target{"container.zip/bin1", "External"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/liba.so", "Device"},
        +				target{"container.zip/libb.so", "Android"},
        +				firstParty{},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"application", "Android"},
        +				target{"application", "Device"},
        +				firstParty{},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"bin/bin1", "Android"},
        +				target{"bin/bin1", "Device"},
        +				target{"bin/bin1", "External"},
        +				firstParty{},
        +				notice{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/notice/NOTICE_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "notice",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"lib/libd.so", "External"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"highest.apex", "Android"},
        +				target{"highest.apex/bin/bin1", "Android"},
        +				target{"highest.apex/bin/bin1", "Device"},
        +				target{"highest.apex/bin/bin1", "External"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/lib/liba.so", "Device"},
        +				target{"highest.apex/lib/libb.so", "Android"},
        +				firstParty{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"container.zip", "Android"},
        +				target{"container.zip/bin1", "Android"},
        +				target{"container.zip/bin1", "Device"},
        +				target{"container.zip/bin1", "External"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/liba.so", "Device"},
        +				target{"container.zip/libb.so", "Android"},
        +				firstParty{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"application", "Android"},
        +				target{"application", "Device"},
        +				firstParty{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"bin/bin1", "Android"},
        +				target{"bin/bin1", "Device"},
        +				target{"bin/bin1", "External"},
        +				firstParty{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "reciprocal",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"lib/libd.so", "External"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"highest.apex", "Android"},
        +				target{"highest.apex/bin/bin1", "Android"},
        +				target{"highest.apex/bin/bin1", "Device"},
        +				target{"highest.apex/bin/bin1", "External"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/lib/liba.so", "Device"},
        +				target{"highest.apex/lib/libb.so", "Android"},
        +				firstParty{},
        +				restricted{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"container.zip", "Android"},
        +				target{"container.zip/bin1", "Android"},
        +				target{"container.zip/bin1", "Device"},
        +				target{"container.zip/bin1", "External"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/liba.so", "Device"},
        +				target{"container.zip/libb.so", "Android"},
        +				firstParty{},
        +				restricted{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"application", "Android"},
        +				target{"application", "Device"},
        +				firstParty{},
        +				restricted{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"bin/bin1", "Android"},
        +				target{"bin/bin1", "Device"},
        +				target{"bin/bin1", "External"},
        +				firstParty{},
        +				restricted{},
        +				reciprocal{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/reciprocal/RECIPROCAL_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "restricted",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"lib/libd.so", "External"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "apex",
        +			roots:     []string{"highest.apex.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"highest.apex", "Android"},
        +				target{"highest.apex/bin/bin1", "Android"},
        +				target{"highest.apex/bin/bin1", "Device"},
        +				target{"highest.apex/bin/bin1", "External"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/bin/bin2", "Android"},
        +				target{"highest.apex/lib/liba.so", "Device"},
        +				target{"highest.apex/lib/libb.so", "Android"},
        +				restricted{},
        +				firstParty{},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "container",
        +			roots:     []string{"container.zip.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"container.zip", "Android"},
        +				target{"container.zip/bin1", "Android"},
        +				target{"container.zip/bin1", "Device"},
        +				target{"container.zip/bin1", "External"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/bin2", "Android"},
        +				target{"container.zip/liba.so", "Device"},
        +				target{"container.zip/libb.so", "Android"},
        +				restricted{},
        +				firstParty{},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +				"testdata/restricted/RESTRICTED_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "application",
        +			roots:     []string{"application.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"application", "Android"},
        +				target{"application", "Device"},
        +				firstParty{},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "binary",
        +			roots:     []string{"bin/bin1.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"bin/bin1", "Android"},
        +				target{"bin/bin1", "Device"},
        +				target{"bin/bin1", "External"},
        +				firstParty{},
        +				proprietary{},
        +			},
        +			expectedDeps: []string{
        +				"testdata/firstparty/FIRST_PARTY_LICENSE",
        +				"testdata/proprietary/PROPRIETARY_LICENSE",
        +			},
        +		},
        +		{
        +			condition: "proprietary",
        +			name:      "library",
        +			roots:     []string{"lib/libd.so.meta_lic"},
        +			expectedOut: []matcher{
        +				target{"lib/libd.so", "External"},
        +				notice{},
        +			},
        +			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        +			stdout := &bytes.Buffer{}
        +			stderr := &bytes.Buffer{}
        +
        +			rootFiles := make([]string, 0, len(tt.roots))
        +			for _, r := range tt.roots {
        +				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        +			}
        +
        +			var deps []string
        +
        +			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, "", &deps}
        +
        +			err := xmlNotice(&ctx, rootFiles...)
        +			if err != nil {
        +				t.Fatalf("xmlnotice: error = %v, stderr = %v", err, stderr)
        +				return
        +			}
        +			if stderr.Len() > 0 {
        +				t.Errorf("xmlnotice: gotStderr = %v, want none", stderr)
        +			}
        +
        +			t.Logf("got stdout: %s", stdout.String())
        +
        +			t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
        +
        +			out := bufio.NewScanner(stdout)
        +			lineno := 0
        +			inBody := false
        +			outOfBody := true
        +			for out.Scan() {
        +				line := out.Text()
        +				if strings.TrimLeft(line, " ") == "" {
        +					continue
        +				}
        +				if lineno == 0 && !inBody && `` == line {
        +					continue
        +				}
        +				if !inBody {
        +					if "" == line {
        +						inBody = true
        +						outOfBody = false
        +					}
        +					continue
        +				} else if "" == line {
        +					outOfBody = true
        +					continue
        +				}
        +
        +				if len(tt.expectedOut) <= lineno {
        +					t.Errorf("xmlnotice: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
        +				} else if !tt.expectedOut[lineno].isMatch(line) {
        +					t.Errorf("xmlnotice: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
        +				}
        +				lineno++
        +			}
        +			if !inBody {
        +				t.Errorf("xmlnotice: missing  tag: got no  tag, want  tag on 2nd line")
        +			}
        +			if !outOfBody {
        +				t.Errorf("xmlnotice: missing  tag: got no  tag, want  tag on last line")
        +			}
        +			for ; lineno < len(tt.expectedOut); lineno++ {
        +				t.Errorf("xmlnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
        +			}
        +
        +			t.Logf("got deps: %q", deps)
        +
        +			t.Logf("want deps: %q", tt.expectedDeps)
        +
        +			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
        +				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
        +					strings.Join(w, "\n"), strings.Join(g, "\n"))
        +			}
        +		})
        +	}
        +}
        +
        +func escape(s string) string {
        +	b := &bytes.Buffer{}
        +	xml.EscapeText(b, []byte(s))
        +	return b.String()
        +}
        +
        +type matcher interface {
        +	isMatch(line string) bool
        +	String() string
        +}
        +
        +type target struct {
        +	name string
        +	lib string
        +}
        +
        +func (m target) isMatch(line string) bool {
        +	groups := installTarget.FindStringSubmatch(line)
        +	if len(groups) != 3 {
        +		return false
        +	}
        +	return groups[1] == escape(m.lib) && strings.HasPrefix(groups[2], "out/") && strings.HasSuffix(groups[2], "/"+escape(m.name))
        +}
        +
        +func (m target) String() string {
        +	return `` + escape(m.name) + ``
        +}
        +
        +func matchesText(line, text string) bool {
        +	groups := licenseText.FindStringSubmatch(line)
        +	if len(groups) != 2 {
        +		return false
        +	}
        +	return groups[1] == escape(text + "\n")
        +}
        +
        +func expectedText(text string) string {
        +	return ``
        +}
        +
        +type firstParty struct{}
        +
        +func (m firstParty) isMatch(line string) bool {
        +	return matchesText(line, "&&&First Party License&&&")
        +}
        +
        +func (m firstParty) String() string {
        +	return expectedText("&&&First Party License&&&")
        +}
        +
        +type notice struct{}
        +
        +func (m notice) isMatch(line string) bool {
        +	return matchesText(line, "%%%Notice License%%%")
        +}
        +
        +func (m notice) String() string {
        +	return expectedText("%%%Notice License%%%")
        +}
        +
        +type reciprocal struct{}
        +
        +func (m reciprocal) isMatch(line string) bool {
        +	return matchesText(line, "$$$Reciprocal License$$$")
        +}
        +
        +func (m reciprocal) String() string {
        +	return expectedText("$$$Reciprocal License$$$")
        +}
        +
        +type restricted struct{}
        +
        +func (m restricted) isMatch(line string) bool {
        +	return matchesText(line, "###Restricted License###")
        +}
        +
        +func (m restricted) String() string {
        +	return expectedText("###Restricted License###")
        +}
        +
        +type proprietary struct{}
        +
        +func (m proprietary) isMatch(line string) bool {
        +	return matchesText(line, "@@@Proprietary License@@@")
        +}
        +
        +func (m proprietary) String() string {
        +	return expectedText("@@@Proprietary License@@@")
        +}
        +
        +type matcherList []matcher
        +
        +func (l matcherList) String() string {
        +	var sb strings.Builder
        +	fmt.Fprintln(&sb, ``)
        +	fmt.Fprintln(&sb, ``)
        +	for _, m := range l {
        +		s := m.String()
        +		fmt.Fprintln(&sb, s)
        +		if _, ok := m.(target); !ok {
        +			fmt.Fprintln(&sb)
        +		}
        +	}
        +	fmt.Fprintln(&sb, `/`)
        +	return sb.String()
        +}
        diff --git a/make/tools/compliance/condition.go b/make/tools/compliance/condition.go
        new file mode 100644
        index 0000000..cfe6f82
        --- /dev/null
        +++ b/make/tools/compliance/condition.go
        @@ -0,0 +1,102 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +)
        +
        +// LicenseCondition identifies a recognized license condition by setting the
        +// corresponding bit.
        +type LicenseCondition uint16
        +
        +// LicenseConditionMask is a bitmask for the recognized license conditions.
        +const LicenseConditionMask = LicenseCondition(0x3ff)
        +
        +const (
        +	// UnencumberedCondition identifies public domain or public domain-
        +	// like license that disclaims copyright.
        +	UnencumberedCondition = LicenseCondition(0x0001)
        +	// PermissiveCondition identifies a license without notice or other
        +	// significant requirements.
        +	PermissiveCondition = LicenseCondition(0x0002)
        +	// NoticeCondition identifies a typical open-source license with only
        +	// notice or attribution requirements.
        +	NoticeCondition = LicenseCondition(0x0004)
        +	// ReciprocalCondition identifies a license with requirement to share
        +	// the module's source only.
        +	ReciprocalCondition = LicenseCondition(0x0008)
        +	// RestrictedCondition identifies a license with requirement to share
        +	// all source code linked to the module's source.
        +	RestrictedCondition = LicenseCondition(0x0010)
        +	// RestrictedClasspathExceptionCondition identifies RestrictedCondition
        +	// waived for dynamic linking from independent modules.
        +	RestrictedClasspathExceptionCondition = LicenseCondition(0x0020)
        +	// WeaklyRestrictedCondition identifies a RestrictedCondition waived
        +	// for dynamic linking.
        +	WeaklyRestrictedCondition = LicenseCondition(0x0040)
        +	// ProprietaryCondition identifies a license with source privacy
        +	// requirements.
        +	ProprietaryCondition = LicenseCondition(0x0080)
        +	// ByExceptionOnly identifies a license where policy requires product
        +	// counsel review prior to use.
        +	ByExceptionOnlyCondition = LicenseCondition(0x0100)
        +	// NotAllowedCondition identifies a license with onerous conditions
        +	// where policy prohibits use.
        +	NotAllowedCondition = LicenseCondition(0x0200)
        +)
        +
        +var (
        +	// RecognizedConditionNames maps condition strings to LicenseCondition.
        +	RecognizedConditionNames = map[string]LicenseCondition{
        +		"unencumbered":                        UnencumberedCondition,
        +		"permissive":                          PermissiveCondition,
        +		"notice":                              NoticeCondition,
        +		"reciprocal":                          ReciprocalCondition,
        +		"restricted":                          RestrictedCondition,
        +		"restricted_with_classpath_exception": RestrictedClasspathExceptionCondition,
        +		"restricted_allows_dynamic_linking":   WeaklyRestrictedCondition,
        +		"proprietary":                         ProprietaryCondition,
        +		"by_exception_only":                   ByExceptionOnlyCondition,
        +		"not_allowed":                         NotAllowedCondition,
        +	}
        +)
        +
        +// Name returns the condition string corresponding to the LicenseCondition.
        +func (lc LicenseCondition) Name() string {
        +	switch lc {
        +	case UnencumberedCondition:
        +		return "unencumbered"
        +	case PermissiveCondition:
        +		return "permissive"
        +	case NoticeCondition:
        +		return "notice"
        +	case ReciprocalCondition:
        +		return "reciprocal"
        +	case RestrictedCondition:
        +		return "restricted"
        +	case RestrictedClasspathExceptionCondition:
        +		return "restricted_with_classpath_exception"
        +	case WeaklyRestrictedCondition:
        +		return "restricted_allows_dynamic_linking"
        +	case ProprietaryCondition:
        +		return "proprietary"
        +	case ByExceptionOnlyCondition:
        +		return "by_exception_only"
        +	case NotAllowedCondition:
        +		return "not_allowed"
        +	}
        +	panic(fmt.Errorf("unrecognized license condition: %04x", lc))
        +}
        diff --git a/make/tools/compliance/condition_test.go b/make/tools/compliance/condition_test.go
        new file mode 100644
        index 0000000..778ce4a
        --- /dev/null
        +++ b/make/tools/compliance/condition_test.go
        @@ -0,0 +1,67 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"testing"
        +)
        +
        +func TestConditionSetHas(t *testing.T) {
        +	impliesShare := ImpliesShared
        +
        +	t.Logf("testing with imliesShare=%04x", impliesShare)
        +
        +	if impliesShare.HasAny(NoticeCondition) {
        +		t.Errorf("impliesShare.HasAny(\"notice\"=%04x) got true, want false", NoticeCondition)
        +	}
        +
        +	if !impliesShare.HasAny(RestrictedCondition) {
        +		t.Errorf("impliesShare.HasAny(\"restricted\"=%04x) got false, want true", RestrictedCondition)
        +	}
        +
        +	if !impliesShare.HasAny(ReciprocalCondition) {
        +		t.Errorf("impliesShare.HasAny(\"reciprocal\"=%04x) got false, want true", ReciprocalCondition)
        +	}
        +
        +	if impliesShare.HasAny(LicenseCondition(0x0000)) {
        +		t.Errorf("impliesShare.HasAny(nil=%04x) got true, want false", LicenseCondition(0x0000))
        +	}
        +}
        +
        +func TestConditionName(t *testing.T) {
        +	for expected, condition := range RecognizedConditionNames {
        +		actual := condition.Name()
        +		if expected != actual {
        +			t.Errorf("unexpected name for condition %04x: got %s, want %s", condition, actual, expected)
        +		}
        +	}
        +}
        +
        +func TestConditionName_InvalidCondition(t *testing.T) {
        +	panicked := false
        +	var lc LicenseCondition
        +	func() {
        +		defer func() {
        +			if err := recover(); err != nil {
        +				panicked = true
        +			}
        +		}()
        +		name := lc.Name()
        +		t.Errorf("invalid condition unexpected name: got %s, wanted panic", name)
        +	}()
        +	if !panicked {
        +		t.Errorf("no expected panic for %04x.Name(): got no panic, wanted panic", lc)
        +	}
        +}
        diff --git a/make/tools/compliance/conditionset.go b/make/tools/compliance/conditionset.go
        new file mode 100644
        index 0000000..7a12ddc
        --- /dev/null
        +++ b/make/tools/compliance/conditionset.go
        @@ -0,0 +1,189 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +	"strings"
        +)
        +
        +// LicenseConditionSet identifies sets of license conditions.
        +type LicenseConditionSet LicenseCondition
        +
        +// AllLicenseConditions is the set of all recognized license conditions.
        +const AllLicenseConditions = LicenseConditionSet(LicenseConditionMask)
        +
        +// NewLicenseConditionSet returns a set containing exactly the elements of
        +// `conditions`.
        +func NewLicenseConditionSet(conditions ...LicenseCondition) LicenseConditionSet {
        +	cs := LicenseConditionSet(0x00)
        +	for _, lc := range conditions {
        +		cs |= LicenseConditionSet(lc)
        +	}
        +	return cs
        +}
        +
        +// Plus returns a new set containing all of the elements of `cs` and all of the
        +// `conditions`.
        +func (cs LicenseConditionSet) Plus(conditions ...LicenseCondition) LicenseConditionSet {
        +	result := cs
        +	for _, lc := range conditions {
        +		result |= LicenseConditionSet(lc)
        +	}
        +	return result
        +}
        +
        +// Union returns a new set containing all of the elements of `cs` and all of the
        +// elements of the `other` sets.
        +func (cs LicenseConditionSet) Union(other ...LicenseConditionSet) LicenseConditionSet {
        +	result := cs
        +	for _, ls := range other {
        +		result |= ls
        +	}
        +	return result
        +}
        +
        +// MatchingAny returns the subset of `cs` equal to any of the `conditions`.
        +func (cs LicenseConditionSet) MatchingAny(conditions ...LicenseCondition) LicenseConditionSet {
        +	result := LicenseConditionSet(0x00)
        +	for _, lc := range conditions {
        +		result |= cs & LicenseConditionSet(lc)
        +	}
        +	return result
        +}
        +
        +// MatchingAnySet returns the subset of `cs` that are members of any of the
        +// `other` sets.
        +func (cs LicenseConditionSet) MatchingAnySet(other ...LicenseConditionSet) LicenseConditionSet {
        +	result := LicenseConditionSet(0x00)
        +	for _, ls := range other {
        +		result |= cs & ls
        +	}
        +	return result
        +}
        +
        +// HasAny returns true when `cs` contains at least one of the `conditions`.
        +func (cs LicenseConditionSet) HasAny(conditions ...LicenseCondition) bool {
        +	for _, lc := range conditions {
        +		if 0x0000 != (cs & LicenseConditionSet(lc)) {
        +			return true
        +		}
        +	}
        +	return false
        +}
        +
        +// MatchesAnySet returns true when `cs` has a non-empty intersection with at
        +// least one of the `other` condition sets.
        +func (cs LicenseConditionSet) MatchesAnySet(other ...LicenseConditionSet) bool {
        +	for _, ls := range other {
        +		if 0x0000 != (cs & ls) {
        +			return true
        +		}
        +	}
        +	return false
        +}
        +
        +// HasAll returns true when `cs` contains every one of the `conditions`.
        +func (cs LicenseConditionSet) HasAll(conditions ...LicenseCondition) bool {
        +	for _, lc := range conditions {
        +		if 0x0000 == (cs & LicenseConditionSet(lc)) {
        +			return false
        +		}
        +	}
        +	return true
        +}
        +
        +// MatchesEverySet returns true when `cs` has a non-empty intersection with
        +// each of the `other` condition sets.
        +func (cs LicenseConditionSet) MatchesEverySet(other ...LicenseConditionSet) bool {
        +	for _, ls := range other {
        +		if 0x0000 == (cs & ls) {
        +			return false
        +		}
        +	}
        +	return true
        +}
        +
        +// Intersection returns the subset of `cs` that are members of every `other`
        +// set.
        +func (cs LicenseConditionSet) Intersection(other ...LicenseConditionSet) LicenseConditionSet {
        +	result := cs
        +	for _, ls := range other {
        +		result &= ls
        +	}
        +	return result
        +}
        +
        +// Minus returns the subset of `cs` that are not equaal to any `conditions`.
        +func (cs LicenseConditionSet) Minus(conditions ...LicenseCondition) LicenseConditionSet {
        +	result := cs
        +	for _, lc := range conditions {
        +		result &^= LicenseConditionSet(lc)
        +	}
        +	return result
        +}
        +
        +// Difference returns the subset of `cs` that are not members of any `other`
        +// set.
        +func (cs LicenseConditionSet) Difference(other ...LicenseConditionSet) LicenseConditionSet {
        +	result := cs
        +	for _, ls := range other {
        +		result &^= ls
        +	}
        +	return result
        +}
        +
        +// Len returns the number of license conditions in the set.
        +func (cs LicenseConditionSet) Len() int {
        +	size := 0
        +	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
        +		if 0x00 != (cs & lc) {
        +			size++
        +		}
        +	}
        +	return size
        +}
        +
        +// AsList returns an array of the license conditions in the set.
        +func (cs LicenseConditionSet) AsList() []LicenseCondition {
        +	result := make([]LicenseCondition, 0, cs.Len())
        +	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
        +		if 0x00 != (cs & lc) {
        +			result = append(result, LicenseCondition(lc))
        +		}
        +	}
        +	return result
        +}
        +
        +// Names returns an array of the names of the license conditions in the set.
        +func (cs LicenseConditionSet) Names() []string {
        +	result := make([]string, 0, cs.Len())
        +	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
        +		if 0x00 != (cs & lc) {
        +			result = append(result, LicenseCondition(lc).Name())
        +		}
        +	}
        +	return result
        +}
        +
        +// IsEmpty returns true when the set contains no license conditions.
        +func (cs LicenseConditionSet) IsEmpty() bool {
        +	return 0x00 == (cs & AllLicenseConditions)
        +}
        +
        +// String returns a human-readable string representation of the set.
        +func (cs LicenseConditionSet) String() string {
        +	return fmt.Sprintf("{%s}", strings.Join(cs.Names(), "|"))
        +}
        diff --git a/make/tools/compliance/conditionset_test.go b/make/tools/compliance/conditionset_test.go
        new file mode 100644
        index 0000000..c91912f
        --- /dev/null
        +++ b/make/tools/compliance/conditionset_test.go
        @@ -0,0 +1,657 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"strings"
        +	"testing"
        +)
        +
        +func TestConditionSet(t *testing.T) {
        +	tests := []struct {
        +		name        string
        +		conditions  []string
        +		plus        *[]string
        +		minus       *[]string
        +		matchingAny map[string][]string
        +		expected    []string
        +	}{
        +		{
        +			name:       "empty",
        +			conditions: []string{},
        +			plus:       &[]string{},
        +			matchingAny: map[string][]string{
        +				"notice":                []string{},
        +				"restricted":            []string{},
        +				"restricted|reciprocal": []string{},
        +			},
        +			expected: []string{},
        +		},
        +		{
        +			name:       "emptyminusnothing",
        +			conditions: []string{},
        +			minus:      &[]string{},
        +			matchingAny: map[string][]string{
        +				"notice":                []string{},
        +				"restricted":            []string{},
        +				"restricted|reciprocal": []string{},
        +			},
        +			expected: []string{},
        +		},
        +		{
        +			name:       "emptyminusnotice",
        +			conditions: []string{},
        +			minus:      &[]string{"notice"},
        +			matchingAny: map[string][]string{
        +				"notice":                []string{},
        +				"restricted":            []string{},
        +				"restricted|reciprocal": []string{},
        +			},
        +			expected: []string{},
        +		},
        +		{
        +			name:       "noticeonly",
        +			conditions: []string{"notice"},
        +			matchingAny: map[string][]string{
        +				"notice":             []string{"notice"},
        +				"notice|proprietary": []string{"notice"},
        +				"restricted":         []string{},
        +			},
        +			expected: []string{"notice"},
        +		},
        +		{
        +			name:       "allnoticeonly",
        +			conditions: []string{"notice"},
        +			plus:       &[]string{"notice"},
        +			matchingAny: map[string][]string{
        +				"notice":             []string{"notice"},
        +				"notice|proprietary": []string{"notice"},
        +				"restricted":         []string{},
        +			},
        +			expected: []string{"notice"},
        +		},
        +		{
        +			name:       "emptyplusnotice",
        +			conditions: []string{},
        +			plus:       &[]string{"notice"},
        +			matchingAny: map[string][]string{
        +				"notice":             []string{"notice"},
        +				"notice|proprietary": []string{"notice"},
        +				"restricted":         []string{},
        +			},
        +			expected: []string{"notice"},
        +		},
        +		{
        +			name:       "everything",
        +			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
        +			plus:       &[]string{"restricted_with_classpath_exception", "restricted_allows_dynamic_linking", "by_exception_only", "not_allowed"},
        +			matchingAny: map[string][]string{
        +				"unencumbered":                        []string{"unencumbered"},
        +				"permissive":                          []string{"permissive"},
        +				"notice":                              []string{"notice"},
        +				"reciprocal":                          []string{"reciprocal"},
        +				"restricted":                          []string{"restricted"},
        +				"restricted_with_classpath_exception": []string{"restricted_with_classpath_exception"},
        +				"restricted_allows_dynamic_linking":   []string{"restricted_allows_dynamic_linking"},
        +				"proprietary":                         []string{"proprietary"},
        +				"by_exception_only":                   []string{"by_exception_only"},
        +				"not_allowed":                         []string{"not_allowed"},
        +				"notice|proprietary":                  []string{"notice", "proprietary"},
        +			},
        +			expected: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +		},
        +		{
        +			name: "everythingplusminusnothing",
        +			conditions: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +			plus:  &[]string{},
        +			minus: &[]string{},
        +			matchingAny: map[string][]string{
        +				"unencumbered|permissive|notice": []string{"unencumbered", "permissive", "notice"},
        +				"restricted|reciprocal":          []string{"reciprocal", "restricted"},
        +				"proprietary|by_exception_only":  []string{"proprietary", "by_exception_only"},
        +				"not_allowed":                    []string{"not_allowed"},
        +			},
        +			expected: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +		},
        +		{
        +			name:       "allbutone",
        +			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
        +			plus:       &[]string{"restricted_allows_dynamic_linking", "by_exception_only", "not_allowed"},
        +			matchingAny: map[string][]string{
        +				"unencumbered":                        []string{"unencumbered"},
        +				"permissive":                          []string{"permissive"},
        +				"notice":                              []string{"notice"},
        +				"reciprocal":                          []string{"reciprocal"},
        +				"restricted":                          []string{"restricted"},
        +				"restricted_with_classpath_exception": []string{},
        +				"restricted_allows_dynamic_linking":   []string{"restricted_allows_dynamic_linking"},
        +				"proprietary":                         []string{"proprietary"},
        +				"by_exception_only":                   []string{"by_exception_only"},
        +				"not_allowed":                         []string{"not_allowed"},
        +				"notice|proprietary":                  []string{"notice", "proprietary"},
        +			},
        +			expected: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +		},
        +		{
        +			name: "everythingminusone",
        +			conditions: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +			minus: &[]string{"restricted_allows_dynamic_linking"},
        +			matchingAny: map[string][]string{
        +				"unencumbered":                        []string{"unencumbered"},
        +				"permissive":                          []string{"permissive"},
        +				"notice":                              []string{"notice"},
        +				"reciprocal":                          []string{"reciprocal"},
        +				"restricted":                          []string{"restricted"},
        +				"restricted_with_classpath_exception": []string{"restricted_with_classpath_exception"},
        +				"restricted_allows_dynamic_linking":   []string{},
        +				"proprietary":                         []string{"proprietary"},
        +				"by_exception_only":                   []string{"by_exception_only"},
        +				"not_allowed":                         []string{"not_allowed"},
        +				"restricted|proprietary":              []string{"restricted", "proprietary"},
        +			},
        +			expected: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +		},
        +		{
        +			name: "everythingminuseverything",
        +			conditions: []string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +			minus: &[]string{
        +				"unencumbered",
        +				"permissive",
        +				"notice",
        +				"reciprocal",
        +				"restricted",
        +				"restricted_with_classpath_exception",
        +				"restricted_allows_dynamic_linking",
        +				"proprietary",
        +				"by_exception_only",
        +				"not_allowed",
        +			},
        +			matchingAny: map[string][]string{
        +				"unencumbered":                        []string{},
        +				"permissive":                          []string{},
        +				"notice":                              []string{},
        +				"reciprocal":                          []string{},
        +				"restricted":                          []string{},
        +				"restricted_with_classpath_exception": []string{},
        +				"restricted_allows_dynamic_linking":   []string{},
        +				"proprietary":                         []string{},
        +				"by_exception_only":                   []string{},
        +				"not_allowed":                         []string{},
        +				"restricted|proprietary":              []string{},
        +			},
        +			expected: []string{},
        +		},
        +		{
        +			name:       "restrictedplus",
        +			conditions: []string{"restricted", "restricted_with_classpath_exception", "restricted_allows_dynamic_linking"},
        +			plus:       &[]string{"permissive", "notice", "restricted", "proprietary"},
        +			matchingAny: map[string][]string{
        +				"unencumbered":                        []string{},
        +				"permissive":                          []string{"permissive"},
        +				"notice":                              []string{"notice"},
        +				"restricted":                          []string{"restricted"},
        +				"restricted_with_classpath_exception": []string{"restricted_with_classpath_exception"},
        +				"restricted_allows_dynamic_linking":   []string{"restricted_allows_dynamic_linking"},
        +				"proprietary":                         []string{"proprietary"},
        +				"restricted|proprietary":              []string{"restricted", "proprietary"},
        +				"by_exception_only":                   []string{},
        +				"proprietary|by_exception_only":       []string{"proprietary"},
        +			},
        +			expected: []string{"permissive", "notice", "restricted", "restricted_with_classpath_exception", "restricted_allows_dynamic_linking", "proprietary"},
        +		},
        +	}
        +	for _, tt := range tests {
        +		toConditions := func(names []string) []LicenseCondition {
        +			result := make([]LicenseCondition, 0, len(names))
        +			for _, name := range names {
        +				result = append(result, RecognizedConditionNames[name])
        +			}
        +			return result
        +		}
        +		populate := func() LicenseConditionSet {
        +			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        +			if tt.plus != nil {
        +				testSet = testSet.Plus(toConditions(*tt.plus)...)
        +			}
        +			if tt.minus != nil {
        +				testSet = testSet.Minus(toConditions(*tt.minus)...)
        +			}
        +			return testSet
        +		}
        +		populateSet := func() LicenseConditionSet {
        +			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        +			if tt.plus != nil {
        +				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
        +			}
        +			if tt.minus != nil {
        +				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
        +			}
        +			return testSet
        +		}
        +		populatePlusSet := func() LicenseConditionSet {
        +			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        +			if tt.plus != nil {
        +				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
        +			}
        +			if tt.minus != nil {
        +				testSet = testSet.Minus(toConditions(*tt.minus)...)
        +			}
        +			return testSet
        +		}
        +		populateMinusSet := func() LicenseConditionSet {
        +			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        +			if tt.plus != nil {
        +				testSet = testSet.Plus(toConditions(*tt.plus)...)
        +			}
        +			if tt.minus != nil {
        +				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
        +			}
        +			return testSet
        +		}
        +		checkMatching := func(cs LicenseConditionSet, t *testing.T) {
        +			for data, expectedNames := range tt.matchingAny {
        +				expectedConditions := toConditions(expectedNames)
        +				expected := NewLicenseConditionSet(expectedConditions...)
        +				actual := cs.MatchingAny(toConditions(strings.Split(data, "|"))...)
        +				actualNames := actual.Names()
        +
        +				t.Logf("MatchingAny(%s): actual set %04x %s", data, actual, actual.String())
        +				t.Logf("MatchingAny(%s): expected set %04x %s", data, expected, expected.String())
        +
        +				if actual != expected {
        +					t.Errorf("MatchingAny(%s): got %04x, want %04x", data, actual, expected)
        +					continue
        +				}
        +				if len(actualNames) != len(expectedNames) {
        +					t.Errorf("len(MatchinAny(%s).Names()): got %d, want %d",
        +						data, len(actualNames), len(expectedNames))
        +				} else {
        +					for i := 0; i < len(actualNames); i++ {
        +						if actualNames[i] != expectedNames[i] {
        +							t.Errorf("MatchingAny(%s).Names()[%d]: got %s, want %s",
        +								data, i, actualNames[i], expectedNames[i])
        +							break
        +						}
        +					}
        +				}
        +				actualConditions := actual.AsList()
        +				if len(actualConditions) != len(expectedConditions) {
        +					t.Errorf("len(MatchingAny(%s).AsList()):  got %d, want %d",
        +						data, len(actualNames), len(expectedNames))
        +				} else {
        +					for i := 0; i < len(actualNames); i++ {
        +						if actualNames[i] != expectedNames[i] {
        +							t.Errorf("MatchingAny(%s).AsList()[%d]: got %s, want %s",
        +								data, i, actualNames[i], expectedNames[i])
        +							break
        +						}
        +					}
        +				}
        +			}
        +		}
        +		checkMatchingSet := func(cs LicenseConditionSet, t *testing.T) {
        +			for data, expectedNames := range tt.matchingAny {
        +				expected := NewLicenseConditionSet(toConditions(expectedNames)...)
        +				actual := cs.MatchingAnySet(NewLicenseConditionSet(toConditions(strings.Split(data, "|"))...))
        +				actualNames := actual.Names()
        +
        +				t.Logf("MatchingAnySet(%s): actual set %04x %s", data, actual, actual.String())
        +				t.Logf("MatchingAnySet(%s): expected set %04x %s", data, expected, expected.String())
        +
        +				if actual != expected {
        +					t.Errorf("MatchingAnySet(%s): got %04x, want %04x", data, actual, expected)
        +					continue
        +				}
        +				if len(actualNames) != len(expectedNames) {
        +					t.Errorf("len(MatchingAnySet(%s).Names()): got %d, want %d",
        +						data, len(actualNames), len(expectedNames))
        +				} else {
        +					for i := 0; i < len(actualNames); i++ {
        +						if actualNames[i] != expectedNames[i] {
        +							t.Errorf("MatchingAnySet(%s).Names()[%d]: got %s, want %s",
        +								data, i, actualNames[i], expectedNames[i])
        +							break
        +						}
        +					}
        +				}
        +				expectedConditions := toConditions(expectedNames)
        +				actualConditions := actual.AsList()
        +				if len(actualConditions) != len(expectedConditions) {
        +					t.Errorf("len(MatchingAnySet(%s).AsList()): got %d, want %d",
        +						data, len(actualNames), len(expectedNames))
        +				} else {
        +					for i := 0; i < len(actualNames); i++ {
        +						if actualNames[i] != expectedNames[i] {
        +							t.Errorf("MatchingAnySet(%s).AsList()[%d]: got %s, want %s",
        +								data, i, actualNames[i], expectedNames[i])
        +							break
        +						}
        +					}
        +				}
        +			}
        +		}
        +
        +		checkExpected := func(actual LicenseConditionSet, t *testing.T) bool {
        +			t.Logf("checkExpected{%s}", strings.Join(tt.expected, ", "))
        +
        +			expectedConditions := toConditions(tt.expected)
        +			expected := NewLicenseConditionSet(expectedConditions...)
        +
        +			actualNames := actual.Names()
        +
        +			t.Logf("actual license condition set: %04x %s", actual, actual.String())
        +			t.Logf("expected license condition set: %04x %s", expected, expected.String())
        +
        +			if actual != expected {
        +				t.Errorf("checkExpected: got %04x, want %04x", actual, expected)
        +				return false
        +			}
        +
        +			if len(actualNames) != len(tt.expected) {
        +				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
        +			} else {
        +				for i := 0; i < len(actualNames); i++ {
        +					if actualNames[i] != tt.expected[i] {
        +						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
        +						break
        +					}
        +				}
        +			}
        +
        +			actualConditions := actual.AsList()
        +			if len(actualConditions) != len(expectedConditions) {
        +				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
        +			} else {
        +				for i := 0; i < len(actualConditions); i++ {
        +					if actualConditions[i] != expectedConditions[i] {
        +						t.Errorf("actual.AsList()[%d]: got %s, want %s",
        +							i, actualConditions[i].Name(), expectedConditions[i].Name())
        +						break
        +					}
        +				}
        +			}
        +
        +			if len(tt.expected) == 0 {
        +				if !actual.IsEmpty() {
        +					t.Errorf("actual.IsEmpty(): got false, want true")
        +				}
        +				if actual.HasAny(expectedConditions...) {
        +					t.Errorf("actual.HasAny(): got true, want false")
        +				}
        +			} else {
        +				if actual.IsEmpty() {
        +					t.Errorf("actual.IsEmpty(): got true, want false")
        +				}
        +				if !actual.HasAny(expectedConditions...) {
        +					t.Errorf("actual.HasAny(all expected): got false, want true")
        +				}
        +			}
        +			if !actual.HasAll(expectedConditions...) {
        +				t.Errorf("actual.Hasll(all expected): want true, got false")
        +			}
        +			for _, expectedCondition := range expectedConditions {
        +				if !actual.HasAny(expectedCondition) {
        +					t.Errorf("actual.HasAny(%q): got false, want true", expectedCondition.Name())
        +				}
        +				if !actual.HasAll(expectedCondition) {
        +					t.Errorf("actual.HasAll(%q): got false, want true", expectedCondition.Name())
        +				}
        +			}
        +
        +			notExpected := (AllLicenseConditions &^ expected)
        +			notExpectedList := notExpected.AsList()
        +			t.Logf("not expected license condition set: %04x %s", notExpected, notExpected.String())
        +
        +			if len(tt.expected) == 0 {
        +				if actual.HasAny(append(expectedConditions, notExpectedList...)...) {
        +					t.Errorf("actual.HasAny(all conditions): want false, got true")
        +				}
        +			} else {
        +				if !actual.HasAny(append(expectedConditions, notExpectedList...)...) {
        +					t.Errorf("actual.HasAny(all conditions): want true, got false")
        +				}
        +			}
        +			if len(notExpectedList) == 0 {
        +				if !actual.HasAll(append(expectedConditions, notExpectedList...)...) {
        +					t.Errorf("actual.HasAll(all conditions): want true, got false")
        +				}
        +			} else {
        +				if actual.HasAll(append(expectedConditions, notExpectedList...)...) {
        +					t.Errorf("actual.HasAll(all conditions): want false, got true")
        +				}
        +			}
        +			for _, unexpectedCondition := range notExpectedList {
        +				if actual.HasAny(unexpectedCondition) {
        +					t.Errorf("actual.HasAny(%q): got true, want false", unexpectedCondition.Name())
        +				}
        +				if actual.HasAll(unexpectedCondition) {
        +					t.Errorf("actual.HasAll(%q): got true, want false", unexpectedCondition.Name())
        +				}
        +			}
        +			return true
        +		}
        +
        +		checkExpectedSet := func(actual LicenseConditionSet, t *testing.T) bool {
        +			t.Logf("checkExpectedSet{%s}", strings.Join(tt.expected, ", "))
        +
        +			expectedConditions := toConditions(tt.expected)
        +			expected := NewLicenseConditionSet(expectedConditions...)
        +
        +			actualNames := actual.Names()
        +
        +			t.Logf("actual license condition set: %04x %s", actual, actual.String())
        +			t.Logf("expected license condition set: %04x %s", expected, expected.String())
        +
        +			if actual != expected {
        +				t.Errorf("checkExpectedSet: got %04x, want %04x", actual, expected)
        +				return false
        +			}
        +
        +			if len(actualNames) != len(tt.expected) {
        +				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
        +			} else {
        +				for i := 0; i < len(actualNames); i++ {
        +					if actualNames[i] != tt.expected[i] {
        +						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
        +						break
        +					}
        +				}
        +			}
        +
        +			actualConditions := actual.AsList()
        +			if len(actualConditions) != len(expectedConditions) {
        +				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
        +			} else {
        +				for i := 0; i < len(actualConditions); i++ {
        +					if actualConditions[i] != expectedConditions[i] {
        +						t.Errorf("actual.AsList()[%d}: got %s, want %s",
        +							i, actualConditions[i].Name(), expectedConditions[i].Name())
        +						break
        +					}
        +				}
        +			}
        +
        +			if len(tt.expected) == 0 {
        +				if !actual.IsEmpty() {
        +					t.Errorf("actual.IsEmpty(): got false, want true")
        +				}
        +				if actual.MatchesAnySet(expected) {
        +					t.Errorf("actual.MatchesAnySet({}): got true, want false")
        +				}
        +				if actual.MatchesEverySet(expected, expected) {
        +					t.Errorf("actual.MatchesEverySet({}, {}): want false, got true")
        +				}
        +			} else {
        +				if actual.IsEmpty() {
        +					t.Errorf("actual.IsEmpty(): got true, want false")
        +				}
        +				if !actual.MatchesAnySet(expected) {
        +					t.Errorf("actual.MatchesAnySet({all expected}): want true, got false")
        +				}
        +				if !actual.MatchesEverySet(expected, expected) {
        +					t.Errorf("actual.MatchesEverySet({all expected}, {all expected}): want true, got false")
        +				}
        +			}
        +
        +			notExpected := (AllLicenseConditions &^ expected)
        +			t.Logf("not expected license condition set: %04x %s", notExpected, notExpected.String())
        +
        +			if len(tt.expected) == 0 {
        +				if actual.MatchesAnySet(expected, notExpected) {
        +					t.Errorf("empty actual.MatchesAnySet({expected}, {not expected}): want false, got true")
        +				}
        +			} else {
        +				if !actual.MatchesAnySet(expected, notExpected) {
        +					t.Errorf("actual.MatchesAnySet({expected}, {not expected}): want true, got false")
        +				}
        +			}
        +			if actual.MatchesAnySet(notExpected) {
        +				t.Errorf("actual.MatchesAnySet({not expected}): want false, got true")
        +			}
        +			if actual.MatchesEverySet(notExpected) {
        +				t.Errorf("actual.MatchesEverySet({not expected}): want false, got true")
        +			}
        +			if actual.MatchesEverySet(expected, notExpected) {
        +				t.Errorf("actual.MatchesEverySet({expected}, {not expected}): want false, got true")
        +			}
        +
        +			if !actual.Difference(expected).IsEmpty() {
        +				t.Errorf("actual.Difference({expected}).IsEmpty(): want true, got false")
        +			}
        +			if expected != actual.Intersection(expected) {
        +				t.Errorf("expected == actual.Intersection({expected}): want true, got false (%04x != %04x)", expected, actual.Intersection(expected))
        +			}
        +			if actual != actual.Intersection(expected) {
        +				t.Errorf("actual == actual.Intersection({expected}): want true, got false (%04x != %04x)", actual, actual.Intersection(expected))
        +			}
        +			return true
        +		}
        +
        +		t.Run(tt.name, func(t *testing.T) {
        +			cs := populate()
        +			if checkExpected(cs, t) {
        +				checkMatching(cs, t)
        +			}
        +			if checkExpectedSet(cs, t) {
        +				checkMatchingSet(cs, t)
        +			}
        +		})
        +
        +		t.Run(tt.name+"_sets", func(t *testing.T) {
        +			cs := populateSet()
        +			if checkExpected(cs, t) {
        +				checkMatching(cs, t)
        +			}
        +			if checkExpectedSet(cs, t) {
        +				checkMatchingSet(cs, t)
        +			}
        +		})
        +
        +		t.Run(tt.name+"_plusset", func(t *testing.T) {
        +			cs := populatePlusSet()
        +			if checkExpected(cs, t) {
        +				checkMatching(cs, t)
        +			}
        +			if checkExpectedSet(cs, t) {
        +				checkMatchingSet(cs, t)
        +			}
        +		})
        +
        +		t.Run(tt.name+"_minusset", func(t *testing.T) {
        +			cs := populateMinusSet()
        +			if checkExpected(cs, t) {
        +				checkMatching(cs, t)
        +			}
        +			if checkExpectedSet(cs, t) {
        +				checkMatchingSet(cs, t)
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/doc.go b/make/tools/compliance/doc.go
        new file mode 100644
        index 0000000..a47c1cf
        --- /dev/null
        +++ b/make/tools/compliance/doc.go
        @@ -0,0 +1,77 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +/*
        +
        +Package compliance provides an approved means for reading, consuming, and
        +analyzing license metadata graphs.
        +
        +Assuming the license metadata and dependencies are fully and accurately
        +recorded in the build system, any discrepancy between the official policy for
        +open source license compliance and this code is a bug in this code.
        +
        +A few principal types to understand are LicenseGraph, LicenseCondition, and
        +ResolutionSet.
        +
        +LicenseGraph
        +------------
        +
        +A LicenseGraph is an immutable graph of the targets and dependencies reachable
        +from a specific set of root targets. In general, the root targets will be the
        +artifacts in a release or distribution. While conceptually immutable, parts of
        +the graph may be loaded or evaluated lazily.
        +
        +LicenseCondition
        +----------------
        +
        +A LicenseCondition is an immutable tuple pairing a condition name with an
        +originating target. e.g. Per current policy, a static library licensed under an
        +MIT license would pair a "notice" condition with the static library target, and
        +a dynamic license licensed under GPL would pair a "restricted" condition with
        +the dynamic library target.
        +
        +ResolutionSet
        +-------------
        +
        +A ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`
        +tuples describing how license conditions apply to targets.
        +
        +`AttachesTo` is the trigger for acting. Distribution of the target invokes
        +the policy.
        +
        +`ActsOn` is the target to share, give notice for, hide etc.
        +
        +`Resolves` is the license condition that the action resolves.
        +
        +Remember: Each license condition pairs a condition name with an originating
        +target so each resolution in a ResolutionSet has two targets it applies to and
        +one target from which it originates, all of which may be the same target.
        +
        +For most condition types, `ActsOn` and `Resolves.Origin` will be the same
        +target. For example, a notice condition policy means attribution or notice must
        +be given for the target where the condition originates. Likewise, a proprietary
        +condition policy means the privacy of the target where the condition originates
        +must be respected. i.e. The thing acted on is the origin.
        +
        +Restricted conditions are different. The infectious nature of restricted often
        +means sharing code that is not the target where the restricted condition
        +originates. Linking an MIT library to a GPL library implies a policy to share
        +the MIT library despite the MIT license having no source sharing requirement.
        +
        +In this case, one or more resolution tuples will have the MIT license module in
        +`ActsOn` and the restricted condition originating at the GPL library module in
        +`Resolves`. These tuples will `AttachTo` every target that depends on the GPL
        +library because shipping any of those targets trigger the policy to share the
        +code.
        +*/
        +package compliance
        diff --git a/make/tools/compliance/go.mod b/make/tools/compliance/go.mod
        new file mode 100644
        index 0000000..61e2158
        --- /dev/null
        +++ b/make/tools/compliance/go.mod
        @@ -0,0 +1,18 @@
        +module android/soong/tools/compliance
        +
        +require google.golang.org/protobuf v0.0.0
        +
        +replace google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf
        +
        +require android/soong v0.0.0
        +
        +replace android/soong v0.0.0 => ../../../soong									      
        +// Indirect deps from golang-protobuf
        +exclude github.com/golang/protobuf v1.5.0
        +
        +replace github.com/google/go-cmp v0.5.5 => ../../../../external/go-cmp
        +
        +// Indirect dep from go-cmp
        +exclude golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
        +
        +go 1.18
        diff --git a/make/tools/compliance/graph.go b/make/tools/compliance/graph.go
        new file mode 100644
        index 0000000..e73ab46
        --- /dev/null
        +++ b/make/tools/compliance/graph.go
        @@ -0,0 +1,528 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +	"sort"
        +	"strings"
        +	"sync"
        +)
        +
        +// LicenseGraph describes the immutable license metadata for a set of root
        +// targets and the transitive closure of their dependencies.
        +//
        +// Alternatively, a graph is a set of edges. In this case directed, annotated
        +// edges from targets to dependencies.
        +//
        +// A LicenseGraph provides the frame of reference for all of the other types
        +// defined here. It is possible to have multiple graphs, and to have targets,
        +// edges, and resolutions from multiple graphs. But it is an error to try to
        +// mix items from different graphs in the same operation.
        +// May panic if attempted.
        +//
        +// The compliance package assumes specific private implementations of each of
        +// these interfaces. May panic if attempts are made to combine different
        +// implementations of some interfaces with expected implementations of other
        +// interfaces here.
        +type LicenseGraph struct {
        +	// rootFiles identifies the original set of files to read. (immutable)
        +	//
        +	// Defines the starting "top" for top-down walks.
        +	//
        +	// Alternatively, an instance of licenseGraphImp conceptually defines a scope within
        +	// the universe of build graphs as a sub-graph rooted at rootFiles where all edges
        +	// and targets for the instance are defined relative to and within that scope. For
        +	// most analyses, the correct scope is to root the graph at all of the distributed
        +	// artifacts.
        +	rootFiles []string
        +
        +	// edges lists the directed edges in the graph from target to dependency. (guarded by mu)
        +	//
        +	// Alternatively, the graph is the set of `edges`.
        +	edges TargetEdgeList
        +
        +	// targets identifies, indexes, and describes the entire set of target node files.
        +	/// (guarded by mu)
        +	targets map[string]*TargetNode
        +
        +	// 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
        +
        +	// 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
        +
        +	// shippedNodes caches the results of a full walk of nodes identifying targets
        +	// distributed either directly or as derivative works. (creation guarded by mu)
        +	shippedNodes *TargetNodeSet
        +
        +	// mu guards against concurrent update.
        +	mu sync.Mutex
        +}
        +
        +// Edges returns the list of edges in the graph. (unordered)
        +func (lg *LicenseGraph) Edges() TargetEdgeList {
        +	edges := make(TargetEdgeList, 0, len(lg.edges))
        +	edges = append(edges, lg.edges...)
        +	return edges
        +}
        +
        +// Targets returns the list of target nodes in the graph. (unordered)
        +func (lg *LicenseGraph) Targets() TargetNodeList {
        +	targets := make(TargetNodeList, 0, len(lg.targets))
        +	for _, target := range lg.targets {
        +		targets = append(targets, target)
        +	}
        +	return targets
        +}
        +
        +// compliance-only LicenseGraph methods
        +
        +// newLicenseGraph constructs a new, empty instance of LicenseGraph.
        +func newLicenseGraph() *LicenseGraph {
        +	return &LicenseGraph{
        +		rootFiles: []string{},
        +		targets:   make(map[string]*TargetNode),
        +	}
        +}
        +
        +// TargetEdge describes a directed, annotated edge from a target to a
        +// dependency. (immutable)
        +//
        +// A LicenseGraph, above, is a set of TargetEdges.
        +//
        +// i.e. `Target` depends on `Dependency` in the manner described by
        +// `Annotations`.
        +type TargetEdge struct {
        +	// target and dependency identify the nodes connected by the edge.
        +	target, dependency *TargetNode
        +
        +	// annotations identifies the set of compliance-relevant annotations describing the edge.
        +	annotations TargetEdgeAnnotations
        +}
        +
        +// Target identifies the target that depends on the dependency.
        +//
        +// Target needs Dependency to build.
        +func (e *TargetEdge) Target() *TargetNode {
        +	return e.target
        +}
        +
        +// Dependency identifies the target depended on by the target.
        +//
        +// Dependency builds without Target, but Target needs Dependency to build.
        +func (e *TargetEdge) Dependency() *TargetNode {
        +	return e.dependency
        +}
        +
        +// Annotations describes the type of edge by the set of annotations attached to
        +// it.
        +//
        +// Only annotations prescribed by policy have any meaning for licensing, and
        +// the meaning for licensing is likewise prescribed by policy. Other annotations
        +// are preserved and ignored by policy.
        +func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
        +	return e.annotations
        +}
        +
        +// String returns a human-readable string representation of the edge.
        +func (e *TargetEdge) String() string {
        +	return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
        +}
        +
        +// TargetEdgeList orders lists of edges by target then dependency then annotations.
        +type TargetEdgeList []*TargetEdge
        +
        +// Len returns the count of the elmements in the list.
        +func (l TargetEdgeList) Len() int { return len(l) }
        +
        +// Swap rearranges 2 elements so that each occupies the other's former position.
        +func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +
        +// Less returns true when the `i`th element is lexicographically less than the `j`th.
        +func (l TargetEdgeList) Less(i, j int) bool {
        +	namei := l[i].target.name
        +	namej := l[j].target.name
        +	if namei == namej {
        +		namei = l[i].dependency.name
        +		namej = l[j].dependency.name
        +	}
        +	if namei == namej {
        +		return l[i].annotations.Compare(l[j].annotations) < 0
        +	}
        +	return namei < namej
        +}
        +
        +// TargetEdgePathSegment describes a single arc in a TargetPath associating the
        +// edge with a context `ctx` defined by whatever process is creating the path.
        +type TargetEdgePathSegment struct {
        +	edge *TargetEdge
        +	ctx  interface{}
        +}
        +
        +// Target identifies the target that depends on the dependency.
        +//
        +// Target needs Dependency to build.
        +func (s TargetEdgePathSegment) Target() *TargetNode {
        +	return s.edge.target
        +}
        +
        +// Dependency identifies the target depended on by the target.
        +//
        +// Dependency builds without Target, but Target needs Dependency to build.
        +func (s TargetEdgePathSegment) Dependency() *TargetNode {
        +	return s.edge.dependency
        +}
        +
        +// Annotations describes the type of edge by the set of annotations attached to
        +// it.
        +//
        +// Only annotations prescribed by policy have any meaning for licensing, and
        +// the meaning for licensing is likewise prescribed by policy. Other annotations
        +// are preserved and ignored by policy.
        +func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
        +	return s.edge.annotations
        +}
        +
        +// Context returns the context associated with the path segment. The type and
        +// value of the context defined by the process creating the path.
        +func (s TargetEdgePathSegment) Context() interface{} {
        +	return s.ctx
        +}
        +
        +// String returns a human-readable string representation of the edge.
        +func (s TargetEdgePathSegment) String() string {
        +	return fmt.Sprintf("%s -[%s]> %s", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), ", "), s.edge.dependency.name)
        +}
        +
        +// TargetEdgePath describes a sequence of edges starting at a root and ending
        +// at some final dependency.
        +type TargetEdgePath []TargetEdgePathSegment
        +
        +// NewTargetEdgePath creates a new, empty path with capacity `cap`.
        +func NewTargetEdgePath(cap int) *TargetEdgePath {
        +	p := make(TargetEdgePath, 0, cap)
        +	return &p
        +}
        +
        +// Push appends a new edge to the list verifying that the target of the new
        +// edge is the dependency of the prior.
        +func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
        +	if len(*p) == 0 {
        +		*p = append(*p, TargetEdgePathSegment{edge, ctx})
        +		return
        +	}
        +	if (*p)[len(*p)-1].edge.dependency != edge.target {
        +		panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
        +	}
        +	*p = append(*p, TargetEdgePathSegment{edge, ctx})
        +}
        +
        +// Pop shortens the path by 1 edge.
        +func (p *TargetEdgePath) Pop() {
        +	if len(*p) == 0 {
        +		panic(fmt.Errorf("attempt to remove edge from empty path"))
        +	}
        +	*p = (*p)[:len(*p)-1]
        +}
        +
        +// Clear makes the path length 0.
        +func (p *TargetEdgePath) Clear() {
        +	*p = (*p)[:0]
        +}
        +
        +// Copy makes a new path with the same value.
        +func (p *TargetEdgePath) Copy() *TargetEdgePath {
        +	result := make(TargetEdgePath, 0, len(*p))
        +	for _, e := range *p {
        +		result = append(result, e)
        +	}
        +	return &result
        +}
        +
        +// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
        +func (p *TargetEdgePath) String() string {
        +	if p == nil {
        +		return "nil"
        +	}
        +	if len(*p) == 0 {
        +		return "[]"
        +	}
        +	var sb strings.Builder
        +	fmt.Fprintf(&sb, "[")
        +	for _, s := range *p {
        +		fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
        +	}
        +	lastSegment := (*p)[len(*p)-1]
        +	fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
        +	return sb.String()
        +}
        +
        +// TargetNode describes a module or target identified by the name of a specific
        +// metadata file. (immutable)
        +//
        +// Each metadata file corresponds to a Soong module or to a Make target.
        +//
        +// A target node can appear as the target or as the dependency in edges.
        +// Most target nodes appear as both target in one edge and as dependency in
        +// other edges.
        +type TargetNode targetNode
        +
        +// Name returns the string that identifies the target node.
        +// i.e. path to license metadata file
        +func (tn *TargetNode) Name() string {
        +	return tn.name
        +}
        +
        +// Dependencies returns the list of edges to dependencies of `tn`.
        +func (tn *TargetNode) Dependencies() TargetEdgeList {
        +	edges := make(TargetEdgeList, 0, len(tn.edges))
        +	edges = append(edges, tn.edges...)
        +	return edges
        +}
        +
        +// PackageName returns the string that identifes the package for the target.
        +func (tn *TargetNode) PackageName() string {
        +	return tn.proto.GetPackageName()
        +}
        +
        +// ModuleTypes returns the list of module types implementing the target.
        +// (unordered)
        +//
        +// In an ideal world, only 1 module type would implement each target, but the
        +// interactions between Soong and Make for host versus product and for a
        +// variety of architectures sometimes causes multiple module types per target
        +// (often a regular build target and a prebuilt.)
        +func (tn *TargetNode) ModuleTypes() []string {
        +	return append([]string{}, tn.proto.ModuleTypes...)
        +}
        +
        +// ModuleClasses returns the list of module classes implementing the target.
        +// (unordered)
        +func (tn *TargetNode) ModuleClasses() []string {
        +	return append([]string{}, tn.proto.ModuleClasses...)
        +}
        +
        +// Projects returns the projects defining the target node. (unordered)
        +//
        +// In an ideal world, only 1 project defines a target, but the interaction
        +// between Soong and Make for a variety of architectures and for host versus
        +// product means a module is sometimes defined more than once.
        +func (tn *TargetNode) Projects() []string {
        +	return append([]string{}, tn.proto.Projects...)
        +}
        +
        +// LicenseKinds returns the list of license kind names for the module or
        +// target. (unordered)
        +//
        +// e.g. SPDX-license-identifier-MIT or legacy_proprietary
        +func (tn *TargetNode) LicenseKinds() []string {
        +	return append([]string{}, tn.proto.LicenseKinds...)
        +}
        +
        +// LicenseConditions returns a copy of the set of license conditions
        +// originating at the target. The values that appear and how each is resolved
        +// is a matter of policy. (unordered)
        +//
        +// e.g. notice or proprietary
        +func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
        +	return tn.licenseConditions
        +}
        +
        +// LicenseTexts returns the paths to the files containing the license texts for
        +// the target. (unordered)
        +func (tn *TargetNode) LicenseTexts() []string {
        +	return append([]string{}, tn.proto.LicenseTexts...)
        +}
        +
        +// IsContainer returns true if the target represents a container that merely
        +// aggregates other targets.
        +func (tn *TargetNode) IsContainer() bool {
        +	return tn.proto.GetIsContainer()
        +}
        +
        +// Built returns the list of files built by the module or target. (unordered)
        +func (tn *TargetNode) Built() []string {
        +	return append([]string{}, tn.proto.Built...)
        +}
        +
        +// Installed returns the list of files installed by the module or target.
        +// (unordered)
        +func (tn *TargetNode) Installed() []string {
        +	return append([]string{}, tn.proto.Installed...)
        +}
        +
        +// TargetFiles returns the list of files built or installed by the module or
        +// target. (unordered)
        +func (tn *TargetNode) TargetFiles() []string {
        +	return append(tn.proto.Built, tn.proto.Installed...)
        +}
        +
        +// InstallMap returns the list of path name transformations to make to move
        +// files from their original location in the file system to their destination
        +// inside a container. (unordered)
        +func (tn *TargetNode) InstallMap() []InstallMap {
        +	result := make([]InstallMap, 0, len(tn.proto.InstallMap))
        +	for _, im := range tn.proto.InstallMap {
        +		result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
        +	}
        +	return result
        +}
        +
        +// Sources returns the list of file names depended on by the target, which may
        +// be a proper subset of those made available by dependency modules.
        +// (unordered)
        +func (tn *TargetNode) Sources() []string {
        +	return append([]string{}, tn.proto.Sources...)
        +}
        +
        +// InstallMap describes the mapping from an input filesystem file to file in a
        +// container.
        +type InstallMap struct {
        +	// FromPath is the input path on the filesystem.
        +	FromPath string
        +
        +	// ContainerPath is the path to the same file inside the container or
        +	// installed location.
        +	ContainerPath string
        +}
        +
        +// TargetEdgeAnnotations describes an immutable set of annotations attached to
        +// an edge from a target to a dependency.
        +//
        +// Annotations typically distinguish between static linkage versus dynamic
        +// versus tools that are used at build time but are not linked in any way.
        +type TargetEdgeAnnotations struct {
        +	annotations map[string]struct{}
        +}
        +
        +// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
        +func newEdgeAnnotations() TargetEdgeAnnotations {
        +	return TargetEdgeAnnotations{make(map[string]struct{})}
        +}
        +
        +// HasAnnotation returns true if an annotation `ann` is in the set.
        +func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
        +	_, ok := ea.annotations[ann]
        +	return ok
        +}
        +
        +// Compare orders TargetAnnotations returning:
        +// -1 when ea < other,
        +// +1 when ea > other, and
        +// 0 when ea == other.
        +func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
        +	a1 := ea.AsList()
        +	a2 := other.AsList()
        +	sort.Strings(a1)
        +	sort.Strings(a2)
        +	for k := 0; k < len(a1) && k < len(a2); k++ {
        +		if a1[k] < a2[k] {
        +			return -1
        +		}
        +		if a1[k] > a2[k] {
        +			return 1
        +		}
        +	}
        +	if len(a1) < len(a2) {
        +		return -1
        +	}
        +	if len(a1) > len(a2) {
        +		return 1
        +	}
        +	return 0
        +}
        +
        +// AsList returns the list of annotation names attached to the edge.
        +// (unordered)
        +func (ea TargetEdgeAnnotations) AsList() []string {
        +	l := make([]string, 0, len(ea.annotations))
        +	for ann := range ea.annotations {
        +		l = append(l, ann)
        +	}
        +	return l
        +}
        +
        +// TargetNodeSet describes a set of distinct nodes in a license graph.
        +type TargetNodeSet struct {
        +	nodes map[*TargetNode]struct{}
        +}
        +
        +// Contains returns true when `target` is an element of the set.
        +func (ts *TargetNodeSet) Contains(target *TargetNode) bool {
        +	_, isPresent := ts.nodes[target]
        +	return isPresent
        +}
        +
        +// AsList returns the list of target nodes in the set. (unordered)
        +func (ts *TargetNodeSet) AsList() TargetNodeList {
        +	result := make(TargetNodeList, 0, len(ts.nodes))
        +	for tn := range ts.nodes {
        +		result = append(result, tn)
        +	}
        +	return result
        +}
        +
        +// Names returns the array of target node namess in the set. (unordered)
        +func (ts *TargetNodeSet) Names() []string {
        +	result := make([]string, 0, len(ts.nodes))
        +	for tn := range ts.nodes {
        +		result = append(result, tn.name)
        +	}
        +	return result
        +}
        +
        +// String returns a human-readable string representation of the set.
        +func (ts *TargetNodeSet) String() string {
        +	return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
        +}
        +
        +// TargetNodeList orders a list of targets by name.
        +type TargetNodeList []*TargetNode
        +
        +// Len returns the count of elements in the list.
        +func (l TargetNodeList) Len() int { return len(l) }
        +
        +// Swap rearranges 2 elements so that each occupies the other's former position.
        +func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +
        +// Less returns true when the `i`th element is lexicographicallt less than the `j`th.
        +func (l TargetNodeList) Less(i, j int) bool {
        +	return l[i].name < l[j].name
        +}
        +
        +// String returns a string representation of the list.
        +func (l TargetNodeList) String() string {
        +	var sb strings.Builder
        +	fmt.Fprintf(&sb, "[")
        +	sep := ""
        +	for _, tn := range l {
        +		fmt.Fprintf(&sb, "%s%s", sep, tn.name)
        +		sep = " "
        +	}
        +	fmt.Fprintf(&sb, "]")
        +	return sb.String()
        +}
        +
        +// Names returns an array the names of the nodes in the same order as the nodes in the list.
        +func (l TargetNodeList) Names() []string {
        +	result := make([]string, 0, len(l))
        +	for _, tn := range l {
        +		result = append(result, tn.name)
        +	}
        +	return result
        +}
        diff --git a/make/tools/compliance/noticeindex.go b/make/tools/compliance/noticeindex.go
        new file mode 100644
        index 0000000..f082383
        --- /dev/null
        +++ b/make/tools/compliance/noticeindex.go
        @@ -0,0 +1,697 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bufio"
        +	"crypto/md5"
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"net/url"
        +	"path/filepath"
        +	"regexp"
        +	"sort"
        +	"strings"
        +)
        +
        +const (
        +	noProjectName = "\u2205"
        +)
        +
        +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?/`)
        +)
        +
        +// NoticeIndex transforms license metadata into license text hashes, library
        +// names, and install paths indexing them for fast lookup/iteration.
        +type NoticeIndex struct {
        +	// lg identifies the license graph to which the index applies.
        +	lg *LicenseGraph
        +	// rs identifies the set of resolutions upon which the index is based.
        +	rs ResolutionSet
        +	// shipped identifies the set of target nodes shipped directly or as derivative works.
        +	shipped *TargetNodeSet
        +	// rootFS locates the root of the file system from which to read the files.
        +	rootFS fs.FS
        +	// hash maps license text filenames to content hashes
        +	hash map[string]hash
        +	// text maps content hashes to content
        +	text map[hash][]byte
        +	// hashLibInstall maps hashes to libraries to install paths.
        +	hashLibInstall map[hash]map[string]map[string]struct{}
        +	// installHashLib maps install paths to libraries to hashes.
        +	installHashLib map[string]map[hash]map[string]struct{}
        +	// libHash maps libraries to hashes.
        +	libHash map[string]map[hash]struct{}
        +	// targetHash maps target nodes to hashes.
        +	targetHashes map[*TargetNode]map[hash]struct{}
        +	// projectName maps project directory names to project name text.
        +	projectName map[string]string
        +	// files lists all the files accessed during indexing
        +	files []string
        +}
        +
        +// IndexLicenseTexts creates a hashed index of license texts for `lg` and `rs`
        +// using the files rooted at `rootFS`.
        +func IndexLicenseTexts(rootFS fs.FS, lg *LicenseGraph, rs ResolutionSet) (*NoticeIndex, error) {
        +	if rs == nil {
        +		rs = ResolveNotices(lg)
        +	}
        +	ni := &NoticeIndex{
        +		lg:             lg,
        +		rs:             rs,
        +		shipped:        ShippedNodes(lg),
        +		rootFS:         rootFS,
        +		hash:           make(map[string]hash),
        +		text:           make(map[hash][]byte),
        +		hashLibInstall: make(map[hash]map[string]map[string]struct{}),
        +		installHashLib: make(map[string]map[hash]map[string]struct{}),
        +		libHash:        make(map[string]map[hash]struct{}),
        +		targetHashes:   make(map[*TargetNode]map[hash]struct{}),
        +		projectName:    make(map[string]string),
        +	}
        +
        +	// index adds all license texts for `tn` to the index.
        +	index := func(tn *TargetNode) (map[hash]struct{}, error) {
        +		if hashes, ok := ni.targetHashes[tn]; ok {
        +			return hashes, nil
        +		}
        +		hashes := make(map[hash]struct{})
        +		for _, text := range tn.LicenseTexts() {
        +			fname := strings.SplitN(text, ":", 2)[0]
        +			if _, ok := ni.hash[fname]; !ok {
        +				err := ni.addText(fname)
        +				if err != nil {
        +					return nil, err
        +				}
        +			}
        +			hash := ni.hash[fname]
        +			if _, ok := hashes[hash]; !ok {
        +				hashes[hash] = struct{}{}
        +			}
        +		}
        +		ni.targetHashes[tn] = hashes
        +		return hashes, nil
        +	}
        +
        +	link := func(tn *TargetNode, hashes map[hash]struct{}, installPaths []string) {
        +		for h := range hashes {
        +			libName := ni.getLibName(tn, h)
        +			if _, ok := ni.libHash[libName]; !ok {
        +				ni.libHash[libName] = make(map[hash]struct{})
        +			}
        +			if _, ok := ni.hashLibInstall[h]; !ok {
        +				ni.hashLibInstall[h] = make(map[string]map[string]struct{})
        +			}
        +			if _, ok := ni.libHash[libName][h]; !ok {
        +				ni.libHash[libName][h] = struct{}{}
        +			}
        +			for _, installPath := range installPaths {
        +				if _, ok := ni.installHashLib[installPath]; !ok {
        +					ni.installHashLib[installPath] = make(map[hash]map[string]struct{})
        +					ni.installHashLib[installPath][h] = make(map[string]struct{})
        +					ni.installHashLib[installPath][h][libName] = struct{}{}
        +				} else if _, ok = ni.installHashLib[installPath][h]; !ok {
        +					ni.installHashLib[installPath][h] = make(map[string]struct{})
        +					ni.installHashLib[installPath][h][libName] = struct{}{}
        +				} else if _, ok = ni.installHashLib[installPath][h][libName]; !ok {
        +					ni.installHashLib[installPath][h][libName] = struct{}{}
        +				}
        +				if _, ok := ni.hashLibInstall[h]; !ok {
        +					ni.hashLibInstall[h] = make(map[string]map[string]struct{})
        +					ni.hashLibInstall[h][libName] = make(map[string]struct{})
        +					ni.hashLibInstall[h][libName][installPath] = struct{}{}
        +				} else if _, ok = ni.hashLibInstall[h][libName]; !ok {
        +					ni.hashLibInstall[h][libName] = make(map[string]struct{})
        +					ni.hashLibInstall[h][libName][installPath] = struct{}{}
        +				} else if _, ok = ni.hashLibInstall[h][libName][installPath]; !ok {
        +					ni.hashLibInstall[h][libName][installPath] = struct{}{}
        +				}
        +			}
        +		}
        +	}
        +
        +	// returns error from walk below.
        +	var err error
        +
        +	WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        +		if err != nil {
        +			return false
        +		}
        +		if !ni.shipped.Contains(tn) {
        +			return false
        +		}
        +		installPaths := getInstallPaths(tn, path)
        +		var hashes map[hash]struct{}
        +		hashes, err = index(tn)
        +		if err != nil {
        +			return false
        +		}
        +		link(tn, hashes, installPaths)
        +		if tn.IsContainer() {
        +			return true
        +		}
        +
        +		for _, r := range rs.Resolutions(tn) {
        +			hashes, err = index(r.actsOn)
        +			if err != nil {
        +				return false
        +			}
        +			link(r.actsOn, hashes, installPaths)
        +		}
        +		return false
        +	})
        +
        +	if err != nil {
        +		return nil, err
        +	}
        +
        +	return ni, nil
        +}
        +
        +// Hashes returns an ordered channel of the hashed license texts.
        +func (ni *NoticeIndex) Hashes() chan hash {
        +	c := make(chan hash)
        +	go func() {
        +		libs := make([]string, 0, len(ni.libHash))
        +		for libName := range ni.libHash {
        +			libs = append(libs, libName)
        +		}
        +		sort.Strings(libs)
        +		hashes := make(map[hash]struct{})
        +		for _, libName := range libs {
        +			hl := make([]hash, 0, len(ni.libHash[libName]))
        +			for h := range ni.libHash[libName] {
        +				if _, ok := hashes[h]; ok {
        +					continue
        +				}
        +				hashes[h] = struct{}{}
        +				hl = append(hl, h)
        +			}
        +			if len(hl) > 0 {
        +				sort.Sort(hashList{ni, libName, "", &hl})
        +				for _, h := range hl {
        +					c <- h
        +				}
        +			}
        +		}
        +		close(c)
        +	}()
        +	return c
        +}
        +
        +// 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)
        +	return files
        +}
        +
        +// HashLibs returns the ordered array of library names using the license text
        +// hashed as `h`.
        +func (ni *NoticeIndex) HashLibs(h hash) []string {
        +	libs := make([]string, 0, len(ni.hashLibInstall[h]))
        +	for libName := range ni.hashLibInstall[h] {
        +		libs = append(libs, libName)
        +	}
        +	sort.Strings(libs)
        +	return libs
        +}
        +
        +// HashLibInstalls returns the ordered array of install paths referencing
        +// library `libName` using the license text hashed as `h`.
        +func (ni *NoticeIndex) HashLibInstalls(h hash, libName string) []string {
        +	installs := make([]string, 0, len(ni.hashLibInstall[h][libName]))
        +	for installPath := range ni.hashLibInstall[h][libName] {
        +		installs = append(installs, installPath)
        +	}
        +	sort.Strings(installs)
        +	return installs
        +}
        +
        +// InstallPaths returns the ordered channel of indexed install paths.
        +func (ni *NoticeIndex) InstallPaths() chan string {
        +	c := make(chan string)
        +	go func() {
        +		paths := make([]string, 0, len(ni.installHashLib))
        +		for path := range ni.installHashLib {
        +			paths = append(paths, path)
        +		}
        +		sort.Strings(paths)
        +		for _, installPath := range paths {
        +			c <- installPath
        +		}
        +		close(c)
        +	}()
        +	return c
        +}
        +
        +// InstallHashes returns the ordered array of hashes attached to `installPath`.
        +func (ni *NoticeIndex) InstallHashes(installPath string) []hash {
        +	result := make([]hash, 0, len(ni.installHashLib[installPath]))
        +	for h := range ni.installHashLib[installPath] {
        +		result = append(result, h)
        +	}
        +	if len(result) > 0 {
        +		sort.Sort(hashList{ni, "", installPath, &result})
        +	}
        +	return result
        +}
        +
        +// InstallHashLibs returns the ordered array of library names attached to
        +// `installPath` as hash `h`.
        +func (ni *NoticeIndex) InstallHashLibs(installPath string, h hash) []string {
        +	result := make([]string, 0, len(ni.installHashLib[installPath][h]))
        +	for libName := range ni.installHashLib[installPath][h] {
        +		result = append(result, libName)
        +	}
        +	sort.Strings(result)
        +	return result
        +}
        +
        +// Libraries returns the ordered channel of indexed library names.
        +func (ni *NoticeIndex) Libraries() chan string {
        +	c := make(chan string)
        +	go func() {
        +		libs := make([]string, 0, len(ni.libHash))
        +		for lib := range ni.libHash {
        +			libs = append(libs, lib)
        +		}
        +		sort.Strings(libs)
        +		for _, lib := range libs {
        +			c <- lib
        +		}
        +		close(c)
        +	}()
        +	return c
        +}
        +
        +// HashText returns the file content of the license text hashed as `h`.
        +func (ni *NoticeIndex) HashText(h hash) []byte {
        +	return ni.text[h]
        +}
        +
        +// getLibName returns the name of the library associated with `noticeFor`.
        +func (ni *NoticeIndex) getLibName(noticeFor *TargetNode, h hash) string {
        +	for _, text := range noticeFor.LicenseTexts() {
        +		if !strings.Contains(text, ":") {
        +			if ni.hash[text].key != h.key {
        +				continue
        +			}
        +			ln := ni.checkMetadataForLicenseText(noticeFor, text)
        +			if len(ln) > 0 {
        +				return ln
        +			}
        +			continue
        +		}
        +
        +		fields := strings.SplitN(text, ":", 2)
        +		fname, pname := fields[0], fields[1]
        +		if ni.hash[fname].key != h.key {
        +			continue
        +		}
        +
        +		ln, err := url.QueryUnescape(pname)
        +		if err != nil {
        +			continue
        +		}
        +		return ln
        +	}
        +	// use name from METADATA if available
        +	ln := ni.checkMetadata(noticeFor)
        +	if len(ln) > 0 {
        +		return ln
        +	}
        +	// use package_name: from license{} module if available
        +	pn := noticeFor.PackageName()
        +	if len(pn) > 0 {
        +		return pn
        +	}
        +	for _, p := range noticeFor.Projects() {
        +		if strings.HasPrefix(p, "prebuilts/") {
        +			for _, licenseText := range noticeFor.LicenseTexts() {
        +				if !strings.HasPrefix(licenseText, "prebuilts/") {
        +					continue
        +				}
        +				if !strings.Contains(licenseText, ":") {
        +					if ni.hash[licenseText].key != h.key {
        +						continue
        +					}
        +				} else {
        +					fields := strings.SplitN(licenseText, ":", 2)
        +					fname := fields[0]
        +					if ni.hash[fname].key != h.key {
        +						continue
        +					}
        +				}
        +				for r, prefix := range SafePrebuiltPrefixes {
        +					match := r.FindString(licenseText)
        +					if len(match) == 0 {
        +						continue
        +					}
        +					strip := SafePathPrefixes[prefix]
        +					if strip {
        +						// strip entire prefix
        +						match = licenseText[len(match):]
        +					} else {
        +						// strip from prebuilts/ until safe prefix
        +						match = licenseText[len(match)-len(prefix):]
        +					}
        +					// remove LICENSE or NOTICE or other filename
        +					li := strings.LastIndex(match, "/")
        +					if li > 0 {
        +						match = match[:li]
        +					}
        +					// remove *licenses/ path segment and subdirectory if in path
        +					if offsets := licensesPathRegexp.FindAllStringIndex(match, -1); offsets != nil && offsets[len(offsets)-1][0] > 0 {
        +						match = match[:offsets[len(offsets)-1][0]]
        +						li = strings.LastIndex(match, "/")
        +						if li > 0 {
        +							match = match[:li]
        +						}
        +					}
        +					return match
        +				}
        +				break
        +			}
        +		}
        +		for prefix, strip := range SafePathPrefixes {
        +			if strings.HasPrefix(p, prefix) {
        +				if strip {
        +					return p[len(prefix):]
        +				} else {
        +					return p
        +				}
        +			}
        +		}
        +	}
        +	// strip off [./]meta_lic from license metadata path and extract base name
        +	n := noticeFor.name[:len(noticeFor.name)-9]
        +	li := strings.LastIndex(n, "/")
        +	if li > 0 {
        +		n = n[li+1:]
        +	}
        +	fi := strings.Index(n, "@")
        +	if fi > 0 {
        +		n = n[:fi]
        +	}
        +	return n
        +}
        +
        +// 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
        +	}
        +	return ""
        +}
        +
        +// checkMetadataForLicenseText
        +func (ni *NoticeIndex) checkMetadataForLicenseText(noticeFor *TargetNode, licenseText string) string {
        +	p := ""
        +	for _, proj := range noticeFor.Projects() {
        +		if strings.HasPrefix(licenseText, proj) {
        +			p = proj
        +		}
        +	}
        +	if len(p) == 0 {
        +		p = filepath.Dir(licenseText)
        +		for {
        +			fi, err := fs.Stat(ni.rootFS, filepath.Join(p, ".git"))
        +			if err == nil && fi.IsDir() {
        +				break
        +			}
        +			if strings.Contains(p, "/") && p != "/" {
        +				p = filepath.Dir(p)
        +				continue
        +			}
        +			return ""
        +		}
        +	}
        +	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)
        +	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]
        +			}
        +		}
        +	}
        +	_ = 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
        +}
        +
        +// addText reads and indexes the content of a license text file.
        +func (ni *NoticeIndex) addText(file string) error {
        +	f, err := ni.rootFS.Open(filepath.Clean(file))
        +	if err != nil {
        +		return fmt.Errorf("error opening license text file %q: %w", file, err)
        +	}
        +
        +	// read the file
        +	text, err := io.ReadAll(f)
        +	if err != nil {
        +		return fmt.Errorf("error reading license text file %q: %w", file, err)
        +	}
        +
        +	hash := hash{fmt.Sprintf("%x", md5.Sum(text))}
        +	ni.hash[file] = hash
        +	if _, alreadyPresent := ni.text[hash]; !alreadyPresent {
        +		ni.text[hash] = text
        +	}
        +
        +	ni.files = append(ni.files, file)
        +
        +	return nil
        +}
        +
        +// getInstallPaths returns the names of the used dependencies mapped to their
        +// installed locations.
        +func getInstallPaths(attachesTo *TargetNode, path TargetEdgePath) []string {
        +	if len(path) == 0 {
        +		installs := attachesTo.Installed()
        +		if 0 == len(installs) {
        +			installs = attachesTo.Built()
        +		}
        +		return installs
        +	}
        +
        +	var getInstalls func(path TargetEdgePath) []string
        +
        +	getInstalls = func(path TargetEdgePath) []string {
        +		// deps contains the output targets from the dependencies in the path
        +		var deps []string
        +		if len(path) > 1 {
        +			// recursively get the targets from the sub-path skipping 1 path segment
        +			deps = getInstalls(path[1:])
        +		} else {
        +			// stop recursion at 1 path segment
        +			deps = path[0].Dependency().TargetFiles()
        +		}
        +		size := 0
        +		prefixes := path[0].Target().TargetFiles()
        +		installMap := path[0].Target().InstallMap()
        +		sources := path[0].Target().Sources()
        +		for _, dep := range deps {
        +			found := false
        +			for _, source := range sources {
        +				if strings.HasPrefix(dep, source) {
        +					found = true
        +					break
        +				}
        +			}
        +			if !found {
        +				continue
        +			}
        +			for _, im := range installMap {
        +				if strings.HasPrefix(dep, im.FromPath) {
        +					size += len(prefixes)
        +					break
        +				}
        +			}
        +		}
        +
        +		installs := make([]string, 0, size)
        +		for _, dep := range deps {
        +			found := false
        +			for _, source := range sources {
        +				if strings.HasPrefix(dep, source) {
        +					found = true
        +					break
        +				}
        +			}
        +			if !found {
        +				continue
        +			}
        +			for _, im := range installMap {
        +				if strings.HasPrefix(dep, im.FromPath) {
        +					for _, prefix := range prefixes {
        +						installs = append(installs, prefix+im.ContainerPath+dep[len(im.FromPath):])
        +					}
        +					break
        +				}
        +			}
        +		}
        +		return installs
        +	}
        +	allInstalls := getInstalls(path)
        +	installs := path[0].Target().Installed()
        +	if len(installs) == 0 {
        +		return allInstalls
        +	}
        +	result := make([]string, 0, len(allInstalls))
        +	for _, install := range allInstalls {
        +		for _, prefix := range installs {
        +			if strings.HasPrefix(install, prefix) {
        +				result = append(result, install)
        +			}
        +		}
        +	}
        +	return result
        +}
        +
        +// hash is an opaque string derived from md5sum.
        +type hash struct {
        +	key string
        +}
        +
        +// String returns the hexadecimal representation of the hash.
        +func (h hash) String() string {
        +	return h.key
        +}
        +
        +// hashList orders an array of hashes
        +type hashList struct {
        +	ni          *NoticeIndex
        +	libName     string
        +	installPath string
        +	hashes      *[]hash
        +}
        +
        +// Len returns the count of elements in the slice.
        +func (l hashList) Len() int { return len(*l.hashes) }
        +
        +// Swap rearranges 2 elements of the slice so that each occupies the other's
        +// former position.
        +func (l hashList) Swap(i, j int) { (*l.hashes)[i], (*l.hashes)[j] = (*l.hashes)[j], (*l.hashes)[i] }
        +
        +// Less returns true when the `i`th element is lexicographically less than
        +// the `j`th element.
        +func (l hashList) Less(i, j int) bool {
        +	var insti, instj int
        +	if len(l.libName) > 0 {
        +		insti = len(l.ni.hashLibInstall[(*l.hashes)[i]][l.libName])
        +		instj = len(l.ni.hashLibInstall[(*l.hashes)[j]][l.libName])
        +	} else {
        +		libsi := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[i])
        +		libsj := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[j])
        +		libsis := strings.Join(libsi, " ")
        +		libsjs := strings.Join(libsj, " ")
        +		if libsis != libsjs {
        +			return libsis < libsjs
        +		}
        +	}
        +	if insti == instj {
        +		leni := len(l.ni.text[(*l.hashes)[i]])
        +		lenj := len(l.ni.text[(*l.hashes)[j]])
        +		if leni == lenj {
        +			// all else equal, just order by hash value
        +			return (*l.hashes)[i].key < (*l.hashes)[j].key
        +		}
        +		// put shortest texts first within same # of installs
        +		return leni < lenj
        +	}
        +	// reverse order of # installs so that most popular appears first
        +	return instj < insti
        +}
        diff --git a/make/tools/compliance/policy_policy.go b/make/tools/compliance/policy_policy.go
        new file mode 100644
        index 0000000..60bdf48
        --- /dev/null
        +++ b/make/tools/compliance/policy_policy.go
        @@ -0,0 +1,289 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"regexp"
        +	"strings"
        +)
        +
        +var (
        +	// RecognizedAnnotations identifies the set of annotations that have
        +	// meaning for compliance policy.
        +	RecognizedAnnotations = map[string]string{
        +		// used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.
        +		"static":    "static",
        +		"dynamic":   "dynamic",
        +		"toolchain": "toolchain",
        +	}
        +
        +	// SafePathPrefixes maps the path prefixes presumed not to contain any
        +	// proprietary or confidential pathnames to whether to strip the prefix
        +	// from the path when used as the library name for notices.
        +	SafePathPrefixes = map[string]bool{
        +		"external/":    true,
        +		"art/":         false,
        +		"build/":       false,
        +		"cts/":         false,
        +		"dalvik/":      false,
        +		"developers/":  false,
        +		"development/": false,
        +		"frameworks/":  false,
        +		"packages/":    true,
        +		"prebuilts/":   false,
        +		"sdk/":         false,
        +		"system/":      false,
        +		"test/":        false,
        +		"toolchain/":   false,
        +		"tools/":       false,
        +	}
        +
        +	// SafePrebuiltPrefixes maps the regular expression to match a prebuilt
        +	// containing the path of a safe prefix to the safe prefix.
        +	SafePrebuiltPrefixes = make(map[*regexp.Regexp]string)
        +
        +	// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
        +	ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
        +
        +	// ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
        +	ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
        +
        +	// ImpliesNotice lists the condition names implying a notice or attribution policy.
        +	ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
        +		RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition |
        +		ProprietaryCondition | ByExceptionOnlyCondition)
        +
        +	// ImpliesReciprocal lists the condition names implying a local source-sharing policy.
        +	ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
        +
        +	// Restricted lists the condition names implying an infectious source-sharing policy.
        +	ImpliesRestricted = LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
        +
        +	// ImpliesProprietary lists the condition names implying a confidentiality policy.
        +	ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
        +
        +	// ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
        +	ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
        +
        +	// ImpliesPrivate lists the condition names implying a source-code privacy policy.
        +	ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
        +
        +	// ImpliesShared lists the condition names implying a source-code sharing policy.
        +	ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
        +)
        +
        +var (
        +	anyLgpl      = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
        +	versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
        +	genericGpl   = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
        +	ccBySa       = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
        +)
        +
        +func init() {
        +	for prefix := range SafePathPrefixes {
        +		if prefix == "prebuilts/" {
        +			continue
        +		}
        +		r := regexp.MustCompile("^prebuilts/[^ ]*/" + prefix)
        +		SafePrebuiltPrefixes[r] = prefix
        +	}
        +}
        +
        +// LicenseConditionSetFromNames returns a set containing the recognized `names` and
        +// silently ignoring or discarding the unrecognized `names`.
        +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
        +			hasClasspath := false
        +			hasGeneric := false
        +			for _, kind := range tn.LicenseKinds() {
        +				if strings.HasSuffix(kind, "-with-classpath-exception") {
        +					cs = cs.Plus(RestrictedClasspathExceptionCondition)
        +					hasClasspath = true
        +				} else 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 && !hasClasspath {
        +				cs = cs.Plus(RestrictedCondition)
        +			}
        +			continue
        +		}
        +		if lc, ok := RecognizedConditionNames[name]; ok {
        +			cs |= LicenseConditionSet(lc)
        +		}
        +	}
        +	return cs
        +}
        +
        +// Resolution happens in three phases:
        +//
        +// 1. A bottom-up traversal propagates (restricted) license conditions up to
        +// targets from dendencies as needed.
        +//
        +// 2. For each condition of interest, a top-down traversal propagates
        +// (restricted) conditions down from targets into linked dependencies.
        +//
        +// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
        +// ancestor nodes from the root down to and including the first non-container.
        +//
        +// e.g. If a disk image contains a binary bin1 that links a library liba, the
        +// notice requirement for liba gets attached to the disk image and to bin1.
        +// Because liba doesn't actually get shipped as a separate artifact, but only
        +// as bits in bin1, it has no actions 'attached' to it. The actions attached
        +// to the image and to bin1 'act on' liba by providing notice.
        +//
        +// The behavior of the 3 phases gets controlled by the 3 functions below.
        +//
        +// The first function controls what happens during the bottom-up propagation.
        +// Restricted conditions propagate up all non-toolchain dependencies; except,
        +// some do not propagate up dynamic links, which may depend on whether the
        +// modules are independent.
        +//
        +// The second function controls what happens during the top-down propagation.
        +// Restricted conditions propagate down as above with the added caveat that
        +// inherited restricted conditions do not propagate from pure aggregates to
        +// their dependencies.
        +//
        +// The final function controls which conditions apply/get attached to ancestors
        +// depending on the types of dependencies involved. All conditions apply across
        +// normal derivation dependencies. No conditions apply across toolchain
        +// dependencies. Some restricted conditions apply across dynamic link
        +// dependencies.
        +//
        +// Not all restricted licenses are create equal. Some have special rules or
        +// exceptions. e.g. LGPL or "with classpath excption".
        +
        +// depConditionsPropagatingToTarget returns the conditions which propagate up an
        +// edge from dependency to target.
        +//
        +// This function sets the policy for the bottom-up propagation and how conditions
        +// flow up the graph from dependencies to targets.
        +//
        +// If a pure aggregation is built into a derivative work that is not a pure
        +// aggregation, per policy it ceases to be a pure aggregation in the context of
        +// that derivative work. The `treatAsAggregate` parameter will be false for
        +// non-aggregates and for aggregates in non-aggregate contexts.
        +func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
        +	result := LicenseConditionSet(0x0000)
        +	if edgeIsDerivation(e) {
        +		result |= depConditions & ImpliesRestricted
        +		return result
        +	}
        +	if !edgeIsDynamicLink(e) {
        +		return result
        +	}
        +
        +	result |= depConditions & LicenseConditionSet(RestrictedCondition)
        +	if 0 != (depConditions&LicenseConditionSet(RestrictedClasspathExceptionCondition)) && !edgeNodesAreIndependentModules(e) {
        +		result |= LicenseConditionSet(RestrictedClasspathExceptionCondition)
        +	}
        +	return result
        +}
        +
        +// targetConditionsPropagatingToDep returns the conditions which propagate down
        +// an edge from target to dependency.
        +//
        +// This function sets the policy for the top-down traversal and how conditions
        +// flow down the graph from targets to dependencies.
        +//
        +// If a pure aggregation is built into a derivative work that is not a pure
        +// aggregation, per policy it ceases to be a pure aggregation in the context of
        +// that derivative work. The `treatAsAggregate` parameter will be false for
        +// non-aggregates and for aggregates in non-aggregate contexts.
        +func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
        +	result := targetConditions
        +
        +	// reverse direction -- none of these apply to things depended-on, only to targets depending-on.
        +	result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
        +
        +	if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
        +		// target is not a derivative work of dependency and is not linked to dependency
        +		result = result.Difference(ImpliesRestricted)
        +		return result
        +	}
        +	if treatAsAggregate {
        +		// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
        +		// Otherwise, restricted does not propagate back down to dependencies.
        +		if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
        +			result = result.Difference(ImpliesRestricted)
        +		}
        +		return result
        +	}
        +	if edgeIsDerivation(e) {
        +		return result
        +	}
        +	result = result.Minus(WeaklyRestrictedCondition)
        +	if edgeNodesAreIndependentModules(e) {
        +		result = result.Minus(RestrictedClasspathExceptionCondition)
        +	}
        +	return result
        +}
        +
        +// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
        +// that apply across edge `e`.
        +//
        +// This function sets the policy for attaching actions to ancestor nodes in the
        +// final resolution walk.
        +func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
        +	result := universe
        +	if edgeIsDerivation(e) {
        +		return result
        +	}
        +	if !edgeIsDynamicLink(e) {
        +		return NewLicenseConditionSet()
        +	}
        +
        +	result &= LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition)
        +	if 0 != (result&LicenseConditionSet(RestrictedClasspathExceptionCondition)) && edgeNodesAreIndependentModules(e) {
        +		result &= LicenseConditionSet(RestrictedCondition)
        +	}
        +	return result
        +}
        +
        +// edgeIsDynamicLink returns true for edges representing shared libraries
        +// linked dynamically at runtime.
        +func edgeIsDynamicLink(e *TargetEdge) bool {
        +	return e.annotations.HasAnnotation("dynamic")
        +}
        +
        +// edgeIsDerivation returns true for edges where the target is a derivative
        +// work of dependency.
        +func edgeIsDerivation(e *TargetEdge) bool {
        +	isDynamic := e.annotations.HasAnnotation("dynamic")
        +	isToolchain := e.annotations.HasAnnotation("toolchain")
        +	return !isDynamic && !isToolchain
        +}
        +
        +// edgeNodesAreIndependentModules returns true for edges where the target and
        +// dependency are independent modules.
        +func edgeNodesAreIndependentModules(e *TargetEdge) bool {
        +	return e.target.PackageName() != e.dependency.PackageName()
        +}
        diff --git a/make/tools/compliance/policy_policy_test.go b/make/tools/compliance/policy_policy_test.go
        new file mode 100644
        index 0000000..27ce16c
        --- /dev/null
        +++ b/make/tools/compliance/policy_policy_test.go
        @@ -0,0 +1,309 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"fmt"
        +	"sort"
        +	"strings"
        +	"testing"
        +)
        +
        +func TestPolicy_edgeConditions(t *testing.T) {
        +	tests := []struct {
        +		name                     string
        +		edge                     annotated
        +		treatAsAggregate         bool
        +		otherCondition           string
        +		expectedDepActions       []string
        +		expectedTargetConditions []string
        +	}{
        +		{
        +			name:                     "firstparty",
        +			edge:                     annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "notice",
        +			edge:                     annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name: "fponlgpl",
        +			edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			expectedDepActions: []string{
        +				"apacheBin.meta_lic:lgplLib.meta_lic:restricted_allows_dynamic_linking",
        +				"lgplLib.meta_lic:lgplLib.meta_lic:restricted_allows_dynamic_linking",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "fponlgpldynamic",
        +			edge:                     annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name: "fpongpl",
        +			edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			expectedDepActions: []string{
        +				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
        +				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name: "fpongpldynamic",
        +			edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			expectedDepActions: []string{
        +				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
        +				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "independentmodule",
        +			edge:                     annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name: "independentmodulestatic",
        +			edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			expectedDepActions: []string{
        +				"apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
        +				"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name: "dependentmodule",
        +			edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			expectedDepActions: []string{
        +				"dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
        +				"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +
        +		{
        +			name:                     "lgplonfp",
        +			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"lgplBin.meta_lic:restricted_allows_dynamic_linking"},
        +		},
        +		{
        +			name:                     "lgplonfpdynamic",
        +			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "gplonfp",
        +			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
        +		},
        +		{
        +			name:                     "gplcontainer",
        +			edge:                     annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			treatAsAggregate:         true,
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
        +		},
        +		{
        +			name:             "gploncontainer",
        +			edge:             annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			treatAsAggregate: true,
        +			otherCondition:   "gplLib.meta_lic:restricted",
        +			expectedDepActions: []string{
        +				"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
        +				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
        +				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:             "gplonbin",
        +			edge:             annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			treatAsAggregate: false,
        +			otherCondition:   "gplLib.meta_lic:restricted",
        +			expectedDepActions: []string{
        +				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
        +				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
        +				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        +			},
        +			expectedTargetConditions: []string{"gplLib.meta_lic:restricted"},
        +		},
        +		{
        +			name:                     "gplonfpdynamic",
        +			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
        +		},
        +		{
        +			name:                     "independentmodulereverse",
        +			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "independentmodulereversestatic",
        +			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted_with_classpath_exception"},
        +		},
        +		{
        +			name:                     "dependentmodulereverse",
        +			edge:                     annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted_with_classpath_exception"},
        +		},
        +		{
        +			name: "ponr",
        +			edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			expectedDepActions: []string{
        +				"proprietary.meta_lic:gplLib.meta_lic:restricted",
        +				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        +			},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "ronp",
        +			edge:                     annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
        +		},
        +		{
        +			name:                     "noticeonb_e_o",
        +			edge:                     annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "b_e_oonnotice",
        +			edge:                     annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "noticeonrecip",
        +			edge:                     annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +		{
        +			name:                     "reciponnotice",
        +			edge:                     annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			expectedDepActions:       []string{},
        +			expectedTargetConditions: []string{},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			fs := make(testFS)
        +			stderr := &bytes.Buffer{}
        +			target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n  file: \"%s\"\n", tt.edge.dep)
        +			for _, ann := range tt.edge.annotations {
        +				target += fmt.Sprintf("  annotations: \"%s\"\n", ann)
        +			}
        +			fs[tt.edge.target] = []byte(target + "}\n")
        +			fs[tt.edge.dep] = []byte(meta[tt.edge.dep])
        +			lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})
        +			if err != nil {
        +				t.Errorf("unexpected error reading graph: %s", err)
        +				return
        +			}
        +			edge := lg.Edges()[0]
        +			// simulate a condition inherited from another edge/dependency.
        +			otherTarget := ""
        +			otherCondition := ""
        +			var otn *TargetNode
        +			if len(tt.otherCondition) > 0 {
        +				fields := strings.Split(tt.otherCondition, ":")
        +				otherTarget = fields[0]
        +				otherCondition = fields[1]
        +				otn = &TargetNode{name: otherTarget}
        +				// other target must exist in graph
        +				lg.targets[otherTarget] = otn
        +				otn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition])
        +			}
        +			targets := make(map[string]*TargetNode)
        +			targets[edge.target.name] = edge.target
        +			targets[edge.dependency.name] = edge.dependency
        +			if otn != nil {
        +				targets[otn.name] = otn
        +			}
        +			if tt.expectedDepActions != nil {
        +				t.Run("depConditionsPropagatingToTarget", func(t *testing.T) {
        +					depConditions := edge.dependency.LicenseConditions()
        +					if otherTarget != "" {
        +						// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
        +						otherCs := otn.LicenseConditions()
        +						depConditions |= otherCs
        +					}
        +					t.Logf("calculate target actions for edge=%s, dep conditions=%04x, treatAsAggregate=%v", edge.String(), depConditions, tt.treatAsAggregate)
        +					csActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate)
        +					t.Logf("calculated target conditions as %04x{%s}", csActual, strings.Join(csActual.Names(), ", "))
        +					csExpected := NewLicenseConditionSet()
        +					for _, triple := range tt.expectedDepActions {
        +						fields := strings.Split(triple, ":")
        +						expectedConditions := NewLicenseConditionSet()
        +						for _, cname := range fields[2:] {
        +							expectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname])
        +						}
        +						csExpected |= expectedConditions
        +					}
        +					t.Logf("expected target conditions as %04x{%s}", csExpected, strings.Join(csExpected.Names(), ", "))
        +					if csActual != csExpected {
        +						t.Errorf("unexpected license conditions: got %04x, want %04x", csActual, csExpected)
        +					}
        +				})
        +			}
        +			if tt.expectedTargetConditions != nil {
        +				t.Run("targetConditionsPropagatingToDep", func(t *testing.T) {
        +					targetConditions := edge.target.LicenseConditions()
        +					if otherTarget != "" {
        +						targetConditions = targetConditions.Union(otn.licenseConditions)
        +					}
        +					t.Logf("calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v", edge.String(), targetConditions.Names(), tt.treatAsAggregate)
        +					cs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate, AllResolutions)
        +					t.Logf("calculated dep conditions as %v", cs.Names())
        +					actual := cs.Names()
        +					sort.Strings(actual)
        +					expected := make([]string, 0)
        +					for _, expectedDepCondition := range tt.expectedTargetConditions {
        +						expected = append(expected, strings.Split(expectedDepCondition, ":")[1])
        +					}
        +					sort.Strings(expected)
        +					if len(actual) != len(expected) {
        +						t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
        +							actual, len(actual), expected, len(expected))
        +					} else {
        +						for i := 0; i < len(actual); i++ {
        +							if actual[i] != expected[i] {
        +								t.Errorf("unexpected target condition at element %d: got %q, want %q",
        +									i, actual[i], expected[i])
        +							}
        +						}
        +					}
        +				})
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_resolve.go b/make/tools/compliance/policy_resolve.go
        new file mode 100644
        index 0000000..d357aec
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolve.go
        @@ -0,0 +1,234 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"sync"
        +)
        +
        +var (
        +	// AllResolutions is a TraceConditions function that resolves all
        +	// unfiltered license conditions.
        +	AllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions })
        +)
        +
        +// TraceConditions is a function that returns the conditions to trace for each
        +// target node `tn`.
        +type TraceConditions func(tn *TargetNode) LicenseConditionSet
        +
        +// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
        +// propagating conditions up the graph as necessary according to the properties
        +// of each edge and according to each license condition in question.
        +//
        +// e.g. if a "restricted" condition applies to a binary, it also applies to all
        +// of the statically-linked libraries and the transitive closure of their static
        +// dependencies; even if neither they nor the transitive closure of their
        +// dependencies originate any "restricted" conditions. The bottom-up walk will
        +// not resolve the library and its transitive closure, but the later top-down
        +// walk will.
        +func ResolveBottomUpConditions(lg *LicenseGraph) {
        +	TraceBottomUpConditions(lg, AllResolutions)
        +}
        +
        +// TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph
        +// propagating trace conditions from `conditionsFn` up the graph as necessary
        +// according to the properties of each edge and according to each license
        +// condition in question.
        +func TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
        +
        +	// short-cut if already walked and cached
        +	lg.mu.Lock()
        +	wg := lg.wgBU
        +
        +	if wg != nil {
        +		lg.mu.Unlock()
        +		wg.Wait()
        +		return
        +	}
        +	wg = &sync.WaitGroup{}
        +	wg.Add(1)
        +	lg.wgBU = wg
        +	lg.mu.Unlock()
        +
        +	// amap identifes targets previously walked. (guarded by mu)
        +	amap := make(map[*TargetNode]struct{})
        +
        +	// cmap identifies targets previously walked as pure aggregates. i.e. as containers
        +	// (guarded by mu)
        +	cmap := make(map[*TargetNode]struct{})
        +	var mu sync.Mutex
        +
        +	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 _, asAggregate := cmap[target]; !asAggregate {
        +					return target.resolution, true
        +				}
        +				// previously walked in a pure aggregate context,
        +				// needs to walk again in non-aggregate context
        +				delete(cmap, target)
        +			} else {
        +				target.resolution |= conditionsFn(target)
        +				amap[target] = struct{}{}
        +			}
        +			if treatAsAggregate {
        +				cmap[target] = struct{}{}
        +			}
        +			return target.resolution, false
        +		}
        +		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
        +	}
        +
        +	// 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
        +// propagating conditions from target to dependency.
        +//
        +// e.g. For current policy, none of the conditions propagate from target to
        +// dependency except restricted. For restricted, the policy is to share the
        +// source of any libraries linked to restricted code and to provide notice.
        +func ResolveTopDownConditions(lg *LicenseGraph) {
        +	TraceTopDownConditions(lg, AllResolutions)
        +}
        +
        +// TraceTopDownCondtions performs a top-down walk of the LicenseGraph
        +// propagating trace conditions returned by `conditionsFn` from target to
        +// dependency.
        +func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
        +
        +	// short-cut if already walked and cached
        +	lg.mu.Lock()
        +	wg := lg.wgTD
        +
        +	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)
        +
        +	// amap contains the set of targets already walked. (guarded by mu)
        +	amap := make(map[*TargetNode]struct{})
        +
        +	// cmap contains the set of targets walked as pure aggregates. i.e. containers
        +	// (guarded by mu)
        +	cmap := make(map[*TargetNode]struct{})
        +
        +	// mu guards concurrent access to cmap
        +	var mu sync.Mutex
        +
        +	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
        +		amap[fnode] = struct{}{}
        +		if treatAsAggregate {
        +			cmap[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
        +				mu.Lock()
        +				defer mu.Unlock()
        +				depcs := dnode.resolution
        +				_, alreadyWalked := amap[dnode]
        +				if !dcs.IsEmpty() && alreadyWalked {
        +					if dcs.Difference(depcs).IsEmpty() {
        +						// no new conditions
        +
        +						// pure aggregates never need walking a 2nd time with same conditions
        +						if treatAsAggregate {
        +							return
        +						}
        +						// non-aggregates don't need walking as non-aggregate a 2nd time
        +						if _, asAggregate := cmap[dnode]; !asAggregate {
        +							return
        +						}
        +						// previously walked as pure aggregate; need to re-walk as non-aggregate
        +						delete(cmap, dnode)
        +					}
        +				}
        +				// 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()
        +}
        diff --git a/make/tools/compliance/policy_resolve_test.go b/make/tools/compliance/policy_resolve_test.go
        new file mode 100644
        index 0000000..f98e4cc
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolve_test.go
        @@ -0,0 +1,672 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"sort"
        +	"testing"
        +)
        +
        +func TestResolveBottomUpConditions(t *testing.T) {
        +	tests := []struct {
        +		name            string
        +		roots           []string
        +		edges           []annotated
        +		expectedActions []tcond
        +	}{
        +		{
        +			name:  "firstparty",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartytool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartywide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restrictedtool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restrictedwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedtool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "classpath",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted_with_classpath_exception"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependent",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +			},
        +		},
        +		{
        +			name:  "classpathdynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependentdynamic",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +
        +			logGraph(lg, t)
        +
        +			ResolveBottomUpConditions(lg)
        +			actual := asActionList(lg)
        +			sort.Sort(actual)
        +			t.Logf("actual: %s", actual.String())
        +
        +			expected := toActionList(lg, tt.expectedActions)
        +			sort.Sort(expected)
        +			t.Logf("expected: %s", expected.String())
        +
        +			if len(actual) != len(expected) {
        +				t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
        +				return
        +			}
        +			for i := 0; i < len(actual); i++ {
        +				if actual[i] != expected[i] {
        +					t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
        +				}
        +			}
        +		})
        +	}
        +}
        +
        +func TestResolveTopDownConditions(t *testing.T) {
        +	tests := []struct {
        +		name            string
        +		roots           []string
        +		edges           []annotated
        +		expectedActions []tcond
        +	}{
        +		{
        +			name:  "firstparty",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"mitLib.meta_lic", "notice|restricted"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restrictedtool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"mitLib.meta_lic", "notice"},
        +				{"gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"mitBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +				{"mplLib.meta_lic", "reciprocal|restricted"},
        +				{"mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restrictedwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"gplLib.meta_lic", "restricted"},
        +				{"mitLib.meta_lic", "notice|restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice|restricted"},
        +				{"mitBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +				{"mplLib.meta_lic", "reciprocal|restricted"},
        +				{"mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +				{"mitLib.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedtool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
        +				{"mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +				{"mitLib.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +				{"mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheContainer.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
        +			},
        +		},
        +		{
        +			name:  "classpath",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice|restricted_with_classpath_exception"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +				{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependent",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +				{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
        +			},
        +		},
        +		{
        +			name:  "classpathdynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"apacheBin.meta_lic", "notice"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +				{"mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependentdynamic",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []tcond{
        +				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
        +				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
        +				{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +
        +			logGraph(lg, t)
        +
        +			ResolveTopDownConditions(lg)
        +			actual := asActionList(lg)
        +			sort.Sort(actual)
        +			t.Logf("actual: %s", actual.String())
        +
        +			expected := toActionList(lg, tt.expectedActions)
        +			sort.Sort(expected)
        +			t.Logf("expected: %s", expected.String())
        +
        +			if len(actual) != len(expected) {
        +				t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
        +				return
        +			}
        +			for i := 0; i < len(actual); i++ {
        +				if actual[i] != expected[i] {
        +					t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
        +				}
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_resolvenotices.go b/make/tools/compliance/policy_resolvenotices.go
        new file mode 100644
        index 0000000..99f6d42
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolvenotices.go
        @@ -0,0 +1,21 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +// ResolveNotices implements the policy for notices.
        +func ResolveNotices(lg *LicenseGraph) ResolutionSet {
        +	ResolveTopDownConditions(lg)
        +	return WalkResolutionsForCondition(lg, ImpliesNotice)
        +}
        diff --git a/make/tools/compliance/policy_resolvenotices_test.go b/make/tools/compliance/policy_resolvenotices_test.go
        new file mode 100644
        index 0000000..cd9dd71
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolvenotices_test.go
        @@ -0,0 +1,468 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"testing"
        +)
        +
        +func TestResolveNotices(t *testing.T) {
        +	tests := []struct {
        +		name                string
        +		roots               []string
        +		edges               []annotated
        +		expectedResolutions []res
        +	}{
        +		{
        +			name:  "firstparty",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "firstpartydynamicshipped",
        +			roots: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restrictedtool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricteddeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restrictedwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicshipped",
        +			roots: []string{"apacheBin.meta_lic", "mitLib.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"mitLib.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "restricteddynamicwideshipped",
        +			roots: []string{"apacheContainer.meta_lic", "gplLib.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedtool",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedtoolshipped",
        +			roots: []string{"apacheBin.meta_lic", "lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestrictedwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicshipped",
        +			roots: []string{"apacheBin.meta_lic", "lgplLib.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicdeepshipped",
        +			roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicwide",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "weakrestricteddynamicwideshipped",
        +			roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "classpath",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependent",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "classpathdynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:  "classpathdynamicshipped",
        +			roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependentdynamic",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "classpathdependentdynamicshipped",
        +			roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        +			actualRs := ResolveNotices(lg)
        +			checkSame(actualRs, expectedRs, t)
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_resolveprivacy.go b/make/tools/compliance/policy_resolveprivacy.go
        new file mode 100644
        index 0000000..2a7992e
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolveprivacy.go
        @@ -0,0 +1,21 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +// ResolveSourcePrivacy implements the policy for source privacy.
        +func ResolveSourcePrivacy(lg *LicenseGraph) ResolutionSet {
        +	ResolveTopDownConditions(lg)
        +	return WalkResolutionsForCondition(lg, ImpliesPrivate)
        +}
        diff --git a/make/tools/compliance/policy_resolveprivacy_test.go b/make/tools/compliance/policy_resolveprivacy_test.go
        new file mode 100644
        index 0000000..e8c953a
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolveprivacy_test.go
        @@ -0,0 +1,87 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"testing"
        +)
        +
        +func TestResolveSourcePrivacy(t *testing.T) {
        +	tests := []struct {
        +		name                string
        +		roots               []string
        +		edges               []annotated
        +		expectedResolutions []res
        +	}{
        +		{
        +			name:  "firstparty",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:  "notice",
        +			roots: []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:  "lgpl",
        +			roots: []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:  "proprietaryonresricted",
        +			roots: []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +			},
        +		},
        +		{
        +			name:  "restrictedonproprietary",
        +			roots: []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        +			actualRs := ResolveSourcePrivacy(lg)
        +			checkResolves(actualRs, expectedRs, t)
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_resolveshare.go b/make/tools/compliance/policy_resolveshare.go
        new file mode 100644
        index 0000000..9b6a8bb
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolveshare.go
        @@ -0,0 +1,21 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +// ResolveSourceSharing implements the policy for source-sharing.
        +func ResolveSourceSharing(lg *LicenseGraph) ResolutionSet {
        +	ResolveTopDownConditions(lg)
        +	return WalkResolutionsForCondition(lg, ImpliesShared)
        +}
        diff --git a/make/tools/compliance/policy_resolveshare_test.go b/make/tools/compliance/policy_resolveshare_test.go
        new file mode 100644
        index 0000000..c451b86
        --- /dev/null
        +++ b/make/tools/compliance/policy_resolveshare_test.go
        @@ -0,0 +1,297 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"testing"
        +)
        +
        +func TestResolveSourceSharing(t *testing.T) {
        +	tests := []struct {
        +		name                string
        +		roots               []string
        +		edges               []annotated
        +		expectedResolutions []res
        +	}{
        +		{
        +			name:  "independentmodulerestricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:  "independentmodulerestrictedshipped",
        +			roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "independentmodulestaticrestricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "dependentmodulerestricted",
        +			roots: []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "dependentmodulerestrictedshipclasspath",
        +			roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "lgplonfprestricted",
        +			roots: []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "lgplonfpdynamicrestricted",
        +			roots: []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "lgplonfpdynamicrestrictedshiplib",
        +			roots: []string{"lgplBin.meta_lic", "apacheLib.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "gplonfprestricted",
        +			roots: []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "gplcontainerrestricted",
        +			roots: []string{"gplContainer.meta_lic"},
        +			edges: []annotated{
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "gploncontainerrestricted",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "gplonbinrestricted",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "gplonfpdynamicrestricted",
        +			roots: []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "gplonfpdynamicrestrictedshiplib",
        +			roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "independentmodulereverserestricted",
        +			roots: []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "independentmodulereversestaticrestricted",
        +			roots: []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "dependentmodulereverserestricted",
        +			roots: []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "dependentmodulereverserestrictedshipdependent",
        +			roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "ponrrestricted",
        +			roots: []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "ronprestricted",
        +			roots: []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:  "noticeonb_e_orestricted",
        +			roots: []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:  "b_e_oonnoticerestricted",
        +			roots: []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:  "noticeonreciprecip",
        +			roots: []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +			},
        +		},
        +		{
        +			name:  "reciponnoticerecip",
        +			roots: []string{"mplBin.meta_lic"},
        +			edges: []annotated{
        +				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        +			actualRs := ResolveSourceSharing(lg)
        +			checkResolves(actualRs, expectedRs, t)
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_shareprivacyconflicts.go b/make/tools/compliance/policy_shareprivacyconflicts.go
        new file mode 100644
        index 0000000..279e179
        --- /dev/null
        +++ b/make/tools/compliance/policy_shareprivacyconflicts.go
        @@ -0,0 +1,71 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +)
        +
        +// SourceSharePrivacyConflict describes an individual conflict between a source-sharing
        +// condition and a source privacy condition
        +type SourceSharePrivacyConflict struct {
        +	SourceNode       *TargetNode
        +	ShareCondition   LicenseCondition
        +	PrivacyCondition LicenseCondition
        +}
        +
        +// Error returns a string describing the conflict.
        +func (conflict SourceSharePrivacyConflict) Error() string {
        +	return fmt.Sprintf("%s %s and must share from %s condition\n", conflict.SourceNode.name,
        +		conflict.PrivacyCondition.Name(), conflict.ShareCondition.Name())
        +}
        +
        +// IsEqualTo returns true when `conflict` and `other` describe the same conflict.
        +func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
        +	return conflict.SourceNode.name == other.SourceNode.name &&
        +		conflict.ShareCondition == other.ShareCondition &&
        +		conflict.PrivacyCondition == other.PrivacyCondition
        +}
        +
        +// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
        +// share the source and to keep the source private apply to the target.
        +func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
        +
        +	ResolveTopDownConditions(lg)
        +	// combined is the combination of source-sharing and source privacy.
        +	combined := WalkActionsForCondition(lg, ImpliesShared.Union(ImpliesPrivate))
        +
        +	// size is the size of the result
        +	size := 0
        +	for _, cs := range combined {
        +		size += cs.Intersection(ImpliesShared).Len() * cs.Intersection(ImpliesPrivate).Len()
        +	}
        +	if size == 0 {
        +		return nil
        +	}
        +	result := make([]SourceSharePrivacyConflict, 0, size)
        +	for actsOn, cs := range combined {
        +		pconditions := cs.Intersection(ImpliesPrivate).AsList()
        +		ssconditions := cs.Intersection(ImpliesShared).AsList()
        +
        +		// report all conflicting condition combinations
        +		for _, p := range pconditions {
        +			for _, ss := range ssconditions {
        +				result = append(result, SourceSharePrivacyConflict{actsOn, ss, p})
        +			}
        +		}
        +	}
        +	return result
        +}
        diff --git a/make/tools/compliance/policy_shareprivacyconflicts_test.go b/make/tools/compliance/policy_shareprivacyconflicts_test.go
        new file mode 100644
        index 0000000..069daa2
        --- /dev/null
        +++ b/make/tools/compliance/policy_shareprivacyconflicts_test.go
        @@ -0,0 +1,123 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"sort"
        +	"testing"
        +)
        +
        +// byConflict orders conflicts by target then share then privacy
        +type byConflict []SourceSharePrivacyConflict
        +
        +// Len returns the count of elements in the slice.
        +func (l byConflict) Len() int { return len(l) }
        +
        +// Swap rearranged 2 elements so that each occupies the other's former
        +// position.
        +func (l byConflict) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +
        +// Less returns true when the `i`th element is lexicographically less than
        +// the `j`th element.
        +func (l byConflict) Less(i, j int) bool {
        +	if l[i].SourceNode.Name() == l[j].SourceNode.Name() {
        +		if l[i].ShareCondition.Name() == l[j].ShareCondition.Name() {
        +			return l[i].PrivacyCondition.Name() < l[j].PrivacyCondition.Name()
        +		}
        +		return l[i].ShareCondition.Name() < l[j].ShareCondition.Name()
        +	}
        +	return l[i].SourceNode.Name() < l[j].SourceNode.Name()
        +}
        +
        +func TestConflictingSharedPrivateSource(t *testing.T) {
        +	tests := []struct {
        +		name              string
        +		roots             []string
        +		edges             []annotated
        +		expectedConflicts []confl
        +	}{
        +		{
        +			name:  "firstparty",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedConflicts: []confl{},
        +		},
        +		{
        +			name:  "notice",
        +			roots: []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedConflicts: []confl{},
        +		},
        +		{
        +			name:  "lgpl",
        +			roots: []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedConflicts: []confl{},
        +		},
        +		{
        +			name:  "proprietaryonrestricted",
        +			roots: []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedConflicts: []confl{
        +				{"proprietary.meta_lic", "gplLib.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
        +			},
        +		},
        +		{
        +			name:  "restrictedonproprietary",
        +			roots: []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedConflicts: []confl{
        +				{"proprietary.meta_lic", "gplBin.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			expectedConflicts := toConflictList(lg, tt.expectedConflicts)
        +			actualConflicts := ConflictingSharedPrivateSource(lg)
        +			sort.Sort(byConflict(expectedConflicts))
        +			sort.Sort(byConflict(actualConflicts))
        +			if len(expectedConflicts) != len(actualConflicts) {
        +				t.Errorf("unexpected number of share/privacy conflicts: got %v with %d conflicts, want %v with %d conflicts",
        +					actualConflicts, len(actualConflicts), expectedConflicts, len(expectedConflicts))
        +			} else {
        +				for i := 0; i < len(actualConflicts); i++ {
        +					if !actualConflicts[i].IsEqualTo(expectedConflicts[i]) {
        +						t.Errorf("unexpected share/privacy conflict at element %d: got %q, want %q",
        +							i, actualConflicts[i].Error(), expectedConflicts[i].Error())
        +					}
        +				}
        +			}
        +
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_shipped.go b/make/tools/compliance/policy_shipped.go
        new file mode 100644
        index 0000000..75c8399
        --- /dev/null
        +++ b/make/tools/compliance/policy_shipped.go
        @@ -0,0 +1,54 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +// ShippedNodes returns the set of nodes in a license graph where the target or
        +// a derivative work gets distributed. (caches result)
        +func ShippedNodes(lg *LicenseGraph) *TargetNodeSet {
        +	lg.mu.Lock()
        +	shipped := lg.shippedNodes
        +	lg.mu.Unlock()
        +	if shipped != nil {
        +		return shipped
        +	}
        +
        +	tset := make(map[*TargetNode]struct{})
        +
        +	WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        +		if _, alreadyWalked := tset[tn]; alreadyWalked {
        +			return false
        +		}
        +		if len(path) > 0 {
        +			if !edgeIsDerivation(path[len(path)-1].edge) {
        +				return false
        +			}
        +		}
        +		tset[tn] = struct{}{}
        +		return true
        +	})
        +
        +	shipped = &TargetNodeSet{tset}
        +
        +	lg.mu.Lock()
        +	if lg.shippedNodes == nil {
        +		lg.shippedNodes = shipped
        +	} else {
        +		// if we end up with 2, release the later for garbage collection.
        +		shipped = lg.shippedNodes
        +	}
        +	lg.mu.Unlock()
        +
        +	return shipped
        +}
        diff --git a/make/tools/compliance/policy_shipped_test.go b/make/tools/compliance/policy_shipped_test.go
        new file mode 100644
        index 0000000..3ae9b46
        --- /dev/null
        +++ b/make/tools/compliance/policy_shipped_test.go
        @@ -0,0 +1,143 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"sort"
        +	"strings"
        +	"testing"
        +)
        +
        +func TestShippedNodes(t *testing.T) {
        +	tests := []struct {
        +		name          string
        +		roots         []string
        +		edges         []annotated
        +		expectedNodes []string
        +	}{
        +		{
        +			name:          "singleton",
        +			roots:         []string{"apacheLib.meta_lic"},
        +			edges:         []annotated{},
        +			expectedNodes: []string{"apacheLib.meta_lic"},
        +		},
        +		{
        +			name:  "simplebinary",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedNodes: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
        +		},
        +		{
        +			name:  "simpledynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedNodes: []string{"apacheBin.meta_lic"},
        +		},
        +		{
        +			name:  "container",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedNodes: []string{
        +				"apacheContainer.meta_lic",
        +				"apacheLib.meta_lic",
        +				"gplLib.meta_lic",
        +			},
        +		},
        +		{
        +			name:  "binary",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedNodes: []string{
        +				"apacheBin.meta_lic",
        +				"apacheLib.meta_lic",
        +				"gplLib.meta_lic",
        +			},
        +		},
        +		{
        +			name:  "binarydynamic",
        +			roots: []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedNodes: []string{
        +				"apacheBin.meta_lic",
        +				"apacheLib.meta_lic",
        +			},
        +		},
        +		{
        +			name:  "containerdeep",
        +			roots: []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheLib.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedNodes: []string{
        +				"apacheContainer.meta_lic",
        +				"apacheBin.meta_lic",
        +				"apacheLib.meta_lic",
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			t.Logf("graph:")
        +			for _, edge := range lg.Edges() {
        +				t.Logf("  %s", edge.String())
        +			}
        +			expectedNodes := append([]string{}, tt.expectedNodes...)
        +			nodeset := ShippedNodes(lg)
        +			t.Logf("shipped node set: %s", nodeset.String())
        +
        +			actualNodes := nodeset.Names()
        +			t.Logf("shipped nodes: [%s]", strings.Join(actualNodes, ", "))
        +
        +			sort.Strings(expectedNodes)
        +			sort.Strings(actualNodes)
        +
        +			t.Logf("sorted nodes: [%s]", strings.Join(actualNodes, ", "))
        +			t.Logf("expected nodes: [%s]", strings.Join(expectedNodes, ", "))
        +			if len(expectedNodes) != len(actualNodes) {
        +				t.Errorf("unexpected number of shipped nodes: %d nodes, want %d nodes",
        +					len(actualNodes), len(expectedNodes))
        +				return
        +			}
        +			for i := 0; i < len(actualNodes); i++ {
        +				if expectedNodes[i] != actualNodes[i] {
        +					t.Errorf("unexpected node at index %d: got %q, want %q",
        +						i, actualNodes[i], expectedNodes[i])
        +				}
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/policy_walk.go b/make/tools/compliance/policy_walk.go
        new file mode 100644
        index 0000000..f4d7bba
        --- /dev/null
        +++ b/make/tools/compliance/policy_walk.go
        @@ -0,0 +1,238 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +// EdgeContextProvider is an interface for injecting edge-specific context
        +// into walk paths.
        +type EdgeContextProvider interface {
        +	// Context returns the context for `edge` when added to `path`.
        +	Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{}
        +}
        +
        +// NoEdgeContext implements EdgeContextProvider for walks that use no context.
        +type NoEdgeContext struct{}
        +
        +// Context returns nil.
        +func (ctx NoEdgeContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
        +	return nil
        +}
        +
        +// ApplicableConditionsContext provides the subset of conditions in `universe`
        +// that apply to each edge in a path.
        +type ApplicableConditionsContext struct {
        +	universe LicenseConditionSet
        +}
        +
        +// Context returns the LicenseConditionSet applicable to the edge.
        +func (ctx ApplicableConditionsContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
        +	universe := ctx.universe
        +	if len(path) > 0 {
        +		universe = path[len(path)-1].ctx.(LicenseConditionSet)
        +	}
        +	return conditionsAttachingAcrossEdge(lg, edge, universe)
        +}
        +
        +// VisitNode is called for each root and for each walked dependency node by
        +// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
        +// down the dependences of the node
        +type VisitNode func(lg *LicenseGraph, target *TargetNode, path TargetEdgePath) bool
        +
        +// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
        +// into depenencies when `visit` returns true.
        +func WalkTopDown(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {
        +	path := NewTargetEdgePath(32)
        +
        +	var walk func(fnode *TargetNode)
        +	walk = func(fnode *TargetNode) {
        +		visitChildren := visit(lg, fnode, *path)
        +		if !visitChildren {
        +			return
        +		}
        +		for _, edge := range fnode.edges {
        +			var edgeContext interface{}
        +			if ctx == nil {
        +				edgeContext = nil
        +			} else {
        +				edgeContext = ctx.Context(lg, *path, edge)
        +			}
        +			path.Push(edge, edgeContext)
        +			walk(edge.dependency)
        +			path.Pop()
        +		}
        +	}
        +
        +	for _, r := range lg.rootFiles {
        +		path.Clear()
        +		walk(lg.targets[r])
        +	}
        +}
        +
        +// resolutionKey identifies results from walking a specific target for a
        +// specific set of conditions.
        +type resolutionKey struct {
        +	target *TargetNode
        +	cs     LicenseConditionSet
        +}
        +
        +// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
        +// resolving all distributed works for `conditions`.
        +func WalkResolutionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ResolutionSet {
        +	shipped := ShippedNodes(lg)
        +
        +	// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
        +	rmap := make(map[resolutionKey]ActionSet)
        +
        +	// cmap identifies previously walked target/condition pairs.
        +	cmap := make(map[resolutionKey]struct{})
        +
        +	// result accumulates the resolutions to return.
        +	result := make(ResolutionSet)
        +	WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        +		universe := conditions
        +		if len(path) > 0 {
        +			universe = path[len(path)-1].ctx.(LicenseConditionSet)
        +		}
        +
        +		if universe.IsEmpty() {
        +			return false
        +		}
        +		key := resolutionKey{tn, universe}
        +
        +		if _, alreadyWalked := cmap[key]; alreadyWalked {
        +			pure := true
        +			for _, p := range path {
        +				target := p.Target()
        +				tkey := resolutionKey{target, universe}
        +				if _, ok := rmap[tkey]; !ok {
        +					rmap[tkey] = make(ActionSet)
        +				}
        +				// attach prior walk outcome to ancestor
        +				for actsOn, cs := range rmap[key] {
        +					rmap[tkey][actsOn] = cs
        +				}
        +				// if prior walk produced results, copy results
        +				// to ancestor.
        +				if _, ok := result[tn]; ok && pure {
        +					if _, ok := result[target]; !ok {
        +						result[target] = make(ActionSet)
        +					}
        +					for actsOn, cs := range result[tn] {
        +						result[target][actsOn] = cs
        +					}
        +					pure = target.IsContainer()
        +				}
        +			}
        +			// if all ancestors are pure aggregates, attach
        +			// matching prior walk conditions to self. Prior walk
        +			// will not have done so if any ancestor was not an
        +			// aggregate.
        +			if pure {
        +				match := rmap[key][tn].Intersection(universe)
        +				if !match.IsEmpty() {
        +					if _, ok := result[tn]; !ok {
        +						result[tn] = make(ActionSet)
        +					}
        +					result[tn][tn] = match
        +				}
        +			}
        +			return false
        +		}
        +		// no need to walk node or dependencies if not shipped
        +		if !shipped.Contains(tn) {
        +			return false
        +		}
        +		if _, ok := rmap[key]; !ok {
        +			rmap[key] = make(ActionSet)
        +		}
        +		// add self to walk outcome
        +		rmap[key][tn] = tn.resolution
        +		cmap[key] = struct{}{}
        +		cs := tn.resolution
        +		if !cs.IsEmpty() {
        +			cs = cs.Intersection(universe)
        +			pure := true
        +			for _, p := range path {
        +				target := p.Target()
        +				tkey := resolutionKey{target, universe}
        +				if _, ok := rmap[tkey]; !ok {
        +					rmap[tkey] = make(ActionSet)
        +				}
        +				// copy current node's action into ancestor
        +				rmap[tkey][tn] = tn.resolution
        +				// conditionally put matching conditions into
        +				// result
        +				if pure && !cs.IsEmpty() {
        +					if _, ok := result[target]; !ok {
        +						result[target] = make(ActionSet)
        +					}
        +					result[target][tn] = cs
        +					pure = target.IsContainer()
        +				}
        +			}
        +			// if all ancestors are pure aggregates, attach
        +			// matching conditions to self.
        +			if pure && !cs.IsEmpty() {
        +				if _, ok := result[tn]; !ok {
        +					result[tn] = make(ActionSet)
        +				}
        +				result[tn][tn] = cs
        +			}
        +		}
        +		return true
        +	})
        +
        +	return result
        +}
        +
        +// WalkActionsForCondition performs a top-down walk of the LicenseGraph
        +// resolving all distributed works for `conditions`.
        +func WalkActionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ActionSet {
        +	shipped := ShippedNodes(lg)
        +
        +	// cmap identifies previously walked target/condition pairs.
        +	cmap := make(map[resolutionKey]struct{})
        +
        +	// amap maps 'actsOn' targets to the applicable conditions
        +	//
        +	// amap is the resulting ActionSet
        +	amap := make(ActionSet)
        +	WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        +		universe := conditions
        +		if len(path) > 0 {
        +			universe = path[len(path)-1].ctx.(LicenseConditionSet)
        +		}
        +		if universe.IsEmpty() {
        +			return false
        +		}
        +		key := resolutionKey{tn, universe}
        +		if _, ok := cmap[key]; ok {
        +			return false
        +		}
        +		if !shipped.Contains(tn) {
        +			return false
        +		}
        +		cs := universe.Intersection(tn.resolution)
        +		if !cs.IsEmpty() {
        +			if _, ok := amap[tn]; ok {
        +				amap[tn] = cs
        +			} else {
        +				amap[tn] = amap[tn].Union(cs)
        +			}
        +		}
        +		return true
        +	})
        +
        +	return amap
        +}
        diff --git a/make/tools/compliance/policy_walk_test.go b/make/tools/compliance/policy_walk_test.go
        new file mode 100644
        index 0000000..92867f9
        --- /dev/null
        +++ b/make/tools/compliance/policy_walk_test.go
        @@ -0,0 +1,1240 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"testing"
        +)
        +
        +func TestWalkResolutionsForCondition(t *testing.T) {
        +	tests := []struct {
        +		name                string
        +		condition           LicenseConditionSet
        +		roots               []string
        +		edges               []annotated
        +		expectedResolutions []res
        +	}{
        +		{
        +			name:      "firstparty",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "notice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "fponlgplnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "fponlgpldynamicnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulerestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:      "independentmodulestaticnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulestaticrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulerestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfpnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfprestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfpdynamicnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfpdynamicrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfprestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplcontainernotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplContainer.meta_lic"},
        +			edges: []annotated{
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplcontainerrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplContainer.meta_lic"},
        +			edges: []annotated{
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gploncontainernotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gploncontainerrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonbinnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonbinrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpdynamicnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpdynamicrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpdynamicrestrictedshipped",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereversenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereverserestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereverserestrictedshipped",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereversestaticnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereversestaticrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulereversenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulereverserestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulereverserestrictedshipped",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ponrnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ponrrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ponrproprietary",
        +			condition: ImpliesProprietary,
        +			roots:     []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +			},
        +		},
        +		{
        +			name:      "ronpnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +				{"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ronprestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ronpproprietary",
        +			condition: ImpliesProprietary,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +			},
        +		},
        +		{
        +			name:      "noticeonb_e_onotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +			},
        +		},
        +		{
        +			name:      "noticeonb_e_orestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:      "noticeonb_e_ob_e_o",
        +			condition: ImpliesByExceptionOnly,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +			},
        +		},
        +		{
        +			name:      "b_e_oonnoticenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +				{"by_exception.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "b_e_oonnoticerestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{},
        +		},
        +		{
        +			name:      "b_e_oonnoticeb_e_o",
        +			condition: ImpliesByExceptionOnly,
        +			roots:     []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +			},
        +		},
        +		{
        +			name:      "noticeonrecipnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +			},
        +		},
        +		{
        +			name:      "noticeonreciprecip",
        +			condition: ImpliesReciprocal,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +			},
        +		},
        +		{
        +			name:      "reciponnoticenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mplBin.meta_lic"},
        +			edges: []annotated{
        +				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        +				{"mplBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "reciponnoticerecip",
        +			condition: ImpliesReciprocal,
        +			roots:     []string{"mplBin.meta_lic"},
        +			edges: []annotated{
        +				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedResolutions: []res{
        +				{"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        +			ResolveTopDownConditions(lg)
        +			actualRs := WalkResolutionsForCondition(lg, tt.condition)
        +			checkResolves(actualRs, expectedRs, t)
        +		})
        +	}
        +}
        +
        +func TestWalkActionsForCondition(t *testing.T) {
        +	tests := []struct {
        +		name            string
        +		condition       LicenseConditionSet
        +		roots           []string
        +		edges           []annotated
        +		expectedActions []act
        +	}{
        +		{
        +			name:      "firstparty",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "notice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "fponlgplnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
        +				{"lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "fponlgpldynamicnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulerestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{},
        +		},
        +		{
        +			name:      "independentmodulestaticnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulestaticrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulerestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfpnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfprestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfpdynamicnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "lgplonfpdynamicrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"lgplBin.meta_lic"},
        +			edges: []annotated{
        +				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfprestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplcontainernotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplContainer.meta_lic"},
        +			edges: []annotated{
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplcontainerrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplContainer.meta_lic"},
        +			edges: []annotated{
        +				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gploncontainernotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gploncontainerrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheContainer.meta_lic"},
        +			edges: []annotated{
        +				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonbinnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        +				{"apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonbinrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpdynamicnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpdynamicrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "gplonfpdynamicrestrictedshipped",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereversenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereverserestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereverserestrictedshipped",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereversestaticnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "independentmodulereversestaticrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulereversenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulereverserestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "dependentmodulereverserestrictedshipped",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
        +			edges: []annotated{
        +				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        +			},
        +			expectedActions: []act{
        +				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ponrnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ponrrestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        +				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ponrproprietary",
        +			condition: ImpliesProprietary,
        +			roots:     []string{"proprietary.meta_lic"},
        +			edges: []annotated{
        +				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +			},
        +		},
        +		{
        +			name:      "ronpnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +				{"proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ronprestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        +				{"proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
        +			},
        +		},
        +		{
        +			name:      "ronpproprietary",
        +			condition: ImpliesProprietary,
        +			roots:     []string{"gplBin.meta_lic"},
        +			edges: []annotated{
        +				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        +			},
        +		},
        +		{
        +			name:      "noticeonb_e_onotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +			},
        +		},
        +		{
        +			name:      "noticeonb_e_orestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{},
        +		},
        +		{
        +			name:      "noticeonb_e_ob_e_o",
        +			condition: ImpliesByExceptionOnly,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +			},
        +		},
        +		{
        +			name:      "b_e_oonnoticenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +				{"mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "b_e_oonnoticerestricted",
        +			condition: ImpliesRestricted,
        +			roots:     []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{},
        +		},
        +		{
        +			name:      "b_e_oonnoticeb_e_o",
        +			condition: ImpliesByExceptionOnly,
        +			roots:     []string{"by_exception.meta_lic"},
        +			edges: []annotated{
        +				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        +			},
        +		},
        +		{
        +			name:      "noticeonrecipnotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        +				{"mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +			},
        +		},
        +		{
        +			name:      "noticeonreciprecip",
        +			condition: ImpliesReciprocal,
        +			roots:     []string{"mitBin.meta_lic"},
        +			edges: []annotated{
        +				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
        +			},
        +		},
        +		{
        +			name:      "reciponnoticenotice",
        +			condition: ImpliesNotice,
        +			roots:     []string{"mplBin.meta_lic"},
        +			edges: []annotated{
        +				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        +				{"mitLib.meta_lic", "mitLib.meta_lic", "notice"},
        +			},
        +		},
        +		{
        +			name:      "reciponnoticerecip",
        +			condition: ImpliesReciprocal,
        +			roots:     []string{"mplBin.meta_lic"},
        +			edges: []annotated{
        +				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        +			},
        +			expectedActions: []act{
        +				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        +			},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := toGraph(stderr, tt.roots, tt.edges)
        +			if err != nil {
        +				t.Errorf("unexpected test data error: got %s, want no error", err)
        +				return
        +			}
        +			expectedAs := toActionSet(lg, tt.expectedActions)
        +			ResolveTopDownConditions(lg)
        +			actualAs := WalkActionsForCondition(lg, tt.condition)
        +			checkResolvesActions(lg, actualAs, expectedAs, t)
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/readgraph.go b/make/tools/compliance/readgraph.go
        new file mode 100644
        index 0000000..7516440
        --- /dev/null
        +++ b/make/tools/compliance/readgraph.go
        @@ -0,0 +1,285 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"os"
        +	"strings"
        +	"sync"
        +
        +	"android/soong/compliance/license_metadata_proto"
        +
        +	"google.golang.org/protobuf/encoding/prototext"
        +)
        +
        +var (
        +	// ConcurrentReaders is the size of the task pool for limiting resource usage e.g. open files.
        +	ConcurrentReaders = 5
        +)
        +
        +type globalFS struct{}
        +
        +func (s globalFS) Open(name string) (fs.File, error) {
        +	return os.Open(name)
        +}
        +
        +var FS globalFS
        +
        +// GetFS returns a filesystem for accessing files under the OUT_DIR environment variable.
        +func GetFS(outDir string) fs.FS {
        +	if len(outDir) > 0 {
        +		return os.DirFS(outDir)
        +	}
        +	return os.DirFS(".")
        +}
        +
        +// result describes the outcome of reading and parsing a single license metadata file.
        +type result struct {
        +	// file identifies the path to the license metadata file
        +	file string
        +
        +	// target contains the parsed metadata or nil if an error
        +	target *TargetNode
        +
        +	// err is nil unless an error occurs
        +	err error
        +}
        +
        +// receiver coordinates the tasks for reading and parsing license metadata files.
        +type receiver struct {
        +	// lg accumulates the read metadata and becomes the final resulting LicenseGraph.
        +	lg *LicenseGraph
        +
        +	// rootFS locates the root of the file system from which to read the files.
        +	rootFS fs.FS
        +
        +	// stderr identifies the error output writer.
        +	stderr io.Writer
        +
        +	// task provides a fixed-size task pool to limit concurrent open files etc.
        +	task chan bool
        +
        +	// results returns one license metadata file result at a time.
        +	results chan *result
        +
        +	// wg detects when done
        +	wg sync.WaitGroup
        +}
        +
        +// ReadLicenseGraph reads and parses `files` and their dependencies into a LicenseGraph.
        +//
        +// `files` become the root files of the graph for top-down walks of the graph.
        +func ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseGraph, error) {
        +	if len(files) == 0 {
        +		return nil, fmt.Errorf("no license metadata to analyze")
        +	}
        +	if ConcurrentReaders < 1 {
        +		return nil, fmt.Errorf("need at least one task in pool")
        +	}
        +
        +	lg := newLicenseGraph()
        +	for _, f := range files {
        +		if strings.HasSuffix(f, "meta_lic") {
        +			lg.rootFiles = append(lg.rootFiles, f)
        +		} else {
        +			lg.rootFiles = append(lg.rootFiles, f+".meta_lic")
        +		}
        +	}
        +
        +	recv := &receiver{
        +		lg:      lg,
        +		rootFS:  rootFS,
        +		stderr:  stderr,
        +		task:    make(chan bool, ConcurrentReaders),
        +		results: make(chan *result, ConcurrentReaders),
        +		wg:      sync.WaitGroup{},
        +	}
        +	for i := 0; i < ConcurrentReaders; i++ {
        +		recv.task <- true
        +	}
        +
        +	readFiles := func() {
        +		lg.mu.Lock()
        +		// identify the metadata files to schedule reading tasks for
        +		for _, f := range lg.rootFiles {
        +			lg.targets[f] = nil
        +		}
        +		lg.mu.Unlock()
        +
        +		// schedule tasks to read the files
        +		for _, f := range lg.rootFiles {
        +			readFile(recv, f)
        +		}
        +
        +		// schedule a task to wait until finished and close the channel.
        +		go func() {
        +			recv.wg.Wait()
        +			close(recv.task)
        +			close(recv.results)
        +		}()
        +	}
        +	go readFiles()
        +
        +	// tasks to read license metadata files are scheduled; read and process results from channel
        +	var err error
        +	for recv.results != nil {
        +		select {
        +		case r, ok := <-recv.results:
        +			if ok {
        +				// handle errors by nil'ing ls, setting err, and clobbering results channel
        +				if r.err != nil {
        +					err = r.err
        +					fmt.Fprintf(recv.stderr, "%s\n", err.Error())
        +					lg = nil
        +					recv.results = nil
        +					continue
        +				}
        +
        +				// record the parsed metadata (guarded by mutex)
        +				recv.lg.mu.Lock()
        +				lg.targets[r.target.name] = r.target
        +				recv.lg.mu.Unlock()
        +			} else {
        +				// finished -- nil the results channel
        +				recv.results = nil
        +			}
        +		}
        +	}
        +
        +	if lg != nil {
        +		esize := 0
        +		for _, tn := range lg.targets {
        +			esize += len(tn.proto.Deps)
        +		}
        +		lg.edges = make(TargetEdgeList, 0, esize)
        +		for _, tn := range lg.targets {
        +			tn.licenseConditions = LicenseConditionSetFromNames(tn, tn.proto.LicenseConditions...)
        +			err = addDependencies(lg, tn)
        +			if err != nil {
        +				return nil, fmt.Errorf("error indexing dependencies for %q: %w", tn.name, err)
        +			}
        +			tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
        +		}
        +	}
        +	return lg, err
        +
        +}
        +
        +// targetNode contains the license metadata for a node in the license graph.
        +type targetNode struct {
        +	proto license_metadata_proto.LicenseMetadata
        +
        +	// name is the path to the metadata file.
        +	name string
        +
        +	// lg is the license graph the node belongs to.
        +	lg *LicenseGraph
        +
        +	// edges identifies the dependencies of the target.
        +	edges TargetEdgeList
        +
        +	// licenseConditions identifies the set of license conditions originating at the target node.
        +	licenseConditions LicenseConditionSet
        +
        +	// resolution identifies the set of conditions resolved by acting on the target node.
        +	resolution LicenseConditionSet
        +}
        +
        +// addDependencies converts the proto AnnotatedDependencies into `edges`
        +func addDependencies(lg *LicenseGraph, tn *TargetNode) error {
        +	tn.edges = make(TargetEdgeList, 0, len(tn.proto.Deps))
        +	for _, ad := range tn.proto.Deps {
        +		dependency := ad.GetFile()
        +		if len(dependency) == 0 {
        +			return fmt.Errorf("missing dependency name")
        +		}
        +		dtn, ok := lg.targets[dependency]
        +		if !ok {
        +			return fmt.Errorf("unknown dependency name %q", dependency)
        +		}
        +		if dtn == nil {
        +			return fmt.Errorf("nil dependency for name %q", dependency)
        +		}
        +		annotations := newEdgeAnnotations()
        +		for _, a := range ad.Annotations {
        +			// look up a common constant annotation string from a small map
        +			// instead of creating 1000's of copies of the same 3 strings.
        +			if ann, ok := RecognizedAnnotations[a]; ok {
        +				annotations.annotations[ann] = struct{}{}
        +			}
        +		}
        +		edge := &TargetEdge{tn, dtn, annotations}
        +		lg.edges = append(lg.edges, edge)
        +		tn.edges = append(tn.edges, edge)
        +	}
        +	return nil
        +}
        +
        +// readFile is a task to read and parse a single license metadata file, and to schedule
        +// additional tasks for reading and parsing dependencies as necessary.
        +func readFile(recv *receiver, file string) {
        +	recv.wg.Add(1)
        +	<-recv.task
        +	go func() {
        +		f, err := recv.rootFS.Open(file)
        +		if err != nil {
        +			recv.results <- &result{file, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
        +			return
        +		}
        +
        +		// read the file
        +		data, err := io.ReadAll(f)
        +		if err != nil {
        +			recv.results <- &result{file, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
        +			return
        +		}
        +		f.Close()
        +
        +		tn := &TargetNode{lg: recv.lg, name: file}
        +
        +		err = prototext.Unmarshal(data, &tn.proto)
        +		if err != nil {
        +			recv.results <- &result{file, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
        +			return
        +		}
        +
        +		// send result for this file and release task before scheduling dependencies,
        +		// but do not signal done to WaitGroup until dependencies are scheduled.
        +		recv.results <- &result{file, tn, nil}
        +		recv.task <- true
        +
        +		// schedule tasks as necessary to read dependencies
        +		for _, ad := range tn.proto.Deps {
        +			dependency := ad.GetFile()
        +			// decide, signal and record whether to schedule task in critical section
        +			recv.lg.mu.Lock()
        +			_, alreadyScheduled := recv.lg.targets[dependency]
        +			if !alreadyScheduled {
        +				recv.lg.targets[dependency] = nil
        +			}
        +			recv.lg.mu.Unlock()
        +			// schedule task to read dependency file outside critical section
        +			if !alreadyScheduled {
        +				readFile(recv, dependency)
        +			}
        +		}
        +
        +		// signal task done after scheduling dependencies
        +		recv.wg.Done()
        +	}()
        +}
        diff --git a/make/tools/compliance/readgraph_test.go b/make/tools/compliance/readgraph_test.go
        new file mode 100644
        index 0000000..bcf9f39
        --- /dev/null
        +++ b/make/tools/compliance/readgraph_test.go
        @@ -0,0 +1,150 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"bytes"
        +	"sort"
        +	"strings"
        +	"testing"
        +)
        +
        +func TestReadLicenseGraph(t *testing.T) {
        +	tests := []struct {
        +		name            string
        +		fs              *testFS
        +		roots           []string
        +		expectedError   string
        +		expectedEdges   []edge
        +		expectedTargets []string
        +	}{
        +		{
        +			name: "trivial",
        +			fs: &testFS{
        +				"app.meta_lic": []byte("package_name: \"Android\"\n"),
        +			},
        +			roots:           []string{"app.meta_lic"},
        +			expectedEdges:   []edge{},
        +			expectedTargets: []string{"app.meta_lic"},
        +		},
        +		{
        +			name: "unterminated",
        +			fs: &testFS{
        +				"app.meta_lic": []byte("package_name: \"Android\n"),
        +			},
        +			roots:         []string{"app.meta_lic"},
        +			expectedError: `invalid character '\n' in string`,
        +		},
        +		{
        +			name: "danglingref",
        +			fs: &testFS{
        +				"app.meta_lic": []byte(AOSP + "deps: {\n  file: \"lib.meta_lic\"\n}\n"),
        +			},
        +			roots:         []string{"app.meta_lic"},
        +			expectedError: `unknown file "lib.meta_lic"`,
        +		},
        +		{
        +			name: "singleedge",
        +			fs: &testFS{
        +				"app.meta_lic": []byte(AOSP + "deps: {\n  file: \"lib.meta_lic\"\n}\n"),
        +				"lib.meta_lic": []byte(AOSP),
        +			},
        +			roots:           []string{"app.meta_lic"},
        +			expectedEdges:   []edge{{"app.meta_lic", "lib.meta_lic"}},
        +			expectedTargets: []string{"app.meta_lic", "lib.meta_lic"},
        +		},
        +		{
        +			name: "fullgraph",
        +			fs: &testFS{
        +				"apex.meta_lic": []byte(AOSP + "deps: {\n  file: \"app.meta_lic\"\n}\ndeps: {\n  file: \"bin.meta_lic\"\n}\n"),
        +				"app.meta_lic":  []byte(AOSP),
        +				"bin.meta_lic":  []byte(AOSP + "deps: {\n  file: \"lib.meta_lic\"\n}\n"),
        +				"lib.meta_lic":  []byte(AOSP),
        +			},
        +			roots: []string{"apex.meta_lic"},
        +			expectedEdges: []edge{
        +				{"apex.meta_lic", "app.meta_lic"},
        +				{"apex.meta_lic", "bin.meta_lic"},
        +				{"bin.meta_lic", "lib.meta_lic"},
        +			},
        +			expectedTargets: []string{"apex.meta_lic", "app.meta_lic", "bin.meta_lic", "lib.meta_lic"},
        +		},
        +	}
        +	for _, tt := range tests {
        +		t.Run(tt.name, func(t *testing.T) {
        +			stderr := &bytes.Buffer{}
        +			lg, err := ReadLicenseGraph(tt.fs, stderr, tt.roots)
        +			if err != nil {
        +				if len(tt.expectedError) == 0 {
        +					t.Errorf("unexpected error: got %s, want no error", err)
        +				} else if !strings.Contains(err.Error(), tt.expectedError) {
        +					t.Errorf("unexpected error: got %s, want %q", err, tt.expectedError)
        +				}
        +				return
        +			}
        +			if len(tt.expectedError) > 0 {
        +				t.Errorf("unexpected success: got no error, want %q err", tt.expectedError)
        +				return
        +			}
        +			if lg == nil {
        +				t.Errorf("missing license graph: got nil, want license graph")
        +				return
        +			}
        +			actualEdges := make([]edge, 0)
        +			for _, e := range lg.Edges() {
        +				actualEdges = append(actualEdges, edge{e.Target().Name(), e.Dependency().Name()})
        +			}
        +			sort.Sort(byEdge(tt.expectedEdges))
        +			sort.Sort(byEdge(actualEdges))
        +			t.Logf("actualEdges:")
        +			for _, edge := range actualEdges {
        +				t.Logf("  %s", edge.String())
        +			}
        +			t.Logf("expectedEdges:")
        +			for _, edge := range actualEdges {
        +				t.Logf("  %s", edge.String())
        +			}
        +			if len(tt.expectedEdges) != len(actualEdges) {
        +				t.Errorf("len(actualEdges): got %d, want %d", len(actualEdges), len(tt.expectedEdges))
        +			} else {
        +				for i := 0; i < len(actualEdges); i++ {
        +					if tt.expectedEdges[i] != actualEdges[i] {
        +						t.Errorf("actualEdges[%d]: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
        +					}
        +				}
        +			}
        +
        +			actualTargets := make([]string, 0)
        +			for _, t := range lg.Targets() {
        +				actualTargets = append(actualTargets, t.Name())
        +			}
        +			sort.Strings(tt.expectedTargets)
        +			sort.Strings(actualTargets)
        +
        +			t.Logf("actualTargets: %v", actualTargets)
        +			t.Logf("expectedTargets: %v", tt.expectedTargets)
        +
        +			if len(tt.expectedTargets) != len(actualTargets) {
        +				t.Errorf("len(actualTargets): got %d, want %d", len(actualTargets), len(tt.expectedTargets))
        +			} else {
        +				for i := 0; i < len(actualTargets); i++ {
        +					if tt.expectedTargets[i] != actualTargets[i] {
        +						t.Errorf("actualTargets[%d]: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
        +					}
        +				}
        +			}
        +		})
        +	}
        +}
        diff --git a/make/tools/compliance/resolution.go b/make/tools/compliance/resolution.go
        new file mode 100644
        index 0000000..acc61e2
        --- /dev/null
        +++ b/make/tools/compliance/resolution.go
        @@ -0,0 +1,149 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +	"sort"
        +	"strings"
        +)
        +
        +// Resolution describes an action to resolve one or more license conditions.
        +//
        +// `AttachesTo` identifies the target node that when distributed triggers the action.
        +// `ActsOn` identifies the target node that is the object of the action.
        +// `Resolves` identifies one or more license conditions that the action resolves.
        +//
        +// e.g. Suppose an MIT library is linked to a binary that also links to GPL code.
        +//
        +// A resolution would attach to the binary to share (act on) the MIT library to
        +// resolve the restricted condition originating from the GPL code.
        +type Resolution struct {
        +	attachesTo, actsOn *TargetNode
        +	cs                 LicenseConditionSet
        +}
        +
        +// AttachesTo returns the target node the resolution attaches to.
        +func (r Resolution) AttachesTo() *TargetNode {
        +	return r.attachesTo
        +}
        +
        +// ActsOn returns the target node that must be acted on to resolve the condition.
        +//
        +// i.e. The node for which notice must be given or whose source must be shared etc.
        +func (r Resolution) ActsOn() *TargetNode {
        +	return r.actsOn
        +}
        +
        +// Resolves returns the set of license condition the resolution satisfies.
        +func (r Resolution) Resolves() LicenseConditionSet {
        +	return r.cs
        +}
        +
        +// asString returns a string representation of the resolution.
        +func (r Resolution) asString() string {
        +	var sb strings.Builder
        +	names := r.cs.Names()
        +	sort.Strings(names)
        +	fmt.Fprintf(&sb, "%s -> %s{%s}", r.attachesTo.name, r.actsOn.name, strings.Join(names, ", "))
        +	return sb.String()
        +}
        +
        +// ResolutionList represents a partial order of Resolutions ordered by
        +// AttachesTo() and ActsOn() leaving `Resolves()` unordered.
        +type ResolutionList []Resolution
        +
        +// Len returns the count of elements in the list.
        +func (l ResolutionList) Len() int { return len(l) }
        +
        +// Swap rearranges 2 elements so that each occupies the other's former position.
        +func (l ResolutionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +
        +// Less returns true when the `i`th element is lexicographically less than tht `j`th.
        +func (l ResolutionList) Less(i, j int) bool {
        +	if l[i].attachesTo.name == l[j].attachesTo.name {
        +		return l[i].actsOn.name < l[j].actsOn.name
        +	}
        +	return l[i].attachesTo.name < l[j].attachesTo.name
        +}
        +
        +// String returns a string representation of the list.
        +func (rl ResolutionList) String() string {
        +	var sb strings.Builder
        +	fmt.Fprintf(&sb, "[")
        +	sep := ""
        +	for _, r := range rl {
        +		fmt.Fprintf(&sb, "%s%s", sep, r.asString())
        +		sep = ", "
        +	}
        +	fmt.Fprintf(&sb, "]")
        +	return sb.String()
        +}
        +
        +// AllConditions returns the union of all license conditions resolved by any
        +// element of the list.
        +func (rl ResolutionList) AllConditions() LicenseConditionSet {
        +	result := NewLicenseConditionSet()
        +	for _, r := range rl {
        +		result = result.Union(r.cs)
        +	}
        +	return result
        +}
        +
        +// ByName returns the sub-list of resolutions resolving conditions matching
        +// `names`.
        +func (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList {
        +	result := make(ResolutionList, 0, rl.CountMatching(conditions))
        +	for _, r := range rl {
        +		if r.Resolves().MatchesAnySet(conditions) {
        +			result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)})
        +		}
        +	}
        +	return result
        +}
        +
        +// CountMatching returns the number of resolutions resolving conditions matching
        +// `conditions`.
        +func (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int {
        +	c := 0
        +	for _, r := range rl {
        +		if r.Resolves().MatchesAnySet(conditions) {
        +			c++
        +		}
        +	}
        +	return c
        +}
        +
        +// ByActsOn returns the sub-list of resolutions matching `actsOn`.
        +func (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList {
        +	result := make(ResolutionList, 0, rl.CountByActsOn(actsOn))
        +	for _, r := range rl {
        +		if r.actsOn == actsOn {
        +			result = append(result, r)
        +		}
        +	}
        +	return result
        +}
        +
        +// CountByActsOn returns the number of resolutions matching `actsOn`.
        +func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int {
        +	c := 0
        +	for _, r := range rl {
        +		if r.actsOn == actsOn {
        +			c++
        +		}
        +	}
        +	return c
        +}
        diff --git a/make/tools/compliance/resolutionset.go b/make/tools/compliance/resolutionset.go
        new file mode 100644
        index 0000000..7c8f333
        --- /dev/null
        +++ b/make/tools/compliance/resolutionset.go
        @@ -0,0 +1,133 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +	"strings"
        +)
        +
        +// ResolutionSet describes an immutable set of targets and the license
        +// conditions each target must satisfy or "resolve" in a specific context.
        +//
        +// Ultimately, the purpose of recording the license metadata and building a
        +// license graph is to identify, describe, and verify the necessary actions or
        +// operations for compliance policy.
        +//
        +// i.e. What is the source-sharing policy? Has it been met? Meet it.
        +//
        +// i.e. Are there incompatible policy requirements? Such as a source-sharing
        +// policy applied to code that policy also says may not be shared? If so, stop
        +// and remove the dependencies that create the situation.
        +//
        +// The ResolutionSet is the base unit for mapping license conditions to the
        +// targets triggering some necessary action per policy. Different ResolutionSet
        +// values may be calculated for different contexts.
        +//
        +// e.g. Suppose an unencumbered binary links in a notice .a library.
        +//
        +// An "unencumbered" condition would originate from the binary, and a "notice"
        +// condition would originate from the .a library. A ResolutionSet for the
        +// context of the Notice policy might attach both conditions to the binary to
        +// act on the origin of each condition. By attaching the notice condition to
        +// the binary, the ResolutionSet stipulates the policy that the release of the
        +// unencumbered binary must provide suitable notice for the .a library.
        +//
        +// The resulting ResolutionSet could be used for building a notice file, for
        +// validating that a suitable notice has been built into the distribution, or
        +// for reporting what notices need to be given.
        +//
        +// The action is defined by the context. In the above example, the action is
        +// providing notice for the module acted on. In another context, the action
        +// might be sharing the source-code or preserving the privacy of the module
        +// acted on.
        +type ResolutionSet map[*TargetNode]ActionSet
        +
        +// AttachesTo identifies the list of targets triggering action to resolve
        +// conditions. (unordered)
        +func (rs ResolutionSet) AttachesTo() TargetNodeList {
        +	result := make(TargetNodeList, 0, len(rs))
        +	for attachesTo := range rs {
        +		result = append(result, attachesTo)
        +	}
        +	return result
        +}
        +
        +// AttachesToTarget returns true if the set contains conditions that
        +// are `attachedTo`.
        +func (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool {
        +	_, isPresent := rs[target]
        +	return isPresent
        +}
        +
        +// Resolutions returns the list of resolutions that `attachedTo`
        +// target must resolve. Returns empty list if no conditions apply.
        +func (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList {
        +	as, ok := rs[attachesTo]
        +	if !ok {
        +		return nil
        +	}
        +	result := make(ResolutionList, 0, len(as))
        +	for actsOn, cs := range as {
        +		result = append(result, Resolution{attachesTo, actsOn, cs})
        +	}
        +	return result
        +}
        +
        +// AllActions returns the set of actions required to resolve the set omitting
        +// the attachment.
        +func (rs ResolutionSet) AllActions() ActionSet {
        +	result := make(ActionSet)
        +	for _, as := range rs {
        +		for actsOn, cs := range as {
        +			if _, ok := result[actsOn]; ok {
        +				result[actsOn] = cs.Union(result[actsOn])
        +			} else {
        +				result[actsOn] = cs
        +			}
        +		}
        +	}
        +	return result
        +}
        +
        +// String returns a human-readable string representation of the set.
        +func (rs ResolutionSet) String() string {
        +	var sb strings.Builder
        +	fmt.Fprintf(&sb, "{")
        +	sep := ""
        +	for attachesTo, as := range rs {
        +		fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.Name(), as.String())
        +		sep = ", "
        +	}
        +	fmt.Fprintf(&sb, "}")
        +	return sb.String()
        +}
        +
        +// ActionSet identifies a set of targets to act on and the license conditions
        +// the action will resolve.
        +type ActionSet map[*TargetNode]LicenseConditionSet
        +
        +// String returns a human-readable string representation of the set.
        +func (as ActionSet) String() string {
        +	var sb strings.Builder
        +	fmt.Fprintf(&sb, "{")
        +	sep := ""
        +	for actsOn, cs := range as {
        +		fmt.Fprintf(&sb, "%s%s%s", sep, actsOn.Name(), cs.String())
        +		sep = ", "
        +	}
        +	fmt.Fprintf(&sb, "}")
        +	return sb.String()
        +}
        diff --git a/make/tools/compliance/resolutionset_test.go b/make/tools/compliance/resolutionset_test.go
        new file mode 100644
        index 0000000..89cdfeb
        --- /dev/null
        +++ b/make/tools/compliance/resolutionset_test.go
        @@ -0,0 +1,136 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"sort"
        +	"testing"
        +)
        +
        +var (
        +	// bottomUp describes the bottom-up resolve of a hypothetical graph
        +	// the graph has a container image, a couple binaries, and a couple
        +	// libraries. bin1 statically links lib1 and dynamically links lib2;
        +	// bin2 dynamically links lib1 and statically links lib2.
        +	// binc represents a compiler or other toolchain binary used for
        +	// building the other binaries.
        +	bottomUp = []res{
        +		{"image", "image", "image", "notice"},
        +		{"image", "image", "bin2", "restricted"},
        +		{"image", "bin1", "bin1", "reciprocal"},
        +		{"image", "bin2", "bin2", "restricted"},
        +		{"image", "lib1", "lib1", "notice"},
        +		{"image", "lib2", "lib2", "notice"},
        +		{"binc", "binc", "binc", "proprietary"},
        +		{"bin1", "bin1", "bin1", "reciprocal"},
        +		{"bin1", "lib1", "lib1", "notice"},
        +		{"bin2", "bin2", "bin2", "restricted"},
        +		{"bin2", "lib2", "lib2", "notice"},
        +		{"lib1", "lib1", "lib1", "notice"},
        +		{"lib2", "lib2", "lib2", "notice"},
        +	}
        +
        +	// notice describes bottomUp after a top-down notice resolve.
        +	notice = []res{
        +		{"image", "image", "image", "notice"},
        +		{"image", "image", "bin2", "restricted"},
        +		{"image", "bin1", "bin1", "reciprocal"},
        +		{"image", "bin2", "bin2", "restricted"},
        +		{"image", "lib1", "lib1", "notice"},
        +		{"image", "lib2", "bin2", "restricted"},
        +		{"image", "lib2", "lib2", "notice"},
        +		{"bin1", "bin1", "bin1", "reciprocal"},
        +		{"bin1", "lib1", "lib1", "notice"},
        +		{"bin2", "bin2", "bin2", "restricted"},
        +		{"bin2", "lib2", "bin2", "restricted"},
        +		{"bin2", "lib2", "lib2", "notice"},
        +		{"lib1", "lib1", "lib1", "notice"},
        +		{"lib2", "lib2", "lib2", "notice"},
        +	}
        +
        +	// share describes bottomUp after a top-down share resolve.
        +	share = []res{
        +		{"image", "image", "bin2", "restricted"},
        +		{"image", "bin1", "bin1", "reciprocal"},
        +		{"image", "bin2", "bin2", "restricted"},
        +		{"image", "lib2", "bin2", "restricted"},
        +		{"bin1", "bin1", "bin1", "reciprocal"},
        +		{"bin2", "bin2", "bin2", "restricted"},
        +		{"bin2", "lib2", "bin2", "restricted"},
        +	}
        +
        +	// proprietary describes bottomUp after a top-down proprietary resolve.
        +	// Note that the proprietary binc is not reachable through the toolchain
        +	// dependency.
        +	proprietary = []res{}
        +)
        +
        +func TestResolutionSet_AttachesTo(t *testing.T) {
        +	lg := newLicenseGraph()
        +
        +	rsShare := toResolutionSet(lg, share)
        +
        +	t.Logf("checking resolution set %s", rsShare.String())
        +
        +	actual := rsShare.AttachesTo().Names()
        +	sort.Strings(actual)
        +
        +	expected := []string{"bin1", "bin2", "image"}
        +
        +	t.Logf("actual rsShare: %v", actual)
        +	t.Logf("expected rsShare: %v", expected)
        +
        +	if len(actual) != len(expected) {
        +		t.Errorf("rsShare: wrong number of targets: got %d, want %d", len(actual), len(expected))
        +		return
        +	}
        +	for i := 0; i < len(actual); i++ {
        +		if actual[i] != expected[i] {
        +			t.Errorf("rsShare: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
        +		}
        +	}
        +
        +	rsPrivate := toResolutionSet(lg, proprietary)
        +	actual = rsPrivate.AttachesTo().Names()
        +	expected = []string{}
        +
        +	t.Logf("actual rsPrivate: %v", actual)
        +	t.Logf("expected rsPrivate: %v", expected)
        +
        +	if len(actual) != len(expected) {
        +		t.Errorf("rsPrivate: wrong number of targets: got %d, want %d", len(actual), len(expected))
        +		return
        +	}
        +	for i := 0; i < len(actual); i++ {
        +		if actual[i] != expected[i] {
        +			t.Errorf("rsPrivate: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
        +		}
        +	}
        +}
        +
        +func TestResolutionSet_AttachesToTarget(t *testing.T) {
        +	lg := newLicenseGraph()
        +
        +	rsShare := toResolutionSet(lg, share)
        +
        +	t.Logf("checking resolution set %s", rsShare.String())
        +
        +	if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
        +		t.Errorf("actual.AttachesToTarget(\"binc\"): got true, want false")
        +	}
        +	if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
        +		t.Errorf("actual.AttachesToTarget(\"image\"): got false want true")
        +	}
        +}
        diff --git a/make/tools/compliance/test_util.go b/make/tools/compliance/test_util.go
        new file mode 100644
        index 0000000..26d7461
        --- /dev/null
        +++ b/make/tools/compliance/test_util.go
        @@ -0,0 +1,603 @@
        +// Copyright 2021 Google LLC
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package compliance
        +
        +import (
        +	"fmt"
        +	"io"
        +	"io/fs"
        +	"sort"
        +	"strings"
        +	"testing"
        +)
        +
        +const (
        +	// AOSP starts a test metadata file for Android Apache-2.0 licensing.
        +	AOSP = `` +
        +		`package_name: "Android"
        +license_kinds: "SPDX-license-identifier-Apache-2.0"
        +license_conditions: "notice"
        +`
        +
        +	// GPL starts a test metadata file for GPL 2.0 licensing.
        +	GPL = `` +
        +		`package_name: "Free Software"
        +license_kinds: "SPDX-license-identifier-GPL-2.0"
        +license_conditions: "restricted"
        +`
        +
        +	// Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing.
        +	Classpath = `` +
        +		`package_name: "Free Software"
        +license_kinds: "SPDX-license-identifier-GPL-2.0-with-classpath-exception"
        +license_conditions: "restricted"
        +`
        +
        +	// DependentModule starts a test metadata file for a module in the same package as `Classpath`.
        +	DependentModule = `` +
        +		`package_name: "Free Software"
        +license_kinds: "SPDX-license-identifier-MIT"
        +license_conditions: "notice"
        +`
        +
        +	// LGPL starts a test metadata file for a module with LGPL 2.0 licensing.
        +	LGPL = `` +
        +		`package_name: "Free Library"
        +license_kinds: "SPDX-license-identifier-LGPL-2.0"
        +license_conditions: "restricted"
        +`
        +
        +	// MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.
        +	MPL = `` +
        +		`package_name: "Reciprocal"
        +license_kinds: "SPDX-license-identifier-MPL-2.0"
        +license_conditions: "reciprocal"
        +`
        +
        +	// MIT starts a test metadata file for a module with generic notice (MIT) licensing.
        +	MIT = `` +
        +		`package_name: "Android"
        +license_kinds: "SPDX-license-identifier-MIT"
        +license_conditions: "notice"
        +`
        +
        +	// Proprietary starts a test metadata file for a module with proprietary licensing.
        +	Proprietary = `` +
        +		`package_name: "Android"
        +license_kinds: "legacy_proprietary"
        +license_conditions: "proprietary"
        +`
        +
        +	// ByException starts a test metadata file for a module with by_exception_only licensing.
        +	ByException = `` +
        +		`package_name: "Special"
        +license_kinds: "legacy_by_exception_only"
        +license_conditions: "by_exception_only"
        +`
        +)
        +
        +var (
        +	// meta maps test file names to metadata file content without dependencies.
        +	meta = map[string]string{
        +		"apacheBin.meta_lic":                 AOSP,
        +		"apacheLib.meta_lic":                 AOSP,
        +		"apacheContainer.meta_lic":           AOSP + "is_container: true\n",
        +		"dependentModule.meta_lic":           DependentModule,
        +		"gplWithClasspathException.meta_lic": Classpath,
        +		"gplBin.meta_lic":                    GPL,
        +		"gplLib.meta_lic":                    GPL,
        +		"gplContainer.meta_lic":              GPL + "is_container: true\n",
        +		"lgplBin.meta_lic":                   LGPL,
        +		"lgplLib.meta_lic":                   LGPL,
        +		"mitBin.meta_lic":                    MIT,
        +		"mitLib.meta_lic":                    MIT,
        +		"mplBin.meta_lic":                    MPL,
        +		"mplLib.meta_lic":                    MPL,
        +		"proprietary.meta_lic":               Proprietary,
        +		"by_exception.meta_lic":              ByException,
        +	}
        +)
        +
        +// newTestNode constructs a test node in the license graph.
        +func newTestNode(lg *LicenseGraph, targetName string) *TargetNode {
        +	if tn, alreadyExists := lg.targets[targetName]; alreadyExists {
        +		return tn
        +	}
        +	tn := &TargetNode{name: targetName}
        +	lg.targets[targetName] = tn
        +	return tn
        +}
        +
        +// newTestCondition constructs a test license condition in the license graph.
        +func newTestCondition(lg *LicenseGraph, targetName string, conditionName string) LicenseCondition {
        +	tn := newTestNode(lg, targetName)
        +	cl := LicenseConditionSetFromNames(tn, conditionName).AsList()
        +	if len(cl) == 0 {
        +		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
        +	} else if len(cl) != 1 {
        +		panic(fmt.Errorf("unexpected multiple conditions from condition name: %q: got %d, want 1", conditionName, len(cl)))
        +	}
        +	lc := cl[0]
        +	tn.licenseConditions = tn.licenseConditions.Plus(lc)
        +	return lc
        +}
        +
        +// newTestConditionSet constructs a test license condition set in the license graph.
        +func newTestConditionSet(lg *LicenseGraph, targetName string, conditionName []string) LicenseConditionSet {
        +	tn := newTestNode(lg, targetName)
        +	cs := LicenseConditionSetFromNames(tn, conditionName...)
        +	if cs.IsEmpty() {
        +		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
        +	}
        +	tn.licenseConditions = tn.licenseConditions.Union(cs)
        +	return cs
        +}
        +
        +// testFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.
        +type testFS map[string][]byte
        +
        +// Open implements fs.FS.Open() to open a file based on the filename.
        +func (fs *testFS) Open(name string) (fs.File, error) {
        +	if _, ok := (*fs)[name]; !ok {
        +		return nil, fmt.Errorf("unknown file %q", name)
        +	}
        +	return &testFile{fs, name, 0}, nil
        +}
        +
        +// testFile implements a test file (fs.File) based on testFS above.
        +type testFile struct {
        +	fs   *testFS
        +	name string
        +	posn int
        +}
        +
        +// Stat not implemented to obviate implementing fs.FileInfo.
        +func (f *testFile) Stat() (fs.FileInfo, error) {
        +	return nil, fmt.Errorf("unimplemented")
        +}
        +
        +// Read copies bytes from the testFS map.
        +func (f *testFile) Read(b []byte) (int, error) {
        +	if f.posn < 0 {
        +		return 0, fmt.Errorf("file not open: %q", f.name)
        +	}
        +	if f.posn >= len((*f.fs)[f.name]) {
        +		return 0, io.EOF
        +	}
        +	n := copy(b, (*f.fs)[f.name][f.posn:])
        +	f.posn += n
        +	return n, nil
        +}
        +
        +// Close marks the testFile as no longer in use.
        +func (f *testFile) Close() error {
        +	if f.posn < 0 {
        +		return fmt.Errorf("file already closed: %q", f.name)
        +	}
        +	f.posn = -1
        +	return nil
        +}
        +
        +// edge describes test data edges to define test graphs.
        +type edge struct {
        +	target, dep string
        +}
        +
        +// String returns a string representation of the edge.
        +func (e edge) String() string {
        +	return e.target + " -> " + e.dep
        +}
        +
        +// byEdge orders edges by target then dep name then annotations.
        +type byEdge []edge
        +
        +// Len returns the count of elements in the slice.
        +func (l byEdge) Len() int { return len(l) }
        +
        +// Swap rearranges 2 elements of the slice so that each occupies the other's
        +// former position.
        +func (l byEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +
        +// Less returns true when the `i`th element is lexicographically less than
        +// the `j`th element.
        +func (l byEdge) Less(i, j int) bool {
        +	if l[i].target == l[j].target {
        +		return l[i].dep < l[j].dep
        +	}
        +	return l[i].target < l[j].target
        +}
        +
        +// annotated describes annotated test data edges to define test graphs.
        +type annotated struct {
        +	target, dep string
        +	annotations []string
        +}
        +
        +func (e annotated) String() string {
        +	if e.annotations != nil {
        +		return e.target + " -> " + e.dep + " [" + strings.Join(e.annotations, ", ") + "]"
        +	}
        +	return e.target + " -> " + e.dep
        +}
        +
        +func (e annotated) IsEqualTo(other annotated) bool {
        +	if e.target != other.target {
        +		return false
        +	}
        +	if e.dep != other.dep {
        +		return false
        +	}
        +	if len(e.annotations) != len(other.annotations) {
        +		return false
        +	}
        +	a1 := append([]string{}, e.annotations...)
        +	a2 := append([]string{}, other.annotations...)
        +	for i := 0; i < len(a1); i++ {
        +		if a1[i] != a2[i] {
        +			return false
        +		}
        +	}
        +	return true
        +}
        +
        +// toGraph converts a list of roots and a list of annotated edges into a test license graph.
        +func toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) {
        +	deps := make(map[string][]annotated)
        +	for _, root := range roots {
        +		deps[root] = []annotated{}
        +	}
        +	for _, edge := range edges {
        +		if prev, ok := deps[edge.target]; ok {
        +			deps[edge.target] = append(prev, edge)
        +		} else {
        +			deps[edge.target] = []annotated{edge}
        +		}
        +		if _, ok := deps[edge.dep]; !ok {
        +			deps[edge.dep] = []annotated{}
        +		}
        +	}
        +	fs := make(testFS)
        +	for file, edges := range deps {
        +		body := meta[file]
        +		for _, edge := range edges {
        +			body += fmt.Sprintf("deps: {\n  file: %q\n", edge.dep)
        +			for _, ann := range edge.annotations {
        +				body += fmt.Sprintf("  annotations: %q\n", ann)
        +			}
        +			body += "}\n"
        +		}
        +		fs[file] = []byte(body)
        +	}
        +
        +	return ReadLicenseGraph(&fs, stderr, roots)
        +}
        +
        +// logGraph outputs a representation of the graph to a test log.
        +func logGraph(lg *LicenseGraph, t *testing.T) {
        +	t.Logf("license graph:")
        +	t.Logf("  targets:")
        +	for _, target := range lg.Targets() {
        +		t.Logf("    %s%s in package %q", target.Name(), target.LicenseConditions().String(), target.PackageName())
        +	}
        +	t.Logf("  /targets")
        +	t.Logf("  edges:")
        +	for _, edge := range lg.Edges() {
        +		t.Logf("    %s", edge.String())
        +	}
        +	t.Logf("  /edges")
        +	t.Logf("/license graph")
        +}
        +
        +// byAnnotatedEdge orders edges by target then dep name then annotations.
        +type byAnnotatedEdge []annotated
        +
        +func (l byAnnotatedEdge) Len() int      { return len(l) }
        +func (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +func (l byAnnotatedEdge) Less(i, j int) bool {
        +	if l[i].target == l[j].target {
        +		if l[i].dep == l[j].dep {
        +			ai := append([]string{}, l[i].annotations...)
        +			aj := append([]string{}, l[j].annotations...)
        +			sort.Strings(ai)
        +			sort.Strings(aj)
        +			for k := 0; k < len(ai) && k < len(aj); k++ {
        +				if ai[k] == aj[k] {
        +					continue
        +				}
        +				return ai[k] < aj[k]
        +			}
        +			return len(ai) < len(aj)
        +		}
        +		return l[i].dep < l[j].dep
        +	}
        +	return l[i].target < l[j].target
        +}
        +
        +// act describes test data resolution actions to define test action sets.
        +type act struct {
        +	actsOn, origin, condition string
        +}
        +
        +// String returns a human-readable string representing the test action.
        +func (a act) String() string {
        +	return fmt.Sprintf("%s{%s:%s}", a.actsOn, a.origin, a.condition)
        +}
        +
        +// toActionSet converts a list of act test data into a test action set.
        +func toActionSet(lg *LicenseGraph, data []act) ActionSet {
        +	as := make(ActionSet)
        +	for _, a := range data {
        +		actsOn := newTestNode(lg, a.actsOn)
        +		cs := newTestConditionSet(lg, a.origin, strings.Split(a.condition, "|"))
        +		as[actsOn] = cs
        +	}
        +	return as
        +}
        +
        +// res describes test data resolutions to define test resolution sets.
        +type res struct {
        +	attachesTo, actsOn, origin, condition string
        +}
        +
        +// toResolutionSet converts a list of res test data into a test resolution set.
        +func toResolutionSet(lg *LicenseGraph, data []res) ResolutionSet {
        +	rmap := make(ResolutionSet)
        +	for _, r := range data {
        +		attachesTo := newTestNode(lg, r.attachesTo)
        +		actsOn := newTestNode(lg, r.actsOn)
        +		if _, ok := rmap[attachesTo]; !ok {
        +			rmap[attachesTo] = make(ActionSet)
        +		}
        +		cs := newTestConditionSet(lg, r.origin, strings.Split(r.condition, ":"))
        +		rmap[attachesTo][actsOn] |= cs
        +	}
        +	return rmap
        +}
        +
        +// tcond associates a target name with '|' separated string conditions.
        +type tcond struct {
        +	target, conditions string
        +}
        +
        +// action represents a single element of an ActionSet for testing.
        +type action struct {
        +	target *TargetNode
        +	cs     LicenseConditionSet
        +}
        +
        +// String returns a human-readable string representation of the action.
        +func (a action) String() string {
        +	return fmt.Sprintf("%s%s", a.target.Name(), a.cs.String())
        +}
        +
        +// actionList represents an array of actions and a total order defined by
        +// target name followed by license condition set.
        +type actionList []action
        +
        +// String returns a human-readable string representation of the list.
        +func (l actionList) String() string {
        +	var sb strings.Builder
        +	fmt.Fprintf(&sb, "[")
        +	sep := ""
        +	for _, a := range l {
        +		fmt.Fprintf(&sb, "%s%s", sep, a.String())
        +		sep = ", "
        +	}
        +	fmt.Fprintf(&sb, "]")
        +	return sb.String()
        +}
        +
        +// Len returns the count of elements in the slice.
        +func (l actionList) Len() int { return len(l) }
        +
        +// Swap rearranges 2 elements of the slice so that each occupies the other's
        +// former position.
        +func (l actionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        +
        +// Less returns true when the `i`th element is lexicographically less than
        +// the `j`th element.
        +func (l actionList) Less(i, j int) bool {
        +	if l[i].target == l[j].target {
        +		return l[i].cs < l[j].cs
        +	}
        +	return l[i].target.Name() < l[j].target.Name()
        +}
        +
        +// asActionList represents the resolved license conditions in a license graph
        +// as an actionList for comparison in a test.
        +func asActionList(lg *LicenseGraph) actionList {
        +	result := make(actionList, 0, len(lg.targets))
        +	for _, target := range lg.targets {
        +		cs := target.resolution
        +		if cs.IsEmpty() {
        +			continue
        +		}
        +		result = append(result, action{target, cs})
        +	}
        +	return result
        +}
        +
        +// toActionList converts an array of tcond into an actionList for comparison
        +// in a test.
        +func toActionList(lg *LicenseGraph, actions []tcond) actionList {
        +	result := make(actionList, 0, len(actions))
        +	for _, actn := range actions {
        +		target := newTestNode(lg, actn.target)
        +		cs := NewLicenseConditionSet()
        +		for _, name := range strings.Split(actn.conditions, "|") {
        +			lc, ok := RecognizedConditionNames[name]
        +			if !ok {
        +				panic(fmt.Errorf("Unrecognized test condition name: %q", name))
        +			}
        +			cs = cs.Plus(lc)
        +		}
        +		result = append(result, action{target, cs})
        +	}
        +	return result
        +}
        +
        +// confl defines test data for a SourceSharePrivacyConflict as a target name,
        +// source condition name, privacy condition name triple.
        +type confl struct {
        +	sourceNode, share, privacy string
        +}
        +
        +// toConflictList converts confl test data into an array of
        +// SourceSharePrivacyConflict for comparison in a test.
        +func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
        +	result := make([]SourceSharePrivacyConflict, 0, len(data))
        +	for _, c := range data {
        +		fields := strings.Split(c.share, ":")
        +		oshare := fields[0]
        +		cshare := fields[1]
        +		fields = strings.Split(c.privacy, ":")
        +		oprivacy := fields[0]
        +		cprivacy := fields[1]
        +		result = append(result, SourceSharePrivacyConflict{
        +			newTestNode(lg, c.sourceNode),
        +			newTestCondition(lg, oshare, cshare),
        +			newTestCondition(lg, oprivacy, cprivacy),
        +		})
        +	}
        +	return result
        +}
        +
        +// checkSameActions compares an actual action set to an expected action set for a test.
        +func checkSameActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
        +	rsActual := make(ResolutionSet)
        +	rsExpected := make(ResolutionSet)
        +	testNode := newTestNode(lg, "test")
        +	rsActual[testNode] = asActual
        +	rsExpected[testNode] = asExpected
        +	checkSame(rsActual, rsExpected, t)
        +}
        +
        +// checkSame compares an actual resolution set to an expected resolution set for a test.
        +func checkSame(rsActual, rsExpected ResolutionSet, t *testing.T) {
        +	t.Logf("actual resolution set: %s", rsActual.String())
        +	t.Logf("expected resolution set: %s", rsExpected.String())
        +
        +	actualTargets := rsActual.AttachesTo()
        +	sort.Sort(actualTargets)
        +
        +	expectedTargets := rsExpected.AttachesTo()
        +	sort.Sort(expectedTargets)
        +
        +	t.Logf("actual targets: %s", actualTargets.String())
        +	t.Logf("expected targets: %s", expectedTargets.String())
        +
        +	for _, target := range expectedTargets {
        +		if !rsActual.AttachesToTarget(target) {
        +			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
        +			continue
        +		}
        +		expectedRl := rsExpected.Resolutions(target)
        +		sort.Sort(expectedRl)
        +		actualRl := rsActual.Resolutions(target)
        +		sort.Sort(actualRl)
        +		if len(expectedRl) != len(actualRl) {
        +			t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
        +				target.name, len(actualRl), len(expectedRl))
        +			continue
        +		}
        +		for i := 0; i < len(expectedRl); i++ {
        +			if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
        +				t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
        +					target.name, i, actualRl[i].asString(), expectedRl[i].asString())
        +				continue
        +			}
        +			expectedConditions := expectedRl[i].Resolves()
        +			actualConditions := actualRl[i].Resolves()
        +			if expectedConditions != actualConditions {
        +				t.Errorf("unexpected conditions apply to %q acting on %q: got %04x with names %s, want %04x with names %s",
        +					target.name, expectedRl[i].actsOn.name,
        +					actualConditions, actualConditions.Names(),
        +					expectedConditions, expectedConditions.Names())
        +				continue
        +			}
        +		}
        +
        +	}
        +	for _, target := range actualTargets {
        +		if !rsExpected.AttachesToTarget(target) {
        +			t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
        +		}
        +	}
        +}
        +
        +// checkResolvesActions compares an actual action set to an expected action set for a test verifying the actual set
        +// resolves all of the expected conditions.
        +func checkResolvesActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
        +	rsActual := make(ResolutionSet)
        +	rsExpected := make(ResolutionSet)
        +	testNode := newTestNode(lg, "test")
        +	rsActual[testNode] = asActual
        +	rsExpected[testNode] = asExpected
        +	checkResolves(rsActual, rsExpected, t)
        +}
        +
        +// checkResolves compares an actual resolution set to an expected resolution set for a test verifying the actual set
        +// resolves all of the expected conditions.
        +func checkResolves(rsActual, rsExpected ResolutionSet, t *testing.T) {
        +	t.Logf("actual resolution set: %s", rsActual.String())
        +	t.Logf("expected resolution set: %s", rsExpected.String())
        +
        +	actualTargets := rsActual.AttachesTo()
        +	sort.Sort(actualTargets)
        +
        +	expectedTargets := rsExpected.AttachesTo()
        +	sort.Sort(expectedTargets)
        +
        +	t.Logf("actual targets: %s", actualTargets.String())
        +	t.Logf("expected targets: %s", expectedTargets.String())
        +
        +	for _, target := range expectedTargets {
        +		if !rsActual.AttachesToTarget(target) {
        +			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
        +			continue
        +		}
        +		expectedRl := rsExpected.Resolutions(target)
        +		sort.Sort(expectedRl)
        +		actualRl := rsActual.Resolutions(target)
        +		sort.Sort(actualRl)
        +		if len(expectedRl) != len(actualRl) {
        +			t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
        +				target.name, len(actualRl), len(expectedRl))
        +			continue
        +		}
        +		for i := 0; i < len(expectedRl); i++ {
        +			if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
        +				t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
        +					target.name, i, actualRl[i].asString(), expectedRl[i].asString())
        +				continue
        +			}
        +			expectedConditions := expectedRl[i].Resolves()
        +			actualConditions := actualRl[i].Resolves()
        +			if expectedConditions != (expectedConditions & actualConditions) {
        +				t.Errorf("expected conditions missing from %q acting on %q: got %04x with names %s, want %04x with names %s",
        +					target.name, expectedRl[i].actsOn.name,
        +					actualConditions, actualConditions.Names(),
        +					expectedConditions, expectedConditions.Names())
        +				continue
        +			}
        +		}
        +
        +	}
        +	for _, target := range actualTargets {
        +		if !rsExpected.AttachesToTarget(target) {
        +			t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
        +		}
        +	}
        +}
        diff --git a/make/tools/docker/.gitignore b/make/tools/docker/.gitignore
        new file mode 100644
        index 0000000..df0b367
        --- /dev/null
        +++ b/make/tools/docker/.gitignore
        @@ -0,0 +1 @@
        +gitconfig
        diff --git a/make/tools/docker/Dockerfile b/make/tools/docker/Dockerfile
        new file mode 100644
        index 0000000..3856ab9
        --- /dev/null
        +++ b/make/tools/docker/Dockerfile
        @@ -0,0 +1,26 @@
        +FROM ubuntu:14.04
        +ARG userid
        +ARG groupid
        +ARG username
        +
        +RUN apt-get update && apt-get install -y git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip python openjdk-7-jdk
        +
        +RUN curl -o jdk8.tgz https://android.googlesource.com/platform/prebuilts/jdk/jdk8/+archive/master.tar.gz \
        + && tar -zxf jdk8.tgz linux-x86 \
        + && mv linux-x86 /usr/lib/jvm/java-8-openjdk-amd64 \
        + && rm -rf jdk8.tgz
        +
        +RUN curl -o /usr/local/bin/repo https://storage.googleapis.com/git-repo-downloads/repo \
        + && echo "d06f33115aea44e583c8669375b35aad397176a411de3461897444d247b6c220  /usr/local/bin/repo" | sha256sum --strict -c - \
        + && chmod a+x /usr/local/bin/repo
        +
        +RUN groupadd -g $groupid $username \
        + && useradd -m -u $userid -g $groupid $username \
        + && echo $username >/root/username \
        + && echo "export USER="$username >>/home/$username/.gitconfig
        +COPY gitconfig /home/$username/.gitconfig
        +RUN chown $userid:$groupid /home/$username/.gitconfig
        +ENV HOME=/home/$username
        +ENV USER=$username
        +
        +ENTRYPOINT chroot --userspec=$(cat /root/username):$(cat /root/username) / /bin/bash -i
        diff --git a/make/tools/docker/README.md b/make/tools/docker/README.md
        new file mode 100644
        index 0000000..304fd18
        --- /dev/null
        +++ b/make/tools/docker/README.md
        @@ -0,0 +1,18 @@
        +The Dockerfile in this directory sets up an Ubuntu Trusty image ready to build
        +a variety of Android branches (>= Lollipop). It's particulary useful to build
        +older branches that required 14.04 if you've upgraded to something newer.
        +
        +First, build the image:
        +```
        +# Copy your host gitconfig, or create a stripped down version
        +$ cp ~/.gitconfig gitconfig
        +$ docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t android-build-trusty .
        +```
        +
        +Then you can start up new instances with:
        +```
        +$ docker run -it --rm -v $ANDROID_BUILD_TOP:/src android-build-trusty
        +> cd /src; source build/envsetup.sh
        +> lunch aosp_arm-eng
        +> m -j50
        +```
        diff --git a/make/tools/droiddoc/Android.bp b/make/tools/droiddoc/Android.bp
        new file mode 100644
        index 0000000..71d4939
        --- /dev/null
        +++ b/make/tools/droiddoc/Android.bp
        @@ -0,0 +1,38 @@
        +// Copyright (C) 2013 The Android Open Source Project
        +//
        +// Licensed under the Apache License, Version 2.0 (the "License");
        +// you may not use this file except in compliance with the License.
        +// You may obtain a copy of the License at
        +//
        +//      http://www.apache.org/licenses/LICENSE-2.0
        +//
        +// Unless required by applicable law or agreed to in writing, software
        +// distributed under the License is distributed on an "AS IS" BASIS,
        +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        +// See the License for the specific language governing permissions and
        +// limitations under the License.
        +
        +package {
        +    // See: http://go/android-license-faq
        +    default_applicable_licenses: [
        +        "Android-Apache-2.0",
        +        "build_make_tools_droiddoc_license",
        +    ],
        +}
        +
        +license {
        +    name: "build_make_tools_droiddoc_license",
        +    package_name: "Android Droiddoc Templates",
        +    license_kinds: [
        +        "SPDX-license-identifier-BSD",
        +        "SPDX-license-identifier-CC-BY-2.5",
        +        "SPDX-license-identifier-GPL-3.0",
        +        "SPDX-license-identifier-MIT",
        +    ],
        +    license_text: ["LICENSE"],
        +}
        +
        +droiddoc_exported_dir {
        +    name: "droiddoc-templates-pdk",
        +    path: "templates-pdk",
        +}
        diff --git a/make/tools/droiddoc/LICENSE b/make/tools/droiddoc/LICENSE
        new file mode 100644
        index 0000000..b591dde
        --- /dev/null
        +++ b/make/tools/droiddoc/LICENSE
        @@ -0,0 +1,1095 @@
        +-----------------------------------------------------
        +microtemplate.js
        +
        +// Simple JavaScript Templating
        +// John Resig - http://ejohn.org/ - MIT Licensed
        +
        +-----------------------------------------------------
        +jquery-history.js
        +
        +/**
        + * jQuery history event v0.1
        + * Copyright (c) 2008 Tom Rodenberg 
        + * Licensed under the GPL (http://www.gnu.org/licenses/gpl.html) license.
        + */
        +
        +                    GNU GENERAL PUBLIC LICENSE
        +                       Version 3, 29 June 2007
        +
        + Copyright (C) 2007 Free Software Foundation, Inc. 
        + Everyone is permitted to copy and distribute verbatim copies
        + of this license document, but changing it is not allowed.
        +
        +                            Preamble
        +
        +  The GNU General Public License is a free, copyleft license for
        +software and other kinds of works.
        +
        +  The licenses for most software and other practical works are designed
        +to take away your freedom to share and change the works.  By contrast,
        +the GNU General Public License is intended to guarantee your freedom to
        +share and change all versions of a program--to make sure it remains free
        +software for all its users.  We, the Free Software Foundation, use the
        +GNU General Public License for most of our software; it applies also to
        +any other work released this way by its authors.  You can apply it to
        +your programs, too.
        +
        +  When we speak of free software, we are referring to freedom, not
        +price.  Our General Public Licenses are designed to make sure that you
        +have the freedom to distribute copies of free software (and charge for
        +them if you wish), that you receive source code or can get it if you
        +want it, that you can change the software or use pieces of it in new
        +free programs, and that you know you can do these things.
        +
        +  To protect your rights, we need to prevent others from denying you
        +these rights or asking you to surrender the rights.  Therefore, you have
        +certain responsibilities if you distribute copies of the software, or if
        +you modify it: responsibilities to respect the freedom of others.
        +
        +  For example, if you distribute copies of such a program, whether
        +gratis or for a fee, you must pass on to the recipients the same
        +freedoms that you received.  You must make sure that they, too, receive
        +or can get the source code.  And you must show them these terms so they
        +know their rights.
        +
        +  Developers that use the GNU GPL protect your rights with two steps:
        +(1) assert copyright on the software, and (2) offer you this License
        +giving you legal permission to copy, distribute and/or modify it.
        +
        +  For the developers' and authors' protection, the GPL clearly explains
        +that there is no warranty for this free software.  For both users' and
        +authors' sake, the GPL requires that modified versions be marked as
        +changed, so that their problems will not be attributed erroneously to
        +authors of previous versions.
        +
        +  Some devices are designed to deny users access to install or run
        +modified versions of the software inside them, although the manufacturer
        +can do so.  This is fundamentally incompatible with the aim of
        +protecting users' freedom to change the software.  The systematic
        +pattern of such abuse occurs in the area of products for individuals to
        +use, which is precisely where it is most unacceptable.  Therefore, we
        +have designed this version of the GPL to prohibit the practice for those
        +products.  If such problems arise substantially in other domains, we
        +stand ready to extend this provision to those domains in future versions
        +of the GPL, as needed to protect the freedom of users.
        +
        +  Finally, every program is threatened constantly by software patents.
        +States should not allow patents to restrict development and use of
        +software on general-purpose computers, but in those that do, we wish to
        +avoid the special danger that patents applied to a free program could
        +make it effectively proprietary.  To prevent this, the GPL assures that
        +patents cannot be used to render the program non-free.
        +
        +  The precise terms and conditions for copying, distribution and
        +modification follow.
        +
        +                       TERMS AND CONDITIONS
        +
        +  0. Definitions.
        +
        +  "This License" refers to version 3 of the GNU General Public License.
        +
        +  "Copyright" also means copyright-like laws that apply to other kinds of
        +works, such as semiconductor masks.
        +
        +  "The Program" refers to any copyrightable work licensed under this
        +License.  Each licensee is addressed as "you".  "Licensees" and
        +"recipients" may be individuals or organizations.
        +
        +  To "modify" a work means to copy from or adapt all or part of the work
        +in a fashion requiring copyright permission, other than the making of an
        +exact copy.  The resulting work is called a "modified version" of the
        +earlier work or a work "based on" the earlier work.
        +
        +  A "covered work" means either the unmodified Program or a work based
        +on the Program.
        +
        +  To "propagate" a work means to do anything with it that, without
        +permission, would make you directly or secondarily liable for
        +infringement under applicable copyright law, except executing it on a
        +computer or modifying a private copy.  Propagation includes copying,
        +distribution (with or without modification), making available to the
        +public, and in some countries other activities as well.
        +
        +  To "convey" a work means any kind of propagation that enables other
        +parties to make or receive copies.  Mere interaction with a user through
        +a computer network, with no transfer of a copy, is not conveying.
        +
        +  An interactive user interface displays "Appropriate Legal Notices"
        +to the extent that it includes a convenient and prominently visible
        +feature that (1) displays an appropriate copyright notice, and (2)
        +tells the user that there is no warranty for the work (except to the
        +extent that warranties are provided), that licensees may convey the
        +work under this License, and how to view a copy of this License.  If
        +the interface presents a list of user commands or options, such as a
        +menu, a prominent item in the list meets this criterion.
        +
        +  1. Source Code.
        +
        +  The "source code" for a work means the preferred form of the work
        +for making modifications to it.  "Object code" means any non-source
        +form of a work.
        +
        +  A "Standard Interface" means an interface that either is an official
        +standard defined by a recognized standards body, or, in the case of
        +interfaces specified for a particular programming language, one that
        +is widely used among developers working in that language.
        +
        +  The "System Libraries" of an executable work include anything, other
        +than the work as a whole, that (a) is included in the normal form of
        +packaging a Major Component, but which is not part of that Major
        +Component, and (b) serves only to enable use of the work with that
        +Major Component, or to implement a Standard Interface for which an
        +implementation is available to the public in source code form.  A
        +"Major Component", in this context, means a major essential component
        +(kernel, window system, and so on) of the specific operating system
        +(if any) on which the executable work runs, or a compiler used to
        +produce the work, or an object code interpreter used to run it.
        +
        +  The "Corresponding Source" for a work in object code form means all
        +the source code needed to generate, install, and (for an executable
        +work) run the object code and to modify the work, including scripts to
        +control those activities.  However, it does not include the work's
        +System Libraries, or general-purpose tools or generally available free
        +programs which are used unmodified in performing those activities but
        +which are not part of the work.  For example, Corresponding Source
        +includes interface definition files associated with source files for
        +the work, and the source code for shared libraries and dynamically
        +linked subprograms that the work is specifically designed to require,
        +such as by intimate data communication or control flow between those
        +subprograms and other parts of the work.
        +
        +  The Corresponding Source need not include anything that users
        +can regenerate automatically from other parts of the Corresponding
        +Source.
        +
        +  The Corresponding Source for a work in source code form is that
        +same work.
        +
        +  2. Basic Permissions.
        +
        +  All rights granted under this License are granted for the term of
        +copyright on the Program, and are irrevocable provided the stated
        +conditions are met.  This License explicitly affirms your unlimited
        +permission to run the unmodified Program.  The output from running a
        +covered work is covered by this License only if the output, given its
        +content, constitutes a covered work.  This License acknowledges your
        +rights of fair use or other equivalent, as provided by copyright law.
        +
        +  You may make, run and propagate covered works that you do not
        +convey, without conditions so long as your license otherwise remains
        +in force.  You may convey covered works to others for the sole purpose
        +of having them make modifications exclusively for you, or provide you
        +with facilities for running those works, provided that you comply with
        +the terms of this License in conveying all material for which you do
        +not control copyright.  Those thus making or running the covered works
        +for you must do so exclusively on your behalf, under your direction
        +and control, on terms that prohibit them from making any copies of
        +your copyrighted material outside their relationship with you.
        +
        +  Conveying under any other circumstances is permitted solely under
        +the conditions stated below.  Sublicensing is not allowed; section 10
        +makes it unnecessary.
        +
        +  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
        +
        +  No covered work shall be deemed part of an effective technological
        +measure under any applicable law fulfilling obligations under article
        +11 of the WIPO copyright treaty adopted on 20 December 1996, or
        +similar laws prohibiting or restricting circumvention of such
        +measures.
        +
        +  When you convey a covered work, you waive any legal power to forbid
        +circumvention of technological measures to the extent such circumvention
        +is effected by exercising rights under this License with respect to
        +the covered work, and you disclaim any intention to limit operation or
        +modification of the work as a means of enforcing, against the work's
        +users, your or third parties' legal rights to forbid circumvention of
        +technological measures.
        +
        +  4. Conveying Verbatim Copies.
        +
        +  You may convey verbatim copies of the Program's source code as you
        +receive it, in any medium, provided that you conspicuously and
        +appropriately publish on each copy an appropriate copyright notice;
        +keep intact all notices stating that this License and any
        +non-permissive terms added in accord with section 7 apply to the code;
        +keep intact all notices of the absence of any warranty; and give all
        +recipients a copy of this License along with the Program.
        +
        +  You may charge any price or no price for each copy that you convey,
        +and you may offer support or warranty protection for a fee.
        +
        +  5. Conveying Modified Source Versions.
        +
        +  You may convey a work based on the Program, or the modifications to
        +produce it from the Program, in the form of source code under the
        +terms of section 4, provided that you also meet all of these conditions:
        +
        +    a) The work must carry prominent notices stating that you modified
        +    it, and giving a relevant date.
        +
        +    b) The work must carry prominent notices stating that it is
        +    released under this License and any conditions added under section
        +    7.  This requirement modifies the requirement in section 4 to
        +    "keep intact all notices".
        +
        +    c) You must license the entire work, as a whole, under this
        +    License to anyone who comes into possession of a copy.  This
        +    License will therefore apply, along with any applicable section 7
        +    additional terms, to the whole of the work, and all its parts,
        +    regardless of how they are packaged.  This License gives no
        +    permission to license the work in any other way, but it does not
        +    invalidate such permission if you have separately received it.
        +
        +    d) If the work has interactive user interfaces, each must display
        +    Appropriate Legal Notices; however, if the Program has interactive
        +    interfaces that do not display Appropriate Legal Notices, your
        +    work need not make them do so.
        +
        +  A compilation of a covered work with other separate and independent
        +works, which are not by their nature extensions of the covered work,
        +and which are not combined with it such as to form a larger program,
        +in or on a volume of a storage or distribution medium, is called an
        +"aggregate" if the compilation and its resulting copyright are not
        +used to limit the access or legal rights of the compilation's users
        +beyond what the individual works permit.  Inclusion of a covered work
        +in an aggregate does not cause this License to apply to the other
        +parts of the aggregate.
        +
        +  6. Conveying Non-Source Forms.
        +
        +  You may convey a covered work in object code form under the terms
        +of sections 4 and 5, provided that you also convey the
        +machine-readable Corresponding Source under the terms of this License,
        +in one of these ways:
        +
        +    a) Convey the object code in, or embodied in, a physical product
        +    (including a physical distribution medium), accompanied by the
        +    Corresponding Source fixed on a durable physical medium
        +    customarily used for software interchange.
        +
        +    b) Convey the object code in, or embodied in, a physical product
        +    (including a physical distribution medium), accompanied by a
        +    written offer, valid for at least three years and valid for as
        +    long as you offer spare parts or customer support for that product
        +    model, to give anyone who possesses the object code either (1) a
        +    copy of the Corresponding Source for all the software in the
        +    product that is covered by this License, on a durable physical
        +    medium customarily used for software interchange, for a price no
        +    more than your reasonable cost of physically performing this
        +    conveying of source, or (2) access to copy the
        +    Corresponding Source from a network server at no charge.
        +
        +    c) Convey individual copies of the object code with a copy of the
        +    written offer to provide the Corresponding Source.  This
        +    alternative is allowed only occasionally and noncommercially, and
        +    only if you received the object code with such an offer, in accord
        +    with subsection 6b.
        +
        +    d) Convey the object code by offering access from a designated
        +    place (gratis or for a charge), and offer equivalent access to the
        +    Corresponding Source in the same way through the same place at no
        +    further charge.  You need not require recipients to copy the
        +    Corresponding Source along with the object code.  If the place to
        +    copy the object code is a network server, the Corresponding Source
        +    may be on a different server (operated by you or a third party)
        +    that supports equivalent copying facilities, provided you maintain
        +    clear directions next to the object code saying where to find the
        +    Corresponding Source.  Regardless of what server hosts the
        +    Corresponding Source, you remain obligated to ensure that it is
        +    available for as long as needed to satisfy these requirements.
        +
        +    e) Convey the object code using peer-to-peer transmission, provided
        +    you inform other peers where the object code and Corresponding
        +    Source of the work are being offered to the general public at no
        +    charge under subsection 6d.
        +
        +  A separable portion of the object code, whose source code is excluded
        +from the Corresponding Source as a System Library, need not be
        +included in conveying the object code work.
        +
        +  A "User Product" is either (1) a "consumer product", which means any
        +tangible personal property which is normally used for personal, family,
        +or household purposes, or (2) anything designed or sold for incorporation
        +into a dwelling.  In determining whether a product is a consumer product,
        +doubtful cases shall be resolved in favor of coverage.  For a particular
        +product received by a particular user, "normally used" refers to a
        +typical or common use of that class of product, regardless of the status
        +of the particular user or of the way in which the particular user
        +actually uses, or expects or is expected to use, the product.  A product
        +is a consumer product regardless of whether the product has substantial
        +commercial, industrial or non-consumer uses, unless such uses represent
        +the only significant mode of use of the product.
        +
        +  "Installation Information" for a User Product means any methods,
        +procedures, authorization keys, or other information required to install
        +and execute modified versions of a covered work in that User Product from
        +a modified version of its Corresponding Source.  The information must
        +suffice to ensure that the continued functioning of the modified object
        +code is in no case prevented or interfered with solely because
        +modification has been made.
        +
        +  If you convey an object code work under this section in, or with, or
        +specifically for use in, a User Product, and the conveying occurs as
        +part of a transaction in which the right of possession and use of the
        +User Product is transferred to the recipient in perpetuity or for a
        +fixed term (regardless of how the transaction is characterized), the
        +Corresponding Source conveyed under this section must be accompanied
        +by the Installation Information.  But this requirement does not apply
        +if neither you nor any third party retains the ability to install
        +modified object code on the User Product (for example, the work has
        +been installed in ROM).
        +
        +  The requirement to provide Installation Information does not include a
        +requirement to continue to provide support service, warranty, or updates
        +for a work that has been modified or installed by the recipient, or for
        +the User Product in which it has been modified or installed.  Access to a
        +network may be denied when the modification itself materially and
        +adversely affects the operation of the network or violates the rules and
        +protocols for communication across the network.
        +
        +  Corresponding Source conveyed, and Installation Information provided,
        +in accord with this section must be in a format that is publicly
        +documented (and with an implementation available to the public in
        +source code form), and must require no special password or key for
        +unpacking, reading or copying.
        +
        +  7. Additional Terms.
        +
        +  "Additional permissions" are terms that supplement the terms of this
        +License by making exceptions from one or more of its conditions.
        +Additional permissions that are applicable to the entire Program shall
        +be treated as though they were included in this License, to the extent
        +that they are valid under applicable law.  If additional permissions
        +apply only to part of the Program, that part may be used separately
        +under those permissions, but the entire Program remains governed by
        +this License without regard to the additional permissions.
        +
        +  When you convey a copy of a covered work, you may at your option
        +remove any additional permissions from that copy, or from any part of
        +it.  (Additional permissions may be written to require their own
        +removal in certain cases when you modify the work.)  You may place
        +additional permissions on material, added by you to a covered work,
        +for which you have or can give appropriate copyright permission.
        +
        +  Notwithstanding any other provision of this License, for material you
        +add to a covered work, you may (if authorized by the copyright holders of
        +that material) supplement the terms of this License with terms:
        +
        +    a) Disclaiming warranty or limiting liability differently from the
        +    terms of sections 15 and 16 of this License; or
        +
        +    b) Requiring preservation of specified reasonable legal notices or
        +    author attributions in that material or in the Appropriate Legal
        +    Notices displayed by works containing it; or
        +
        +    c) Prohibiting misrepresentation of the origin of that material, or
        +    requiring that modified versions of such material be marked in
        +    reasonable ways as different from the original version; or
        +
        +    d) Limiting the use for publicity purposes of names of licensors or
        +    authors of the material; or
        +
        +    e) Declining to grant rights under trademark law for use of some
        +    trade names, trademarks, or service marks; or
        +
        +    f) Requiring indemnification of licensors and authors of that
        +    material by anyone who conveys the material (or modified versions of
        +    it) with contractual assumptions of liability to the recipient, for
        +    any liability that these contractual assumptions directly impose on
        +    those licensors and authors.
        +
        +  All other non-permissive additional terms are considered "further
        +restrictions" within the meaning of section 10.  If the Program as you
        +received it, or any part of it, contains a notice stating that it is
        +governed by this License along with a term that is a further
        +restriction, you may remove that term.  If a license document contains
        +a further restriction but permits relicensing or conveying under this
        +License, you may add to a covered work material governed by the terms
        +of that license document, provided that the further restriction does
        +not survive such relicensing or conveying.
        +
        +  If you add terms to a covered work in accord with this section, you
        +must place, in the relevant source files, a statement of the
        +additional terms that apply to those files, or a notice indicating
        +where to find the applicable terms.
        +
        +  Additional terms, permissive or non-permissive, may be stated in the
        +form of a separately written license, or stated as exceptions;
        +the above requirements apply either way.
        +
        +  8. Termination.
        +
        +  You may not propagate or modify a covered work except as expressly
        +provided under this License.  Any attempt otherwise to propagate or
        +modify it is void, and will automatically terminate your rights under
        +this License (including any patent licenses granted under the third
        +paragraph of section 11).
        +
        +  However, if you cease all violation of this License, then your
        +license from a particular copyright holder is reinstated (a)
        +provisionally, unless and until the copyright holder explicitly and
        +finally terminates your license, and (b) permanently, if the copyright
        +holder fails to notify you of the violation by some reasonable means
        +prior to 60 days after the cessation.
        +
        +  Moreover, your license from a particular copyright holder is
        +reinstated permanently if the copyright holder notifies you of the
        +violation by some reasonable means, this is the first time you have
        +received notice of violation of this License (for any work) from that
        +copyright holder, and you cure the violation prior to 30 days after
        +your receipt of the notice.
        +
        +  Termination of your rights under this section does not terminate the
        +licenses of parties who have received copies or rights from you under
        +this License.  If your rights have been terminated and not permanently
        +reinstated, you do not qualify to receive new licenses for the same
        +material under section 10.
        +
        +  9. Acceptance Not Required for Having Copies.
        +
        +  You are not required to accept this License in order to receive or
        +run a copy of the Program.  Ancillary propagation of a covered work
        +occurring solely as a consequence of using peer-to-peer transmission
        +to receive a copy likewise does not require acceptance.  However,
        +nothing other than this License grants you permission to propagate or
        +modify any covered work.  These actions infringe copyright if you do
        +not accept this License.  Therefore, by modifying or propagating a
        +covered work, you indicate your acceptance of this License to do so.
        +
        +  10. Automatic Licensing of Downstream Recipients.
        +
        +  Each time you convey a covered work, the recipient automatically
        +receives a license from the original licensors, to run, modify and
        +propagate that work, subject to this License.  You are not responsible
        +for enforcing compliance by third parties with this License.
        +
        +  An "entity transaction" is a transaction transferring control of an
        +organization, or substantially all assets of one, or subdividing an
        +organization, or merging organizations.  If propagation of a covered
        +work results from an entity transaction, each party to that
        +transaction who receives a copy of the work also receives whatever
        +licenses to the work the party's predecessor in interest had or could
        +give under the previous paragraph, plus a right to possession of the
        +Corresponding Source of the work from the predecessor in interest, if
        +the predecessor has it or can get it with reasonable efforts.
        +
        +  You may not impose any further restrictions on the exercise of the
        +rights granted or affirmed under this License.  For example, you may
        +not impose a license fee, royalty, or other charge for exercise of
        +rights granted under this License, and you may not initiate litigation
        +(including a cross-claim or counterclaim in a lawsuit) alleging that
        +any patent claim is infringed by making, using, selling, offering for
        +sale, or importing the Program or any portion of it.
        +
        +  11. Patents.
        +
        +  A "contributor" is a copyright holder who authorizes use under this
        +License of the Program or a work on which the Program is based.  The
        +work thus licensed is called the contributor's "contributor version".
        +
        +  A contributor's "essential patent claims" are all patent claims
        +owned or controlled by the contributor, whether already acquired or
        +hereafter acquired, that would be infringed by some manner, permitted
        +by this License, of making, using, or selling its contributor version,
        +but do not include claims that would be infringed only as a
        +consequence of further modification of the contributor version.  For
        +purposes of this definition, "control" includes the right to grant
        +patent sublicenses in a manner consistent with the requirements of
        +this License.
        +
        +  Each contributor grants you a non-exclusive, worldwide, royalty-free
        +patent license under the contributor's essential patent claims, to
        +make, use, sell, offer for sale, import and otherwise run, modify and
        +propagate the contents of its contributor version.
        +
        +  In the following three paragraphs, a "patent license" is any express
        +agreement or commitment, however denominated, not to enforce a patent
        +(such as an express permission to practice a patent or covenant not to
        +sue for patent infringement).  To "grant" such a patent license to a
        +party means to make such an agreement or commitment not to enforce a
        +patent against the party.
        +
        +  If you convey a covered work, knowingly relying on a patent license,
        +and the Corresponding Source of the work is not available for anyone
        +to copy, free of charge and under the terms of this License, through a
        +publicly available network server or other readily accessible means,
        +then you must either (1) cause the Corresponding Source to be so
        +available, or (2) arrange to deprive yourself of the benefit of the
        +patent license for this particular work, or (3) arrange, in a manner
        +consistent with the requirements of this License, to extend the patent
        +license to downstream recipients.  "Knowingly relying" means you have
        +actual knowledge that, but for the patent license, your conveying the
        +covered work in a country, or your recipient's use of the covered work
        +in a country, would infringe one or more identifiable patents in that
        +country that you have reason to believe are valid.
        +
        +  If, pursuant to or in connection with a single transaction or
        +arrangement, you convey, or propagate by procuring conveyance of, a
        +covered work, and grant a patent license to some of the parties
        +receiving the covered work authorizing them to use, propagate, modify
        +or convey a specific copy of the covered work, then the patent license
        +you grant is automatically extended to all recipients of the covered
        +work and works based on it.
        +
        +  A patent license is "discriminatory" if it does not include within
        +the scope of its coverage, prohibits the exercise of, or is
        +conditioned on the non-exercise of one or more of the rights that are
        +specifically granted under this License.  You may not convey a covered
        +work if you are a party to an arrangement with a third party that is
        +in the business of distributing software, under which you make payment
        +to the third party based on the extent of your activity of conveying
        +the work, and under which the third party grants, to any of the
        +parties who would receive the covered work from you, a discriminatory
        +patent license (a) in connection with copies of the covered work
        +conveyed by you (or copies made from those copies), or (b) primarily
        +for and in connection with specific products or compilations that
        +contain the covered work, unless you entered into that arrangement,
        +or that patent license was granted, prior to 28 March 2007.
        +
        +  Nothing in this License shall be construed as excluding or limiting
        +any implied license or other defenses to infringement that may
        +otherwise be available to you under applicable patent law.
        +
        +  12. No Surrender of Others' Freedom.
        +
        +  If conditions are imposed on you (whether by court order, agreement or
        +otherwise) that contradict the conditions of this License, they do not
        +excuse you from the conditions of this License.  If you cannot convey a
        +covered work so as to satisfy simultaneously your obligations under this
        +License and any other pertinent obligations, then as a consequence you may
        +not convey it at all.  For example, if you agree to terms that obligate you
        +to collect a royalty for further conveying from those to whom you convey
        +the Program, the only way you could satisfy both those terms and this
        +License would be to refrain entirely from conveying the Program.
        +
        +  13. Use with the GNU Affero General Public License.
        +
        +  Notwithstanding any other provision of this License, you have
        +permission to link or combine any covered work with a work licensed
        +under version 3 of the GNU Affero General Public License into a single
        +combined work, and to convey the resulting work.  The terms of this
        +License will continue to apply to the part which is the covered work,
        +but the special requirements of the GNU Affero General Public License,
        +section 13, concerning interaction through a network will apply to the
        +combination as such.
        +
        +  14. Revised Versions of this License.
        +
        +  The Free Software Foundation may publish revised and/or new versions of
        +the GNU General Public License from time to time.  Such new versions will
        +be similar in spirit to the present version, but may differ in detail to
        +address new problems or concerns.
        +
        +  Each version is given a distinguishing version number.  If the
        +Program specifies that a certain numbered version of the GNU General
        +Public License "or any later version" applies to it, you have the
        +option of following the terms and conditions either of that numbered
        +version or of any later version published by the Free Software
        +Foundation.  If the Program does not specify a version number of the
        +GNU General Public License, you may choose any version ever published
        +by the Free Software Foundation.
        +
        +  If the Program specifies that a proxy can decide which future
        +versions of the GNU General Public License can be used, that proxy's
        +public statement of acceptance of a version permanently authorizes you
        +to choose that version for the Program.
        +
        +  Later license versions may give you additional or different
        +permissions.  However, no additional obligations are imposed on any
        +author or copyright holder as a result of your choosing to follow a
        +later version.
        +
        +  15. Disclaimer of Warranty.
        +
        +  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
        +APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
        +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
        +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
        +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        +PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
        +IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
        +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
        +
        +  16. Limitation of Liability.
        +
        +  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
        +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
        +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
        +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
        +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
        +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
        +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
        +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
        +SUCH DAMAGES.
        +
        +  17. Interpretation of Sections 15 and 16.
        +
        +  If the disclaimer of warranty and limitation of liability provided
        +above cannot be given local legal effect according to their terms,
        +reviewing courts shall apply local law that most closely approximates
        +an absolute waiver of all civil liability in connection with the
        +Program, unless a warranty or assumption of liability accompanies a
        +copy of the Program in return for a fee.
        +
        +                     END OF TERMS AND CONDITIONS
        +
        +            How to Apply These Terms to Your New Programs
        +
        +  If you develop a new program, and you want it to be of the greatest
        +possible use to the public, the best way to achieve this is to make it
        +free software which everyone can redistribute and change under these terms.
        +
        +  To do so, attach the following notices to the program.  It is safest
        +to attach them to the start of each source file to most effectively
        +state the exclusion of warranty; and each file should have at least
        +the "copyright" line and a pointer to where the full notice is found.
        +
        +    
        +    Copyright (C)   
        +
        +    This program is free software: you can redistribute it and/or modify
        +    it under the terms of the GNU General Public License as published by
        +    the Free Software Foundation, either version 3 of the License, or
        +    (at your option) any later version.
        +
        +    This program is distributed in the hope that it will be useful,
        +    but WITHOUT ANY WARRANTY; without even the implied warranty of
        +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        +    GNU General Public License for more details.
        +
        +    You should have received a copy of the GNU General Public License
        +    along with this program.  If not, see .
        +
        +Also add information on how to contact you by electronic and paper mail.
        +
        +  If the program does terminal interaction, make it output a short
        +notice like this when it starts in an interactive mode:
        +
        +      Copyright (C)   
        +    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
        +    This is free software, and you are welcome to redistribute it
        +    under certain conditions; type `show c' for details.
        +
        +The hypothetical commands `show w' and `show c' should show the appropriate
        +parts of the General Public License.  Of course, your program's commands
        +might be different; for a GUI interface, you would use an "about box".
        +
        +  You should also get your employer (if you work as a programmer) or school,
        +if any, to sign a "copyright disclaimer" for the program, if necessary.
        +For more information on this, and how to apply and follow the GNU GPL, see
        +.
        +
        +  The GNU General Public License does not permit incorporating your program
        +into proprietary programs.  If your program is a subroutine library, you
        +may consider it more useful to permit linking proprietary applications with
        +the library.  If this is what you want to do, use the GNU Lesser General
        +Public License instead of this License.  But first, please read
        +.
        +
        +-----------------------------------------------------
        +yui-3.3.0-reset-min.css
        +
        +/*
        +Copyright (c) 2010, Yahoo! Inc. All rights reserved.
        +Code licensed under the BSD License:
        +http://developer.yahoo.com/yui/license.html
        +version: 3.3.0
        +build: 3167
        +*/
        +
        +
        +Software License Agreement (BSD License)
        +Copyright (c) 2010, Yahoo! Inc.
        +All rights reserved.
        +
        +Redistribution and use of this software in source and binary forms, with or
        +without modification, are permitted provided that the following conditions are
        +met:
        +
        +    Redistributions of source code must retain the above copyright notice, this
        +    list of conditions and the following disclaimer.
        +    Redistributions in binary form must reproduce the above copyright notice,
        +    this list of conditions and the following disclaimer in the documentation
        +    and/or other materials provided with the distribution.
        +    Neither the name of Yahoo! Inc. nor the names of its contributors may be
        +    used to endorse or promote products derived from this software without
        +    specific prior written permission of Yahoo! Inc.
        +
        +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
        +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
        +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
        +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
        +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
        +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        +Sources of Intellectual Property Included in the YUI Library
        +
        +YUI is issued by Yahoo! under the BSD license above. Below is a list of certain
        +publicly available software that is the source of intellectual property in YUI,
        +along with the licensing terms that pertain to thosesources of IP. This list is
        +for informational purposes only and is not intended to represent an exhaustive
        +list of third party contributions to the YUI.
        +
        +    Douglas Crockford's JSON parsing and stringifying methods: In the JSON
        +    Utility, Douglas Crockford's JSON parsing and stringifying methods are
        +    adapted from work published at JSON.org. The adapted work is in the public
        +    domain.
        +
        +    Robert Penner's animation-easing algorithms: In the Animation Utility, YUI
        +    makes use of Robert Penner's algorithms for easing.
        +
        +    Geoff Stearns's SWFObject: In the Charts Control and the Uploader versions
        +    through 2.7.0, YUI makes use of Geoff Stearns's SWFObject v1.5 for Flash
        +    Player detection and embedding. More information on SWFObject can be found
        +    here (http://blog.deconcept.com/swfobject/). SWFObject is (c) 2007 Geoff
        +    Stearns and is released under the MIT License
        +    (http://www.opensource.org/licenses/mit-license.php).
        +
        +    Diego Perini's IEContentLoaded technique: The Event Utility employs a
        +    technique developed by Diego Perini and licensed under GPL. YUI's use of
        +    this technique is included under our BSD license with the author's
        +    permission.
        +
        +
        +From MIT license link above:
        +
        +Permission is hereby granted, free of charge, to any person obtaining a copy of
        +this software and associated documentation files (the "Software"), to deal in
        +the Software without restriction, including without limitation the rights to
        +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        +of the Software, and to permit persons to whom the Software is furnished to do
        +so, subject to the following conditions:
        +
        +The above copyright notice and this permission notice shall be included in all
        +copies or substantial portions of the Software.
        +
        +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        +SOFTWARE.
        +
        +-----------------------------------------------------
        +customizations.cs
        +
        +  Except as noted, this content is 
        +  licensed under 
        +  Creative Commons Attribution 2.5.
        +
        +
        +Creative Commons
        +Creative Commons Legal Code
        +
        +Attribution 2.5
        +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL
        +SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT
        +RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS.
        +CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND
        +DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
        +
        +License
        +
        +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
        +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
        +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
        +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
        +
        +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE
        +BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS
        +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
        +CONDITIONS.
        +
        +1. Definitions
        +
        +    "Collective Work" means a work, such as a periodical issue, anthology or
        +    encyclopedia, in which the Work in its entirety in unmodified form, along
        +    with a number of other contributions, constituting separate and independent
        +    works in themselves, are assembled into a collective whole. A work that
        +    constitutes a Collective Work will not be considered a Derivative Work (as
        +    defined below) for the purposes of this License.
        +
        +    "Derivative Work" means a work based upon the Work or upon the Work and
        +    other pre-existing works, such as a translation, musical arrangement,
        +    dramatization, fictionalization, motion picture version, sound recording,
        +    art reproduction, abridgment, condensation, or any other form in which the
        +    Work may be recast, transformed, or adapted, except that a work that
        +    constitutes a Collective Work will not be considered a Derivative Work for
        +    the purpose of this License. For the avoidance of doubt, where the Work is
        +    a musical composition or sound recording, the synchronization of the Work
        +    in timed-relation with a moving image ("synching") will be considered a
        +    Derivative Work for the purpose of this License.
        +
        +    "Licensor" means the individual or entity that offers the Work under the
        +    terms of this License.
        +
        +    "Original Author" means the individual or entity who created the Work.
        +
        +    "Work" means the copyrightable work of authorship offered under the terms
        +    of this License.
        +
        +    "You" means an individual or entity exercising rights under this License
        +    who has not previously violated the terms of this License with respect to
        +    the Work, or who has received express permission from the Licensor to
        +    exercise rights under this License despite a previous violation.
        +
        +2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or
        +restrict any rights arising from fair use, first sale or other limitations on
        +the exclusive rights of the copyright owner under copyright law or other
        +applicable laws.
        +
        +3. License Grant. Subject to the terms and conditions of this License, Licensor
        +hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the
        +duration of the applicable copyright) license to exercise the rights in the
        +Work as stated below:
        +
        +    to reproduce the Work, to incorporate the Work into one or more Collective
        +    Works, and to reproduce the Work as incorporated in the Collective Works;
        +
        +    to create and reproduce Derivative Works;
        +
        +    to distribute copies or phonorecords of, display publicly, perform
        +    publicly, and perform publicly by means of a digital audio transmission the
        +    Work including as incorporated in Collective Works;
        +
        +    to distribute copies or phonorecords of, display publicly, perform
        +    publicly, and perform publicly by means of a digital audio transmission
        +    Derivative Works.
        +
        +    For the avoidance of doubt, where the work is a musical composition:
        +        Performance Royalties Under Blanket Licenses. Licensor waives the
        +        exclusive right to collect, whether individually or via a performance
        +        rights society (e.g. ASCAP, BMI, SESAC), royalties for the public
        +        performance or public digital performance (e.g. webcast) of the Work.
        +
        +        Mechanical Rights and Statutory Royalties. Licensor waives the
        +        exclusive right to collect, whether individually or via a music rights
        +        agency or designated agent (e.g. Harry Fox Agency), royalties for any
        +        phonorecord You create from the Work ("cover version") and distribute,
        +        subject to the compulsory license created by 17 USC Section 115 of the
        +        US Copyright Act (or the equivalent in other jurisdictions).
        +
        +    Webcasting Rights and Statutory Royalties. For the avoidance of doubt,
        +    where the Work is a sound recording, Licensor waives the exclusive right to
        +    collect, whether individually or via a performance-rights society (e.g.
        +    SoundExchange), royalties for the public digital performance (e.g. webcast)
        +    of the Work, subject to the compulsory license created by 17 USC Section
        +    114 of the US Copyright Act (or the equivalent in other jurisdictions).
        +
        +The above rights may be exercised in all media and formats whether now known or
        +hereafter devised. The above rights include the right to make such
        +modifications as are technically necessary to exercise the rights in other
        +media and formats. All rights not expressly granted by Licensor are hereby
        +reserved.
        +
        +4. Restrictions.The license granted in Section 3 above is expressly made
        +subject to and limited by the following restrictions:
        +
        +    You may distribute, publicly display, publicly perform, or publicly
        +    digitally perform the Work only under the terms of this License, and You
        +    must include a copy of, or the Uniform Resource Identifier for, this
        +    License with every copy or phonorecord of the Work You distribute, publicly
        +    display, publicly perform, or publicly digitally perform. You may not offer
        +    or impose any terms on the Work that alter or restrict the terms of this
        +    License or the recipients' exercise of the rights granted hereunder. You
        +    may not sublicense the Work. You must keep intact all notices that refer to
        +    this License and to the disclaimer of warranties. You may not distribute,
        +    publicly display, publicly perform, or publicly digitally perform the Work
        +    with any technological measures that control access or use of the Work in a
        +    manner inconsistent with the terms of this License Agreement. The above
        +    applies to the Work as incorporated in a Collective Work, but this does not
        +    require the Collective Work apart from the Work itself to be made subject
        +    to the terms of this License. If You create a Collective Work, upon notice
        +    from any Licensor You must, to the extent practicable, remove from the
        +    Collective Work any credit as required by clause 4(b), as requested. If You
        +    create a Derivative Work, upon notice from any Licensor You must, to the
        +    extent practicable, remove from the Derivative Work any credit as required
        +    by clause 4(b), as requested.
        +
        +    If you distribute, publicly display, publicly perform, or publicly
        +    digitally perform the Work or any Derivative Works or Collective Works, You
        +    must keep intact all copyright notices for the Work and provide, reasonable
        +    to the medium or means You are utilizing: (i) the name of the Original
        +    Author (or pseudonym, if applicable) if supplied, and/or (ii) if the
        +    Original Author and/or Licensor designate another party or parties (e.g. a
        +    sponsor institute, publishing entity, journal) for attribution in
        +    Licensor's copyright notice, terms of service or by other reasonable means,
        +    the name of such party or parties; the title of the Work if supplied; to
        +    the extent reasonably practicable, the Uniform Resource Identifier, if any,
        +    that Licensor specifies to be associated with the Work, unless such URI
        +    does not refer to the copyright notice or licensing information for the
        +    Work; and in the case of a Derivative Work, a credit identifying the use of
        +    the Work in the Derivative Work (e.g., "French translation of the Work by
        +    Original Author," or "Screenplay based on original Work by Original
        +    Author"). Such credit may be implemented in any reasonable manner;
        +    provided, however, that in the case of a Derivative Work or Collective
        +    Work, at a minimum such credit will appear where any other comparable
        +    authorship credit appears and in a manner at least as prominent as such
        +    other comparable authorship credit.
        +
        +5. Representations, Warranties and Disclaimer
        +
        +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS
        +THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND
        +CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING,
        +WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A
        +PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
        +ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
        +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH
        +EXCLUSION MAY NOT APPLY TO YOU.
        +
        +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN
        +NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL,
        +INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS
        +LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE
        +POSSIBILITY OF SUCH DAMAGES.
        +
        +7. Termination
        +
        +    This License and the rights granted hereunder will terminate automatically
        +    upon any breach by You of the terms of this License. Individuals or
        +    entities who have received Derivative Works or Collective Works from You
        +    under this License, however, will not have their licenses terminated
        +    provided such individuals or entities remain in full compliance with those
        +    licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
        +    this License.
        +
        +    Subject to the above terms and conditions, the license granted here is
        +    perpetual (for the duration of the applicable copyright in the Work).
        +    Notwithstanding the above, Licensor reserves the right to release the Work
        +    under different license terms or to stop distributing the Work at any time;
        +    provided, however that any such election will not serve to withdraw this
        +    License (or any other license that has been, or is required to be, granted
        +    under the terms of this License), and this License will continue in full
        +    force and effect unless terminated as stated above.
        +
        +8. Miscellaneous
        +
        +    Each time You distribute or publicly digitally perform the Work or a
        +    Collective Work, the Licensor offers to the recipient a license to the Work
        +    on the same terms and conditions as the license granted to You under this
        +    License.
        +
        +    Each time You distribute or publicly digitally perform a Derivative Work,
        +    Licensor offers to the recipient a license to the original Work on the same
        +    terms and conditions as the license granted to You under this License.
        +
        +    If any provision of this License is invalid or unenforceable under
        +    applicable law, it shall not affect the validity or enforceability of the
        +    remainder of the terms of this License, and without further action by the
        +    parties to this agreement, such provision shall be reformed to the minimum
        +    extent necessary to make such provision valid and enforceable.
        +
        +    No term or provision of this License shall be deemed waived and no breach
        +    consented to unless such waiver or consent shall be in writing and signed
        +    by the party to be charged with such waiver or consent.
        +
        +    This License constitutes the entire agreement between the parties with
        +    respect to the Work licensed here. There are no understandings, agreements
        +    or representations with respect to the Work not specified here. Licensor
        +    shall not be bound by any additional provisions that may appear in any
        +    communication from You. This License may not be modified without the mutual
        +    written agreement of the Licensor and You.
        +
        +Creative Commons is not a party to this License, and makes no warranty
        +whatsoever in connection with the Work. Creative Commons will not be liable to
        +You or any party on any legal theory for any damages whatsoever, including
        +without limitation any general, special, incidental or consequential damages
        +arising in connection to this license. Notwithstanding the foregoing two (2)
        +sentences, if Creative Commons has expressly identified itself as the Licensor
        +hereunder, it shall have all rights and obligations of Licensor.
        +
        +Except for the limited purpose of indicating to the public that the Work is
        +licensed under the CCPL, neither party will use the trademark "Creative
        +Commons" or any related trademark or logo of Creative Commons without the prior
        +written consent of Creative Commons. Any permitted use will be in compliance
        +with Creative Commons' then-current trademark usage guidelines, as may be
        +published on its website or otherwise made available upon request from time to
        +time.
        +
        +Creative Commons may be contacted at https://creativecommons.org/.
        +
        +-----------------------------------------------------
        +jquery-resizable.min.js
        +
        +/*
        + * jQuery JavaScript Library v1.3.2
        + * http://jquery.com/
        + *
        + * Copyright (c) 2009 John Resig
        + * Dual licensed under the MIT and GPL licenses.
        + * http://docs.jquery.com/License
        + *
        + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
        + * Revision: 6246
        + */
        +
        +The MIT License (MIT)
        +
        +Copyright (c) 2009 John Resig
        +
        +Permission is hereby granted, free of charge, to any person obtaining a copy of
        +this software and associated documentation files (the "Software"), to deal in
        +the Software without restriction, including without limitation the rights to
        +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        +of the Software, and to permit persons to whom the Software is furnished to do
        +so, subject to the following conditions:
        +
        +The above copyright notice and this permission notice shall be included in all
        +copies or substantial portions of the Software.
        +
        +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        +SOFTWARE.
        +
        +-----------------------------------------------------
        +jquery-1.6.2.min.js
        +
        +/*!
        + * jQuery JavaScript Library v1.6.2
        + * http://jquery.com/
        + *
        + * Copyright 2011, John Resig
        + * Dual licensed under the MIT or GPL Version 2 licenses.
        + * http://jquery.org/license
        + *
        + * Includes Sizzle.js
        + * http://sizzlejs.com/
        + * Copyright 2011, The Dojo Foundation
        + * Released under the MIT, BSD, and GPL Licenses.
        + *
        + * Date: Thu Jun 30 14:16:56 2011 -0400
        + */
        +
        +The MIT License (MIT)
        +
        +Copyright (c) 2011 John Resig, and The Dojo Foundation
        +
        +Permission is hereby granted, free of charge, to any person obtaining a copy of
        +this software and associated documentation files (the "Software"), to deal in
        +the Software without restriction, including without limitation the rights to
        +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        +of the Software, and to permit persons to whom the Software is furnished to do
        +so, subject to the following conditions:
        +
        +The above copyright notice and this permission notice shall be included in all
        +copies or substantial portions of the Software.
        +
        +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        +SOFTWARE.
        +
        diff --git a/make/tools/droiddoc/README b/make/tools/droiddoc/README
        new file mode 100644
        index 0000000..ef28e63
        --- /dev/null
        +++ b/make/tools/droiddoc/README
        @@ -0,0 +1,5 @@
        +If you're looking for the templates-sdk/ files, they've moved
        +to external/doclava/res/assets/.
        +
        +The remaining template files here should also be eventually removed
        +so that we can unify the structure and style of all DocLava builds.
        diff --git a/make/tools/droiddoc/templates-pdk/assets/android-developer-core.css b/make/tools/droiddoc/templates-pdk/assets/android-developer-core.css
        new file mode 100644
        index 0000000..c4fffaa
        --- /dev/null
        +++ b/make/tools/droiddoc/templates-pdk/assets/android-developer-core.css
        @@ -0,0 +1,1196 @@
        +/* file: android-developer-core.css
        +   author: smain
        +   date: september 2008
        +   info: core developer styles (developer.android.com)
        +*/
        +
        +
        +/* RESET STYLES */
        +
        +html,body,div,h1,h2,h3,h4,h5,h6,p,img,
        +dl,dt,dd,ol,ul,li,table,caption,tbody,
        +tfoot,thead,tr,th,td,form,fieldset,
        +embed,object,applet {
        +  margin: 0;
        +  padding: 0;
        +  border: 0;
        +}
        +
        +/* BASICS */
        +
        +html, body {
        +  overflow:hidden; /* keeps scrollbar off IE */
        +  background-color:#fff;
        +}
        +
        +body {
        +  font-family:arial,sans-serif;
        +  color:#000;
        +  font-size:13px;
        +  color:#333;
        +  background-image:url(images/bg_fade.jpg);
        +  background-repeat:repeat-x;
        +}
        +
        +a, a code {
        +  color:#006699;
        +}
        +
        +a:active,
        +a:active code {
        +  color:#f00;
        +} 
        +
        +a:visited,
        +a:visited code {
        +  color:#006699;
        +}
        +
        +input, select,
        +textarea, option, label {
        +  font-family:inherit;
        +  font-size:inherit;
        +  padding:0;
        +  margin:0;
        +  vertical-align:middle;
        +}
        +
        +option {
        +  padding:0 4px;
        +}
        +
        +p, form {
        +  padding:0;
        +  margin:0 0 1em;
        +}
        +
        +code, pre {
        +  color:#007000;
        +  font-family:monospace;
        +  line-height:1em;
        +}
        +
        +var {
        +  color:#007000;
        +  font-style:italic;
        +}
        +
        +pre {
        +  border:1px solid #ccc;
        +  background-color:#fafafa;
        +  padding:10px;
        +  margin:0 0 1em 1em;
        +  overflow:auto;
        +  line-height:inherit; /* fixes vertical scrolling in webkit */
        +}
        +
        +h1,h2,h3,h4,h5 {
        +  margin:1em 0;
        +  padding:0;
        +}
        +
        +p,ul,ol,dl,dd,dt,li {
        +  line-height:1.3em;
        +}
        +
        +ul,ol {
        +  margin:0 0 .8em;
        +  padding:0 0 0 2em;
        +}
        +
        +li {
        +  padding:0 0 .5em;
        +}
        +
        +dl {
        +  margin:0 0 1em 0;
        +  padding:0;
        +}
        +
        +dt {
        +  margin:0;
        +  padding:0;
        +}
        +
        +dd {
        +  margin:0 0 1em;
        +  padding:0 0 0 2em;
        +}
        +
        +li p {
        +  margin:.5em 0 0;
        +}
        +
        +dd p {
        +  margin:1em 0 0;
        +}
        +
        +li pre, li table, li img {
        +  margin:.5em 0 0 1em;
        +}
        +
        +dd pre,
        +#jd-content dd table,
        +#jd-content dd img {
        +  margin:1em 0 0 1em;
        +}
        +
        +li ul,
        +li ol,
        +dd ul,
        +dd ol {
        +  margin:0;
        +  padding: 0 0 0 2em;
        +}
        +
        +li li,
        +dd li {
        +  margin:0;
        +  padding:.5em 0 0;
        +}
        +
        +dl dl,
        +ol dl,
        +ul dl {
        +  margin:0 0 1em;
        +  padding:0;
        +}
        +
        +table {
        +  font-size:1em;
        +  margin:0 0 1em;
        +  padding:0;
        +  border-collapse:collapse;
        +  border-width:0;
        +  empty-cells:show;
        +}
        +
        +td,th {
        +  border:1px solid #ccc;
        +  padding:6px 12px;
        +  text-align:left;
        +  vertical-align:top;
        +  background-color:inherit;
        +}
        +
        +th {
        +  background-color:#dee8f1;
        +}
        +
        +td > p:last-child {
        +  margin:0;
        +}
        +
        +hr.blue {
        +  background-color:#DDF0F2;
        +  border:none;
        +  height:5px;
        +  margin:20px 0 10px;
        +}
        +
        +blockquote {
        +  margin: 0 0 1em 1em;
        +  padding: 0 4em 0 1em;
        +  border-left:2px solid #eee;
        +}
        +/* LAYOUT */
        +
        +#body-content {
        +  /* "Preliminary" watermark for preview releases and interim builds.
        +  background:transparent url(images/preliminary.png) repeat scroll 0 0; */
        +  margin:0;
        +  position:relative;
        +  width:100%;
        +}
        +
        +#header {
        +  height: 114px;
        +  position:relative;
        +  z-index:100;
        +  min-width:675px; /* min width for the tabs, before they wrap */
        +  padding:0 10px;
        +  border-bottom:3px solid #94b922;
        +}
        +
        +#headerLeft{
        +  padding: 25px 0 0;
        +}
        +
        +#headerLeft img{
        +  height:50px;
        +}
        +
        +#headerRight {
        +  position:absolute;
        +  right:0;
        +  top:0;
        +  text-align:right;
        +}
        +
        +/* Tabs in the header */
        +
        +#header ul {
        +  list-style: none;
        +  margin: 7px 0 0;
        +  padding: 0;
        +  height: 29px;
        +}
        +
        +#header li {
        +  float: left;
        +  margin: 0px 2px 0px 0px;
        +  padding:0;
        +}
        +
        +#header li a {
        +  text-decoration: none;
        +  display: block;
        +  background-image: url(images/bg_images_sprite.png);
        +  background-position: 0 -58px;
        +  background-repeat: no-repeat;
        +  color: #666;
        +  font-size: 13px;
        +  font-weight: bold;
        +  width: 94px;
        +  height: 29px;
        +  text-align: center;
        +  margin: 0px;
        +}
        +
        +#header li a:hover {
        +  background-image: url(images/bg_images_sprite.png);
        +  background-position: 0 -29px;
        +  background-repeat: no-repeat;
        +}
        +
        +#header li a span {
        +  position:relative;
        +  top:7px;
        +}
        +
        +#header li a span+span {
        +  display:none;
        +}
        +
        +/* tab highlighting */
        +
        +.home #home-link a,
        +.guide #guide-link a,
        +.reference #reference-link a,
        +.sdk #sdk-link a,
        +.resources #resources-link a,
        +.videos #videos-link a {
        +  background-image: url(images/bg_images_sprite.png);
        +  background-position: 0 0;
        +  background-repeat: no-repeat;
        +  color: #fff;
        +  font-weight: bold;
        +  cursor:default;
        +}
        +
        +.home #home-link a:hover,
        +.guide #guide-link a:hover,
        +.reference #reference-link a:hover,
        +.sdk #sdk-link a:hover,
        +.resources #resources-link a:hover,
        +.videos #videos-link  a:hover {
        +  background-image: url(images/bg_images_sprite.png);
        +  background-position: 0 0;
        +}
        +
        +#headerLinks {
        +  margin:10px 10px 0 0;
        +  height:13px;
        +  font-size: 11px;
        +  vertical-align: top;
        +}
        +
        +#headerLinks a {
        +  color: #7FA9B5;
        +}
        +
        +#headerLinks img {
        +  vertical-align:middle;
        +}
        +
        +#language {
        +  margin:0 10px 0 4px;
        +}
        +
        +#search {
        +  height:45px;
        +  margin:15px 10px 0 0;
        +}
        +
        +/* MAIN BODY */
        +
        +#mainBodyFluid {
        +  margin: 20px 10px;
        +  color:#333;
        +}
        +
        +#mainBodyFixed {
        +  margin: 20px 10px;
        +  color: #333;
        +  width:930px;
        +  position:relative;
        +}
        +
        +#mainBodyFixed h3,
        +#mainBodyFluid h3 {
        +  color:#336666;
        +  font-size:1.25em;
        +  margin: 0em 0em 0em 0em;
        +  padding-bottom:.5em;
        +}
        +
        +#mainBodyFixed h2,
        +#mainBodyFluid h2 {
        +  color:#336666;
        +  font-size:1.25em;
        +  margin: 0;
        +  padding-bottom:.5em;
        +}
        +
        +#mainBodyFixed h1,
        +#mainBodyFluid h1 {
        +  color:#435A6E;
        +  font-size:1.7em;
        +  margin: 1em 0;
        +}
        +
        +#mainBodyFixed .green,
        +#mainBodyFluid .green,
        +#jd-content .green {
        +  color:#7BB026;
        +  background-color:none;
        +}
        +
        +#mainBodyLeft {
        +  float: left;
        +  width: 600px;
        +  margin-right: 20px;
        +  color: #333;
        +  position:relative;
        +}
        +
        +div.indent {
        +  margin-left: 40px;
        +  margin-right: 70px;
        +}
        +
        +#mainBodyLeft p {
        +  color: #333;
        +  font-size: 13px;
        +}
        +
        +#mainBodyLeft p.blue {
        +  color: #669999;
        +}
        +
        +#mainBodyLeft #communityDiv {
        +  float: left;
        +  background-image:url(images/bg_community_leftDiv.jpg);
        +  background-repeat: no-repeat;
        +  width: 581px;
        +  height: 347px;
        +  padding: 20px 0px 0px 20px;
        +}
        +
        +#mainBodyRight {
        +  float: left;
        +  width: 300px;
        +  color: #333;
        +}
        +
        +#mainBodyRight p {
        +  padding-right: 50px;
        +  color: #333;
        +}
        +
        +#mainBodyRight table {
        +  width: 100%;
        +}
        +
        +#mainBodyRight td {
        +  border:0px solid #666;
        +  padding:0px 5px;
        +  text-align:left;
        +}
        +
        +#mainBodyRight td p {
        +  margin:0 0 1em 0;
        +}
        +
        +#mainBodyRight .blueBorderBox {
        +  border:5px solid #ddf0f2;
        +  padding:18px 18px 18px 18px;
        +  text-align:left;
        +}
        +
        +#mainBodyFixed .seperator {
        +  background-image:url(images/hr_gray_side.jpg);
        +  background-repeat:no-repeat;
        +  width: 100%;
        +  float: left;
        +  clear: both;
        +}
        +
        +#mainBodyBottom {
        +  float: left;
        +  width: 100%;
        +  clear:both;
        +  color: #333;
        +}
        +
        +#mainBodyBottom .seperator {
        +  background-image:url(images/hr_gray_main.jpg);
        +  background-repeat:no-repeat;
        +  width: 100%;
        +  float: left;
        +  clear: both;
        +}
        +
        +/* FOOTER */
        +
        +#footer {
        +  float: left;
        +  width:90%;
        +  margin: 20px;
        +  color: #aaa;
        +  font-size: 11px;
        +}
        +
        +#footer a {
        +  color: #aaa;
        +  font-size: 11px;
        +}
        +
        +#footer a:hover {
        +  text-decoration: underline;
        +  color:#aaa;
        +}
        +
        +#footerlinks {
        +  margin-top:2px;
        +}
        +
        +#footerlinks a,
        +#footerlinks a:visited {
        +  color:#006699;
        +}
        +
        +/* SEARCH FILTER */
        +
        +#search_autocomplete {
        +  color:#aaa;
        +}
        +
        +#search-button {
        +  display:inline;
        +}
        +
        +#search_filtered_div {
        +  position:absolute;
        +  margin-top:-1px;
        +  z-index:101;
        +  border:1px solid #BCCDF0;
        +  background-color:#fff;
        +}
        +
        +#search_filtered {
        +  min-width:100%;
        +}
        +#search_filtered td{
        +  background-color:#fff;
        +  border-bottom: 1px solid #669999;
        +  line-height:1.5em;
        +}
        +
        +#search_filtered .jd-selected {
        +  background-color: #94b922;
        +  cursor:pointer;
        +}
        +#search_filtered .jd-selected,
        +#search_filtered .jd-selected a {
        +  color:#fff;
        +}
        +
        +.no-display {
        +  display: none;
        +}
        +
        +.jd-autocomplete {
        +  font-family: Arial, sans-serif;
        +  padding-left: 6px;
        +  padding-right: 6px;
        +  padding-top: 1px;
        +  padding-bottom: 1px;
        +  font-size: 0.81em;
        +  border: none;
        +  margin: 0;
        +  line-height: 1.05em;
        +}
        +
        +.show-row {
        +  display: table-row;
        +}
        +.hide-row {
        +  display: hidden;
        +}
        +
        +/* SEARCH */
        +
        +/* restrict global search form width */
        +#searchForm {
        +  width:350px;
        +}
        +
        +#searchTxt {
        +  width:200px;
        +}
        +
        +/* disable twiddle and size selectors for left column */
        +#leftSearchControl div {
        +  width: 100%;
        +}
        +
        +#leftSearchControl .gsc-twiddle {
        +  background-image : none;
        +}
        +
        +#leftSearchControl td, #searchForm td {
        +  border: 0px solid #000;
        +}
        +
        +#leftSearchControl .gsc-resultsHeader .gsc-title {
        +  padding-left : 0px;
        +  font-weight : bold;
        +  font-size : 13px;
        +  color:#006699;
        +  display : none;
        +}
        +
        +#leftSearchControl .gsc-resultsHeader div.gsc-results-selector {
        +  display : none;
        +}
        +
        +#leftSearchControl .gsc-resultsRoot {
        +  padding-top : 6px;
        +}
        +
        +#leftSearchControl div.gs-visibleUrl-long {
        +  display : block;
        +  color:#006699;
        +}
        +
        +.gsc-webResult div.gs-visibleUrl-short,
        +table.gsc-branding,
        +.gsc-clear-button {
        +  display : none;
        +}
        +
        +.gsc-cursor-box .gsc-cursor div.gsc-cursor-page,
        +.gsc-cursor-box .gsc-trailing-more-results a.gsc-trailing-more-results,
        +#leftSearchControl a,
        +#leftSearchControl a b {
        +  color:#006699;
        +}
        +
        +.gsc-resultsHeader {
        +  display: none;
        +}
        +
        +/* Disable built in search forms */
        +.gsc-control form.gsc-search-box {
        +  display : none;
        +}
        +table.gsc-search-box {
        +  margin:6px 0 0 0;
        +  border-collapse:collapse;
        +}
        +
        +td.gsc-input {
        +  padding:0 2px;
        +  width:100%;
        +  vertical-align:middle;
        +}
        +
        +input.gsc-input {
        +  border:1px solid #BCCDF0;
        +  width:99%;
        +  padding-left:2px;
        +  font-size:.95em;
        +}
        +
        +td.gsc-search-button {
        +  text-align: right;
        +  padding:0;
        +  vertical-align:top;
        +}
        +
        +#search-button {
        +  margin:0 0 0 2px;
        +  font-size:11px;
        +}
        +
        +/* search result tabs */
        +
        +#doc-content .gsc-control {
        +  position:relative;
        +}
        +
        +#doc-content .gsc-tabsArea {
        +  position:relative;
        +  white-space:nowrap;
        +}
        +
        +#doc-content .gsc-tabHeader {
        +  padding: 3px 6px;
        +  position:relative;
        +  width:auto;
        +}
        +
        +#doc-content .gsc-tabHeader.gsc-tabhActive {
        +  border-top: 2px solid #94B922;
        +}
        +
        +#doc-content h2#searchTitle {
        +  padding:0;
        +}
        +
        +#doc-content .gsc-resultsbox-visible {
        +  padding:1em 0 0 6px;
        +}
        +
        +/* CAROUSEL */
        +
        +#homeMiddle {
        +  padding: 0px 0px 0px 0px;
        +  float: left;
        +  width: 584px;
        +  height: 627px;
        +  position:relative;
        +}
        +
        +#topAnnouncement {
        +  background:url(images/home/bg_home_announcement.png) no-repeat 0 0;
        +}
        +  
        +#homeTitle {
        +  padding:15px 15px 0;
        +  height:30px;
        +}
        +
        +#homeTitle h2 {
        +  padding:0;
        +}
        +
        +#announcement-block {
        +  padding:0 15px 0;
        +  overflow:hidden;
        +  background: url(images/hr_gray_side.jpg) no-repeat 15px 0;
        +  zoom:1;
        +}
        +
        +#announcement-block>* {
        +  padding:15px 0 0;
        +}
        +
        +#announcement-block img {
        +  float:left;
        +  margin:0 30px 0 0;
        +}
        +
        +#announcement {
        +  float:left;
        +  margin:0;
        +}
        +
        +#carousel {
        +  background:url(images/home/bg_home_carousel.png) no-repeat 0 0;
        +  position:relative;
        +  height:400px;
        +}
        +
        +#carouselMain {
        +  background: url(images/home/bg_home_carousel_board.png) 0 0 no-repeat;
        +  height:auto;
        +  padding: 25px 21px 0;
        +  overflow:hidden;
        +  position:relative;
        +  zoom:1; /*IE6*/
        +}
        +
        +#carouselMain img {
        +  margin:0;
        +}
        +
        +#carouselMain .bulletinDesc h3 {
        +  margin:0;
        +  padding:0;
        +}
        +
        +#carouselMain .bulletinDesc p {
        +  margin:0;
        +  padding:0.7em 0 0;
        +}
        +
        +#carouselWheel {
        +  background: url(images/home/bg_home_carousel_wheel.png) 0 0 no-repeat;
        +  padding-top:40px;
        +  height:150px;
        +}
        +
        +.clearer { clear:both; }
        +
        +a#arrow-left, a#arrow-right {
        +  float:left;
        +  width:42px;
        +  height:42px;
        +  background-image:url(images/home/carousel_buttons_sprite.png);
        +  background-repeat:no-repeat;
        +}
        +a#arrow-left {
        +  margin:35px 3px 0 10px;
        +}
        +a#arrow-right {
        +  margin:35px 10px 0 0;
        +}
        +a.arrow-left-off,
        +a#arrow-left.arrow-left-off:hover {
        +  background-position:0 0;
        +}
        +a.arrow-right-off,
        +a#arrow-right.arrow-right-off:hover {
        +  background-position:-42px 0;
        +}
        +a#arrow-left:hover {
        +  background-position:0 -42px;
        +}
        +a#arrow-right:hover {
        +  background-position:-42px -42px;
        +}
        +a.arrow-left-on {
        +  background-position:0 0;
        +}
        +a.arrow-right-on {
        +  background-position:-42px 0;
        +}
        +a.arrow-right-off,
        +a.arrow-left-off {
        +  cursor:default;
        +}
        +
        +.app-list-container {
        +  margin:0 20px;
        +  position:relative;
        +  width:100%;
        +}
        +
        +div#list-clip {
        +  height:110px;
        +  width:438px;
        +  overflow:hidden;
        +  position:relative;
        +  float:left;
        +}
        +
        +div#app-list {
        +  left:0;
        +  z-index:1;
        +  position:absolute;
        +  margin:11px 0 0;
        +  _margin-top:13px;
        +  width:1000%;
        +}
        +
        +#app-list a {
        +  display:block;
        +  float:left;
        +  height:90px;
        +  width:90px;
        +  margin:0 24px 0;
        +  padding:3px;
        +  background:#99cccc;
        +  -webkit-border-radius:7px;
        +  -moz-border-radius:7px;
        +  border-radius:7px;
        +  text-decoration:none;
        +  text-align:center;
        +  font-size:11px;
        +  line-height:11px;
        +}
        +
        +#app-list a span {
        +  position:relative;
        +  top:-4px;
        +}
        +
        +#app-list img {
        +  width:90px;
        +  height:70px;
        +  margin:0;
        +}
        +
        +#app-list a.selected,
        +#app-list a:active.selected,
        +#app-list a:hover.selected {
        +  background:#A4C639;
        +  color:#fff;
        +  cursor:default;
        +  text-decoration:none;
        +}
        +
        +#app-list a:hover,
        +#app-list a:active {
        +  background:#ff9900;
        +}
        +
        +#app-list a:hover span,
        +#app-list a:active span {
        +  text-decoration:underline;
        +}
        +
        +#droid-name {
        +  padding-top:.5em;
        +  color:#666;
        +  padding-bottom:.25em;
        +}
        +
        +/*IE6*/
        +* html #app-list a { zoom: 1; margin:0 24px 0 15px;}
        +
        +* html #list-clip {
        +  width:430px !important;
        +}
        +
        +/*carousel bulletin layouts*/
        +/*460px width*/
        +/*185px height*/
        +.img-left {
        +  float:left;
        +  width:230px;
        +  overflow:hidden;
        +  padding:8px 0 8px 8px;
        +}
        +.desc-right {
        +  float:left;
        +  width:270px;
        +  padding:10px;
        +}
        +.img-right {
        +  float:right;
        +  width:220px;
        +  overflow:hidden;
        +  padding:8px 8px 8px 0;
        +}
        +.desc-left {
        +  float:right;
        +  width:280px;
        +  padding:10px;
        +  text-align:right;
        +}
        +.img-top {
        +  padding:20px 20px 0;
        +}
        +.desc-bottom {
        +  padding:10px;
        +}
        +
        +
        +/* VIDEO PAGE */
        +
        +#mainBodyLeft.videoPlayer {
        +  width:570px;
        +}
        +
        +#mainBodyRight.videoPlayer {
        +  width:330px;
        +}
        +
        +/* player */
        +
        +#videoPlayerBox {
        +  background-color: #DAF3FC;
        +  border-radius:7px;
        +  -moz-border-radius:7px;
        +  -webkit-border-radius:7px;
        +  width:530px;
        +  padding:20px;
        +  border:1px solid #d3ecf5;
        +  box-shadow:2px 3px 1px #eee;
        +  -moz-box-shadow:2px 3px 1px #eee;
        +  -webkit-box-shadow:2px 3px 1px #eee;
        +}
        +
        +#videoBorder {
        +  background-color: #FFF;
        +  min-height:399px;
        +  height:auto !important;
        +  border:1px solid #ccdada;
        +  border-radius:7px 7px 0 0;
        +  -moz-border-radius:7px 7px 0 0;
        +  -webkit-border-top-left-radius:7px;
        +  -webkit-border-top-right-radius:7px;
        +}
        +
        +#videoPlayerTitle {
        +  width:500px;
        +  padding:15px 15px 0;
        +}
        +
        +#videoPlayerTitle h2 {
        +  font-weight:bold;
        +  font-size:1.2em;
        +  color:#336666;
        +  margin:0;
        +  padding:0;
        +}
        +
        +#objectWrapper {
        +  padding:15px 15px;
        +  height:334px;
        +  width:500px;
        +}
        +
        +/* playlist tabs */
        +
        +ul#videoTabs {
        +  list-style-type:none;
        +  padding:0;
        +  clear:both;
        +  margin:0;
        +  padding: 20px 0 0 15px;
        +  zoom:1; /* IE7/8, otherwise top-padding is double */
        +}
        +
        +ul#videoTabs li {
        +  display:inline;
        +  padding:0;
        +  margin:0 3px 0 0;
        +  line-height:2em;
        +}
        +
        +ul#videoTabs li a {
        +  border-radius:7px 7px 0 0;
        +  -moz-border-radius:7px 7px 0 0;
        +  -webkit-border-top-left-radius:7px;
        +  -webkit-border-top-right-radius:7px;
        +  background:#95c0d0;
        +  color:#fff;
        +  text-decoration:none;
        +  padding:.45em 1.5em;
        +  font-weight:bold;
        +}
        +
        +ul#videoTabs li.selected a {
        +  font-weight:bold;
        +  text-decoration:none;
        +  color:#555;
        +  background:#daf3fc;
        +  border-bottom:1px solid #daf3fc;
        +}
        +
        +ul#videoTabs li:hover a {
        +  background:#85acba;
        +}
        +
        +ul#videoTabs li.selected:hover a {
        +  background:#daf3fc;
        +}
        +
        +/* playlists */
        +
        +#videos {
        +  background:#daf3fc;
        +  margin-bottom:1.5em;
        +  padding:15px;
        +  border-radius:5px;
        +  -moz-border-radius:5px;
        +  -webkit-border-radius:5px;
        +  box-shadow:2px 3px 1px #eee;
        +  -moz-box-shadow:2px 3px 1px #eee;
        +  -webkit-box-shadow:2px 3px 1px #eee;
        +}
        +
        +#videos div {
        +  display:none;
        +}
        +
        +#videos div.selected {
        +  display:block;
        +}
        +
        +ul.videoPreviews {
        +  list-style:none;
        +  padding:0;
        +  margin:0;
        +  zoom:1; /* IE, otherwise, layout doesn't update when showing 'more' */
        +}
        +
        +ul.videoPreviews li {
        +  margin:0 0 5px;
        +  padding:0;
        +  overflow:hidden;
        +  position:relative;
        +}
        +
        +#mainBodyFixed ul.videoPreviews h3 {
        +  font-size: 12px;
        +  margin:0 0 1em 130px;
        +  padding:0;
        +  font-weight:bold;
        +  color:inherit;
        +}
        +
        +ul.videoPreviews a {
        +  margin:1px;
        +  padding:10px;
        +  text-decoration:none;
        +  height:90px;
        +  display:block;
        +  border-radius:5px;
        +  -moz-border-radius:5px;
        +  -webkit-border-radius:5px;
        +  background-color:transparent;
        +}
        +
        +ul.videoPreviews a:hover {
        +  background-color:#FFF;
        +  border:none; /* IE8, otherwise, bg doesn't work */
        +}
        +
        +ul.videoPreviews a.selected {
        +  background-color: #FF9900;
        +}
        +
        +ul.videoPreviews img {
        +  float:left;
        +  clear:left;
        +  margin:0;
        +}
        +
        +ul.videoPreviews h3 {
        +  font-size:12px;
        +  font-weight:bold;
        +  text-decoration:none;
        +  margin:0 0 1em 130px;
        +  padding:0;
        +}
        +
        +ul.videoPreviews p {
        +  font-size: 12px;
        +  text-decoration:none;
        +  margin:0 0 1.2em 130px;
        +}
        +
        +ul.videoPreviews p.full {
        +  display:none;
        +}
        +
        +ul.videoPreviews span.more {
        +  padding:0 0 0 12px;
        +  background:url(images/arrow_bluelink_down.png) 0 2px no-repeat;
        +}
        +
        +ul.videoPreviews span.less {
        +  padding:0 0 0 12px;
        +  background:url(images/arrow_bluelink_up.png) 0 2px no-repeat;
        +  display:none;
        +}
        +
        +ul.videoPreviews p.toggle {
        +  position:absolute;
        +  margin:0;
        +  margin-top:-23px; /* instead of bottom:23px, because IE won't do it correctly */
        +  left:140px;
        +}
        +
        +ul.videoPreviews p.toggle a {
        +  height:auto;
        +  margin:0;
        +  padding:0;
        +  zoom:1; /* IE6, otherwise the margin considers the img on redraws */
        +}
        +
        +ul.videoPreviews p.toggle a:hover {
        +  text-decoration:underline;
        +  background:transparent; /* IE6, otherwise it inherits white */
        +}
        +
        +/* featured videos */
        +
        +#mainBodyRight h2 {
        +  padding:0 0 5px;
        +}
        +
        +#mainBodyRight ul.videoPreviews {
        +  margin:10px 0 0;
        +}
        +
        +#mainBodyRight ul.videoPreviews li {
        +  font-size:11px;
        +  line-height:13px;
        +  margin:0 0 5px;
        +  padding:0;
        +}
        +
        +#mainBodyRight ul.videoPreviews h3 {
        +  padding:0;
        +  margin:0;
        +  font-size:100%;
        +}
        +
        +#mainBodyRight ul.videoPreviews a {
        +  text-decoration:none;
        +  height:108px;
        +  border:1px solid #FFF;
        +}
        +
        +#mainBodyRight ul.videoPreviews a:hover {
        +  border:1px solid #CCDADA;
        +}
        +
        +#mainBodyRight ul.videoPreviews a.selected {
        +  border:1px solid #FFF;
        +}
        +
        +#mainBodyRight ul.videoPreviews p {
        +  line-height:1.2em;
        +  padding:0;
        +  margin:4px 0 0 130px;
        +}
        +
        +#mainBodyRight ul.videoPreviews img {
        +  margin-top:5px;
        +}
        +
        +/* Pretty printing styles. Used with prettify.js. */
        +
        +.str { color: #080; }
        +.kwd { color: #008; }
        +.com { color: #800; }
        +.typ { color: #606; }
        +.lit { color: #066; }
        +.pun { color: #660; }
        +.pln { color: #000; }
        +dl.tag-list dt code,
        +.tag { color: #008; }
        +dl.atn-list dt code,
        +.atn { color: #828; }
        +.atv { color: #080; }
        +.dec { color: #606; }
        +
        +@media print {
        +  .str { color: #060; }
        +  .kwd { color: #006; font-weight: bold; }
        +  .com { color: #600; font-style: italic; }
        +  .typ { color: #404; font-weight: bold; }
        +  .lit { color: #044; }
        +  .pun { color: #440; }
        +  .pln { color: #000; }
        +  .tag { color: #006; font-weight: bold; }
        +  .atn { color: #404; }
        +  .atv { color: #060; }
        +}
        diff --git a/make/tools/droiddoc/templates-pdk/assets/android-developer-docs-devguide.css b/make/tools/droiddoc/templates-pdk/assets/android-developer-docs-devguide.css
        new file mode 100644
        index 0000000..d8bd3b3
        --- /dev/null
        +++ b/make/tools/droiddoc/templates-pdk/assets/android-developer-docs-devguide.css
        @@ -0,0 +1,19 @@
        +
        +@import url("android-developer-docs.css");
        +
        +/* Page title */
        +
        +#jd-header h1 {
        +  padding: 8px 0 0 0;
        +}
        +
        +/* Page content container */
        +
        +#jd-header table {
        +margin: 0 0 1em 1em;
        +}
        +
        +#jd-content table table,
        +#jd-content table img {
        +  margin:1em 0;
        +}
        \ No newline at end of file
        diff --git a/make/tools/droiddoc/templates-pdk/assets/android-developer-docs.css b/make/tools/droiddoc/templates-pdk/assets/android-developer-docs.css
        new file mode 100644
        index 0000000..b8b9c71
        --- /dev/null
        +++ b/make/tools/droiddoc/templates-pdk/assets/android-developer-docs.css
        @@ -0,0 +1,1576 @@
        +/* file: android-developer-docs.css
        +   author: smain
        +   date: september 2008
        +   info: developer doc styles (developer.android.com)
        +*/
        +
        +@import url("android-developer-core.css");
        +
        +#title {
        +  border-bottom: 4px solid #ccc;
        +  display:none;
        +}
        +
        +#title h1 {
        +  color:#336666;
        +  margin:0;
        +  padding: 5px 10px;
        +  font-size: 1em;
        +  line-height: 15px;
        +}
        +
        +#title h1 .small{
        +  color:#000;
        +  margin:0;
        +  font-size: 13px;
        +  padding:0 0 0 15px;
        +}
        +
        +/* SIDE NAVIGATION */
        +
        +#side-nav {
        +  padding:0 6px 0 0;
        +  background-color: #fff;
        +  font-size:12px;
        +}
        +
        +#resize-packages-nav {
        +/* keeps the resize handle below the h-scroll handle */
        +  height:270px;
        +  overflow:hidden;
        +  max-height:100%;
        +}
        +
        +#packages-nav {
        +  height:270px;
        +  max-height:inherit;
        +  position:relative;
        +  overflow:auto;
        +}
        +
        +#classes-nav,
        +#devdoc-nav {
        +  overflow:auto;
        +  position:relative;
        +}
        +
        +#side-nav ul {
        +  list-style: none;
        +  margin: 0;
        +  padding:5px 0;
        +}
        +
        +#side-nav ul ul {
        +  margin: .5em 0 0 0;
        +  padding: 0;
        +}
        +
        +#side-nav li {
        +  padding:0;
        +  padding:1px 0 1px 0;
        +  zoom:1;
        +}
        +
        +#side-nav li span.heading,
        +#side-nav li h2 {
        +  display:block;
        +  font-size:12px;
        +  font-weight: bold;
        +  margin:.5em 0 0 0;
        +  padding: 3px 0 1px 9px;
        +}
        +
        +#side-nav li a {
        +  display: inline-block; /* needed to apply padding to line-wraps */
        +  text-decoration:none;
        +  padding: 0 0 0 18px;
        +  zoom:1;
        +}
        +
        +#side-nav li a span+span {
        +  display:none;
        +}
        +
        +#side-nav li a:hover {
        +  text-decoration:underline;
        +}
        +
        +#side-nav li a+a {
        +  padding: 0;
        +}
        +/*second level (nested) list*/
        +#side-nav li li li a {
        +  padding: 0 0 0 28px;
        +}
        +/*third level (nested) list*/
        +#side-nav li li li li a {
        +  padding: 0 0 0 38px;
        +}
        +
        +#side-nav .selected {
        +  background-color: #435a6e;
        +  color: #fff;
        +  font-weight:bold;
        +}
        +
        +#side-nav .selected a {
        +  color: #fff;
        +  text-decoration:none;
        +}
        +
        +#side-nav strong {
        +  display:block;
        +}
        +
        +#side-nav .toggle-list .toggle-img {
        +  margin:0;
        +  padding:0;
        +  position:absolute;
        +  top:0;
        +  left:0;
        +  height:16px;
        +  width:15px;
        +  outline-style:none;
        +}
        +/* second-level toggle */
        +#side-nav .toggle-list .toggle-list .toggle-img {
        +  left:10px;
        +}
        +
        +#side-nav .closed .toggle-img,
        +#side-nav .open .closed .toggle-img {
        +  background:url('images/triangle-closed-small.png') 7px 4px no-repeat;
        +}
        +#side-nav .open .toggle-img {
        +  background:url('images/triangle-opened-small.png') 7px 4px no-repeat;
        +}
        +
        +#side-nav .toggle-list {
        +  position:relative;
        +}
        +
        +#side-nav .toggle-list ul {
        +  margin:0;
        +  display:none;
        +}
        +
        +#side-nav .toggle-list div {
        +  display:block;
        +}
        +
        +#index-links .selected {
        +  background-color: #fff;
        +  color: #000;
        +  font-weight:normal;
        +  text-decoration:none;
        +}
        +
        +#index-links {
        +  padding:7px 0 4px 10px;
        +}
        +
        +/* nav tree */
        +
        +#nav-tree ul {
        +  padding:5px 0 1.5em;
        +}
        +
        +#side-nav #nav-tree ul li a,
        +#side-nav #nav-tree ul li span.no-children {
        +  padding: 0 0 0 0;
        +  margin: 0;
        +}
        +
        +#nav-tree .plus {
        +  margin: 0 3px 0 0;
        +}
        +
        +#nav-tree ul ul {
        +  list-style: none;
        +  margin: 0;
        +  padding: 0 0 0 0;
        +}
        +
        +#nav-tree ul li {
        +  margin: 0;
        +  padding: 0 0 0 0;
        +  white-space: nowrap;
        +}
        +
        +#nav-tree .children_ul {
        +  margin:0;
        +}
        +
        +#nav-tree a.nolink {
        +  color: black;
        +  text-decoration: none;
        +}
        +
        +#nav-tree span.label {
        +  width: 100%;
        +}
        +
        +#nav-tree {
        +  overflow-x: auto;
        +  overflow-y: scroll;
        +}
        +
        +#nav-swap {
        +  font-size:10px;
        +  line-height:10px;
        +  margin-left:1em;
        +  text-decoration:none;
        +  display:block;
        +}
        +
        +#tree-link {
        +
        +}
        +
        +/* DOCUMENT BODY */
        +
        +#doc-content {
        +  overflow:auto;
        +}
        +
        +#jd-header {
        +  background-color: #E2E2E2;
        +  padding: 7px 15px;
        +}
        +
        +#jd-header h1 {
        +  margin: 0 0 10px;
        +  font-size:1.7em;
        +}
        +
        +#jd-header .crumb {
        +  font-size:.9em;
        +  line-height:1em;
        +  color:#777;
        +}
        +
        +#jd-header .crumb a,
        +#jd-header .crumb a:visited {
        +  text-decoration:none;
        +  color:#777;
        +}
        +
        +#jd-header .crumb a:hover {
        +  text-decoration:underline;
        +}
        +
        +#jd-header table {
        +  margin:0;
        +  padding:0;
        +}
        +
        +#jd-header td {
        +  border:none;
        +  padding:0;
        +  vertical-align:top;
        +}
        +
        +#jd-header.guide-header {
        +  background-color:#fff;
        +  color:#435a6e;
        +  height:50px;
        +}
        +
        +#jd-descr {
        +  position:relative;
        +}
        +
        +/* summary tables for reference pages */
        +.jd-sumtable {
        +  margin: .5em 1em 1em 1em;
        +  width:95%; /* consistent table widths; within IE's quirks */
        +  font-size:.9em;
        +}
        +
        +.jd-sumtable a {
        +  text-decoration:none;
        +}
        +
        +.jd-sumtable a:hover {
        +  text-decoration:underline;
        +}
        +
        +/* the link inside a sumtable for "Show All/Hide All" */
        +.toggle-all {
        +  display:block;
        +  float:right;
        +  font-weight:normal;
        +  font-size:0.9em;
        +}
        +
        +/* adjustments for in/direct subclasses tables */
        +.jd-sumtable-subclasses {
        +  margin: 1em 0 0 0;
        +  max-width:968px;
        +}
        +
        +/* extra space between end of method name and open-paren */
        +.sympad {
        +  margin-right: 2px;
        +}
        +
        +/* right alignment for the return type in sumtable */
        +.jd-sumtable .jd-typecol {
        +  text-align:right;
        +}
        +
        +/* adjustments for the expando table-in-table */
        +.jd-sumtable-expando {
        +  margin:.5em 0;
        +  padding:0;
        +}
        +
        +/* a div that holds a short description */
        +.jd-descrdiv {
        +  padding:3px 1em 0 1em;
        +  margin:0;
        +  border:0;
        +}
        +
        +/* page-top-right container for reference pages (holds
        +links to summary tables) */
        +#api-info-block {
        +  font-size:.8em;
        +  padding:6px 10px;
        +  font-weight:normal;
        +  float:right;
        +  text-align:right;
        +  color:#999;
        +  max-width:70%;
        +}
        +
        +#api-level-toggle {
        +  padding:0 10px;
        +  font-size:11px;
        +  float:right;
        +}
        +
        +#api-level-toggle label.disabled {
        +  color:#999;
        +}
        +
        +div.api-level {
        +  font-size:.8em;
        +  font-weight:normal;
        +  color:#999;
        +  float:right;
        +  padding:0 7px 0;
        +  margin-top:-25px;
        +}
        +
        +#api-info-block div.api-level {
        +  font-size:1.3em;
        +  font-weight:bold;
        +  float:none;
        +  color:#444;
        +  padding:0;
        +  margin:0;
        +}
        +
        +/* Force link colors for IE6 */
        +div.api-level a {
        +  color:#999;
        +}
        +#api-info-block div.api-level a:link {
        +  color:#444;
        +}
        +#api-level-toggle a {
        +  color:#999;
        +}
        +
        +div#deprecatedSticker {
        +  display:none;
        +  z-index:99;
        +  position:fixed;
        +  right:15px;
        +  top:114px;
        +  margin:0;
        +  padding:1em;
        +  background:#FFF;
        +  border:1px solid #dddd00;
        +  box-shadow:-5px 5px 10px #ccc;
        +  -moz-box-shadow:-5px 5px 10px #ccc;
        +  -webkit-box-shadow:-5px 5px 10px #ccc;
        +}
        +
        +div#naMessage {
        +  display:none;
        +  width:555px;
        +  height:0;
        +  margin:0 auto;
        +}
        +
        +div#naMessage div {
        +  z-index:99;
        +  width:450px;
        +  position:fixed;
        +  margin:50px 0;
        +  padding:4em 4em 3em;
        +  background:#FFF;
        +  border:1px solid #dddd00;
        +  box-shadow:-10px 10px 40px #888;
        +  -moz-box-shadow:-10px 10px 40px #888;
        +  -webkit-box-shadow:-10px 10px 40px #888;
        +}
        +/* IE6 can't position fixed */
        +* html div#naMessage div { position:absolute; }
        +
        +div#naMessage strong {
        +  font-size:1.1em;
        +}
        +
        +.absent,
        +.absent a:link,
        +.absent a:visited,
        +.absent a:hover,
        +.absent * {
        +  color:#bbb !important;
        +  cursor:default !important;
        +  text-decoration:none !important;
        +}
        +
        +#api-level-toggle a,
        +.api-level a {
        +  color:inherit;
        +  text-decoration:none;
        +}
        +
        +#api-level-toggle a:hover,
        +.api-level a:hover {
        +  color:inherit;
        +  text-decoration:underline !important;
        +  cursor:pointer !important;
        +}
        +
        +#side-nav li.absent.selected,
        +#side-nav li.absent.selected *,
        +#side-nav div.label.absent.selected,
        +#side-nav div.label.absent.selected * {
        +  background-color:#eaeaea !important;
        +}
        +/* IE6 quirk (won't chain classes, so just keep background blue) */
        +* html #side-nav li.selected,
        +* html #side-nav li.selected *,
        +* html #side-nav div.label.selected,
        +* html #side-nav div.label.selected * {
        +  background-color: #435a6e !important;
        +}
        +
        +
        +.absent h4.jd-details-title,
        +.absent h4.jd-details-title * {
        +  background-color:#f6f6f6 !important;
        +}
        +
        +.absent img {
        +  opacity: .3;
        +  filter: alpha(opacity=30);
        +  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
        +}
        +
        +
        +/* applies to a div containing links to summary tables */
        +.sum-details-links {
        +  padding:0;
        +  font-weight:normal;
        +}
        +
        +.sum-details-links a {
        +  text-decoration:none;
        +}
        +
        +.sum-details-links a:hover {
        +  text-decoration:underline;
        +}
        +
        +
        +/* inheritance table */
        +.jd-inheritance-table {
        +  border-spacing:0;
        +  margin:0;
        +  padding:0;
        +  font-size:.9em;
        +}
        +.jd-inheritance-table td {
        +  border: none;
        +  margin: 0;
        +  padding: 0;
        +}
        +.jd-inheritance-table .jd-inheritance-space {
        +  font-weight:bold;
        +  width:1em;
        +}
        +.jd-inheritance-table .jd-inheritance-interface-cell {
        +  padding-left: 17px;
        +}
        +
        +#jd-content {
        +  padding: 18px 15px;
        +}
        +
        +hr {
        +  background-color:#ccc;
        +  border-color:#fff;
        +  margin:2em 0 1em;
        +}
        +
        +/* DOC CLASSES */
        +
        +#jd-content h1 {
        +/*sdk page*/
        +  font-size:1.6em;
        +  color:#336666;
        +  margin:0 0 .5em;
        +}
        +
        +#jd-content h2 {
        +  font-size:1.45em;
        +  color:#111;
        +  border-top:2px solid #ccc;
        +  padding: .5em 0 0;
        +  margin: 2em 0 1em 0;
        +}
        +
        +#jd-content h3 {
        +  font-size:1.3em;
        +  color:#3a3a3a;
        +  padding: 0;
        +  margin: 1.5em 0 .65em 0;
        +}
        +
        +#jd-content h4 {
        +  font-size:1.1em;
        +  color:#3a3a3a;
        +  padding: 0;
        +  margin: 1.25em 0 .65em 0;
        +}
        +
        +#jd-content h5 {
        +  font-size:1.0em;
        +  color:#3a3a3a;
        +  padding: 0;
        +  margin: 1em 0 .65em 0;
        +}
        +
        +#jd-content .small-header {
        +  font-size:1em;
        +  color:#000;
        +  font-weight:bold;
        +  border:none;
        +  padding:0;
        +  margin:1em 0 .5em;
        +  position:inherit;
        +}
        +
        +#jd-content table {
        +  margin: 0 0 1em 1em;
        +}
        +
        +#jd-content img {
        +  margin: 0 0 1em 1em;
        +}
        +
        +#jd-content li img,
        +#jd-content dd img {
        +  margin:.5em 0 .5em 1em;
        +}
        +
        +.nolist {
        +  list-style:none;
        +  padding:0;
        +  margin:0 0 1em 1em;
        +}
        +
        +.nolist li {
        +  padding:0 0 2px;
        +  margin:0;
        +}
        +
        +h4 .normal {
        +  font-size:.9em;
        +  font-weight:normal;
        +}
        +
        +.caps {
        +  font-variant:small-caps;
        +  font-size:1.2em;
        +}
        +
        +dl.tag-list dl.atn-list {
        +  padding:0 0 0 2em;
        +}
        +
        +.jd-details {
        +/*  border:1px solid #669999;
        +  padding:4px; */
        +  margin:0 0 1em;
        +}
        +
        +/* API reference: a container for the
        +.tagdata blocks that make up the detailed
        +description */
        +.jd-details-descr {
        +  padding:0;
        +  margin:.5em .25em;
        +}
        +
        +/* API reference: a block containing
        +a detailed description, a params table,
        +seealso list, etc */
        +.jd-tagdata {
        +  margin:.5em 1em;
        +}
        +
        +.jd-tagdata p {
        +  margin:0 0 1em 1em;
        +}
        +
        +/* API reference: adjustments to
        +the detailed description block */
        +.jd-tagdescr {
        +  margin:.25em 0 .75em 0;
        +  line-height:1em;
        +}
        +
        +.jd-tagdescr p {
        +  margin:.5em 0;
        +  padding:0;
        +
        +}
        +
        +.jd-tagdescr ol,
        +.jd-tagdescr ul {
        +  margin:0 2.5em;
        +  padding:0;
        +}
        +
        +.jd-tagdescr table,
        +.jd-tagdescr img {
        +  margin:.25em 1em;
        +}
        +
        +.jd-tagdescr li {
        +margin:0 0 .25em 0;
        +padding:0;
        +}
        +
        +/* API reference: heading marking
        +the details section for constants,
        +attrs, methods, etc. */
        +h4.jd-details-title {
        +  font-size:1.15em;
        +  background-color: #E2E2E2;
        +  margin:1.5em 0 .6em;
        +  padding:3px 95px 3px 3px; /* room for api-level */
        +}
        +
        +h4.jd-tagtitle {
        +  margin:0;
        +}
        +
        +/* API reference: heading for "Parameters", "See Also", etc.,
        +in details sections */
        +h5.jd-tagtitle {
        +  margin:0 0 .25em 0;
        +  font-size:1em;
        +}
        +
        +.jd-tagtable {
        +  margin:0;
        +}
        +
        +.jd-tagtable td,
        +.jd-tagtable th {
        +  border:none;
        +  background-color:#fff;
        +  vertical-align:top;
        +  font-weight:normal;
        +  padding:2px 10px;
        +}
        +
        +.jd-tagtable th {
        +  font-style:italic;
        +}
        +
        +#jd-content table h2 {
        +  background-color: #d6d6d6;
        +  font-size: 1.1em;
        +  margin:0 0 10px;
        +  padding:5px;
        +  left:0;
        +  width:auto;
        +}
        +
        +div.design-announce {
        +  border-top:1px solid #33B5E5;
        +  border-bottom:1px solid #33B5E5;
        +  padding:5px 10px 10px 55px;
        +  margin:2em 0;
        +  background:url('images/icon_design.png') 5px 13px no-repeat;
        +}
        +
        +div.design-announce p {
        +  margin: .5em 0 0 0;
        +}
        +
        +div.special {
        +  padding: .5em 1em 1em 1em;
        +  margin: 0 0 1em;
        +  background-color: #DAF3FC;
        +  border:1px solid #d3ecf5;
        +  border-radius:5px;
        +  -moz-border-radius:5px;
        +  -webkit-border-radius:5px;
        +}
        +
        +div.special p {
        +  margin: .5em 0 0 0;
        +}
        +
        +div.special ol {
        +  margin: 0;
        +}
        +
        +div.special ol li {
        +  margin: 0;
        +  padding: 0;
        +}
        +
        +#jd-content div.special h2,
        +#jd-content div.special h3 {
        +  color:#669999;
        +  font-size:1.2em;
        +  border:none;
        +  margin:0 0 .5em;
        +  padding:0;
        +}
        +
        +#jd-content div.special.reference h2,
        +#jd-content div.special.reference h3,
        +#jd-content div.special.reference h4 {
        +  color:#000;
        +  font-size:1em;
        +  border:none;
        +  font-weight:bold;
        +  margin:.5em 0;
        +  padding:0;
        +}
        +
        +p.note, div.note,
        +p.caution, div.caution,
        +p.warning, div.warning {
        +  margin: 1em;
        +  padding: 0 0 0 .5em;
        +  border-left: 4px solid;
        +}
        +
        +p.special-note,
        +div.special-note {
        +  background-color:#EBF3DB;
        +  padding:10px 20px;
        +  margin:0 0 1em;
        +}
        +
        +p.note,
        +div.note {
        + border-color: #99aacc;
        +}
        +
        +p.warning,
        +div.warning {
        +  border-color: #aa0033;
        +}
        +
        +p.caution,
        +div.caution {
        +  border-color: #ffcf00;
        +}
        +
        +li .note,
        +li .caution,
        +li .warning {
        +  margin: .5em 0 0 0;
        +  padding: .2em .5em .2em .9em;
        +}
        +
        +/* Makes sure the first paragraph does not add top-whitespace within the box*/
        +li .note>p:first-child,
        +li .caution>p:first-child,
        +li .warning>p:first-child {
        +  margin-top:0;
        +  padding-top:0;
        +}
        +
        +dl.xml dt {
        +  font-variant:small-caps;
        +  font-size:1.2em;
        +}
        +
        +dl.xml dl {
        +  padding:0;
        +}
        +
        +dl.xml dl dt {
        +  font-variant:normal;
        +  font-size:1em;
        +}
        +
        +.listhead li {
        +  font-weight: bold;
        +}
        +
        +.listhead li *, /*ie*/.listhead li li {
        +  font-weight: normal;
        +}
        +
        +ol.no-style,
        +ul.no-style {
        +  list-style:none;
        +  padding-left:1em;
        +}
        +
        +.new,
        +.new-child {
        +  font-size: .78em;
        +  font-weight: bold;
        +  color: #ff3d3d;
        +  text-decoration: none;
        +  vertical-align:top;
        +  line-height:.9em;
        +  white-space:nowrap;
        +}
        +
        +.toggle-list.open .new-child {
        +  display:none;
        +}
        +
        +pre.classic {
        +  background-color:transparent;
        +  border:none;
        +  padding:0;
        +}
        +
        +p.img-caption {
        +  margin: -0.5em 0 1em 1em; /* matches default img left-margin */
        +}
        +
        +div.figure {
        +  float:right;
        +  clear:right;
        +  margin:1em 0 0 0;
        +  padding:0 0 0 3em;
        +  background-color:#fff;
        +  /* width must be defined w/ an inline style matching the image width */
        +}
        +
        +#jd-content
        +div.figure img {
        +  margin: 0 0 1em;
        +}
        +
        +div.figure p.img-caption {
        +  margin: -0.5em 0 1em 0;
        +}
        +
        +p.table-caption {
        +  margin: 0 0 0.5em 1em; /* matches default table left-margin */
        +}
        +
        +
        +/* toggle for misc content (such as long sample code) 
        +   see toggleContent() script in android-developer-docs.js */
        +.toggle-content.closed .toggle-content-toggleme {
        +  display:none;
        +}
        +
        +.toggle-content a[href="#"] {
        +  text-decoration:none;
        +  color:inherit;
        +}
        +
        +.toggle-content-toggleme {
        +  padding-bottom:1px; /* fixes animation bounce due to margins */
        +}
        +
        +#jd-content .toggle-content img.toggle-content-img {
        +  margin:0;
        +}
        +
        +
        +/* BEGIN quickview sidebar element styles */
        +
        +#qv-wrapper {
        +  float: right;
        +  width:310px; /* +35px padding */
        +  background-color:#fff;
        +  margin:-48px 0 2px 0;
        +  padding:0 0 20px 35px;
        +}
        +
        +#qv {
        +  background-color:#fff;
        +  border:4px solid #dee8f1;
        +  margin:0;
        +  padding:0 5px 5px;
        +  width:292px; /* +10px padding; +8px border */
        +  font-size:.9em;
        +}
        +
        +#qv ol {
        +  list-style:none;
        +  padding: 0;
        +}
        +
        +#qv ol ol{
        +  list-style:none;
        +  padding: 0 0 0 12px;
        +  margin:0;
        +}
        +
        +#qv ul {
        +  padding: 0 10px 0 2em;
        +}
        +
        +#qv li {
        +  padding: 0 10px 3px;
        +  line-height: 1.2em;
        +}
        +
        +#qv li li {
        +  padding: 3px 10px 0;
        +}
        +
        +#qv ul li {
        +  padding: 0 10px 0 0;
        +}
        +
        +#qv li.selected a {
        +  color:#555;
        +  text-decoration:none;
        +}
        +
        +#qv a,
        +#qv a code {
        +  color:#cc6600;
        +}
        +
        +#qv p {
        +  margin:8px 0 0;
        +  padding:0 10px;
        +}
        +
        +#jd-content #qv h2 {
        +  font-size:1.05em;
        +  font-weight:bold;
        +  margin:12px 0 .25em 0;
        +  padding:0 10px;
        +  background-color:transparent;
        +  color:#7BB026;
        +  border:none;
        +  left:0;
        +  z-index:1;
        +}
        +
        +#qv-extra #rule {
        +  padding: 0 10px;
        +  margin: 0;
        +}
        +
        +#qv-sub-rule {
        +  padding: 5px 15px 10px;
        +  margin: 0;
        +}
        +
        +#jd-content
        +#qv-sub-rule h2 {
        +  margin: 0 0 .5em 0;
        +}
        +
        +/* END quickview sidebar element styles */
        +
        +/* Begin sidebox sidebar element styles */
        +
        +.sidebox-wrapper {
        +  float:right;
        +  clear:right;
        +  width:310px; /* +35px padding */
        +  background-color:#fff;
        +  margin:0;
        +  padding:0 0 20px 35px;
        +}
        +
        +.sidebox {
        +  border-left:1px solid #dee8f1;
        +  background-color:#ffffee;
        +  margin:0;
        +  padding:8px 12px;
        +  font-size:0.9em;
        +  width:285px; /* +24px padding; +1px border */
        +}
        +
        +.sidebox p {
        +  margin-bottom: .75em;
        +}
        +
        +.sidebox ul {
        +  padding: 0 0 0 1.5em;
        +}
        +
        +.sidebox li ul {
        +  margin-top:0;
        +  margin-bottom:.1em;
        +}
        +
        +.sidebox li {
        +padding:0 0 0 0em;
        +}
        +
        +#jd-content .sidebox h2,
        +#jd-content .sidebox h3,
        +#jd-content .sidebox h4,
        +#jd-content .sidebox h5 {
        +  border:none;
        +  font-size:1em;
        +  margin:0;
        +  padding:0 0 8px;
        +  left:0;
        +  z-index:0;
        +}
        +
        +.sidebox hr {
        +  background-color:#ccc;
        +  border:none;
        +}
        +
        +/* End sidebox sidebar element styles */
        +
        +/* BEGIN developer training bar styles */
        +
        +div#tb-wrapper {
        +  float: right;
        +  clear:right;
        +  width:380px; /* +25px padding = 405 */
        +  background-color:#fff;
        +  margin:0 0 2px 0;
        +  padding:0 0 20px 25px;
        +}
        +
        +div#tb {
        +  margin:0;
        +  padding:0 15px;
        +  width:350px; /* +15px padding = 380 */
        +  font-size:.9em;
        +  background:#e9e9e9;
        +  border:1px solid #aaa;
        +  border-radius:5px;
        +  -moz-border-radius:5px;
        +  -webkit-border-radius:5px;
        +  overflow:auto;
        +}
        +
        +div#tb h2 {
        +  font-size:1.3em;
        +  font-weight:bold;
        +  margin:1em 0;
        +  padding:0;
        +  background-color:transparent;
        +  border:none;
        +  clear:both;
        +}
        +
        +div.download-box a.button {
        +  color: #069;
        +  font-size:1.1em;
        +  font-weight:bold;
        +  text-decoration:none;
        +  height:27px;
        +  line-height:27px;
        +  text-align:center;
        +  padding:5px 8px;
        +  background-color: #fff;
        +  border: 1px solid #aaa;
        +  -webkit-border-radius: 2px;
        +  -moz-border-radius: 2px;
        +  border-radius: 2px;
        +}
        +
        +div.download-box a.button:hover {
        +  border-color: #09C;
        +  background-color: #4CADCB;
        +  background-image: -webkit-gradient(linear,left top,left bottom,from(#5dbcd9),to(#4cadcb));
        +  background-image: -webkit-linear-gradient(top,#5dbcd9,#4cadcb);
        +  background-image: -moz-linear-gradient(top,#5dbcd9,#4cadcb);
        +  background-image: -ms-linear-gradient(top,#5dbcd9,#4cadcb);
        +  background-image: -o-linear-gradient(top,#5dbcd9,#4cadcb);
        +  background-image: linear-gradient(top,#5dbcd9,#4cadcb);
        +  filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#5dbcd9',EndColorStr='#4cadcb');
        +  color: #fff;
        +}
        +
        +div.download-box a.button:active {
        +  background-color: #1E799A;
        +  background-image: none;
        +  border-color: #30B7E6;
        +}
        +
        +div.download-box p.filename {
        +  font-size:0.85em;
        +  color:#888;
        +  margin:4px 0 1em 10px;
        +}
        +
        +/* End developer training bar */
        +
        +/* Training nav bar (previous/next) */
        +
        +div.training-nav-top {
        +  float: right;
        +  width:380px; /* +25px padding = 405 */
        +  margin:-58px 0 0 0;
        +  padding:0 0 20px 25px;
        +}
        +
        +div.training-nav-bottom {
        +  padding:1px; /* for weird FF bug (scrollbar appears) */
        +  margin:3em 0;
        +  overflow:auto;
        +}
        +
        +div.training-nav-button-next a,
        +div.training-nav-button-previous a {
        +  display:block;
        +  width:160px;
        +  height:55px;
        +  padding:4px 7px;
        +  border:1px solid #aaa;
        +  border-radius:5px;
        +  -moz-border-radius:5px;
        +  -webkit-border-radius:5px;
        +  text-decoration:none;
        +  font-weight:bold;
        +}
        +
        +div.training-nav-button-next a:hover,
        +div.training-nav-button-previous a:hover {
        +  border:1px solid #069; /* match link color */
        +}
        +
        +div.training-nav-button-next a:active,
        +div.training-nav-button-previous a:active {
        +  border:1px solid #f00; /* match link color */
        +}
        +  
        +div.training-nav-button-previous {
        +  float:left;
        +  text-align:left;
        +}
        +
        +div.training-nav-button-next {
        +  float:right;
        +  text-align:right;
        +}
        +
        +span.training-nav-button-title {
        +  display:block;
        +  font-size:.85em;
        +  font-weight:normal;
        +  line-height:1.3em;
        +  margin:.5em 0 0;
        +}
        +
        +/* End training nav bar */
        +
        +/* BEGIN image and caption styles (originally for UI Guidelines docs) */
        +
        +table.image-caption {
        +  padding:0;
        +  margin:.5em 0;
        +  border:0;
        +}
        +
        +td.image-caption-i {
        +  font-size:92%;
        +  padding:0 5px;
        +  margin:0;
        +  border:0;
        +}
        +
        +td.image-caption-i img {
        +  padding:0 1em;
        +  margin:0;
        +}
        +
        +.image-list {
        +  width:24px;
        +  text-align:center;
        +}
        +
        +td.image-caption-c {
        +  font-size:92%;
        +  padding:1em 2px 2px 2px;
        +  margin:0;
        +  border:0;
        +  width:350px;
        +}
        +
        +.grad-rule-top {
        +background-image:url(images/grad-rule-qv.png);
        +background-repeat:no-repeat;
        +padding-top:1em;
        +margin-top:0;
        +}
        +
        +.image-caption-nested {
        +  margin-top:0;
        +  padding:0 0 0 1em;
        +}
        +
        +.image-caption-nested td {
        +  padding:0 4px 2px 0;
        +  margin:0;
        +  border:0;
        +}
        +
        +/* END image and caption styles */
        +
        +/* table of contents */
        +
        +ol.toc {
        +  margin: 0 0 1em 0;
        +  padding: 0;
        +  list-style: none;
        +  font-size:95%;
        +}
        +
        +ol.toc li {
        +  font-weight: bold;
        +  margin: 0 0 .5em 1em;
        +  padding: 0;
        +}
        +
        +ol.toc li p {
        +  font-weight: normal;
        +}
        +
        +ol.toc li ol {
        +  margin: 0;
        +  padding: 0;
        +}
        +
        +ol.toc li li {
        +  padding: 0;
        +  margin: 0 0 0 1em;
        +  font-weight: normal;
        +  list-style: none;
        +}
        +
        +table ol.toc {
        +  margin-left: 0;
        +}
        +
        +.columns td {
        +  padding:0 5px;
        +  border:none;
        +}
        +
        +/* link table */
        +.jd-linktable {
        +  margin: 0 0 1em;
        +  border-bottom: 1px solid #888;
        +}
        +.jd-linktable th,
        +.jd-linktable td {
        +  padding: 3px 5px;
        +  vertical-align: top;
        +  text-align: left;
        +  border:none;
        +}
        +.jd-linktable tr {
        +  background-color: #fff;
        +}
        +.jd-linktable td {
        +  border-top: 1px solid #888;
        +  background-color: inherit;
        +}
        +.jd-linktable td  p {
        +  padding: 0 0 5px;
        +}
        +.jd-linktable .jd-linkcol {
        +}
        +.jd-linktable .jd-descrcol {
        +}
        +.jd-linktable .jd-typecol {
        +  text-align:right;
        +}
        +.jd-linktable .jd-valcol {
        +}
        +.jd-linktable .jd-commentrow {
        +  border-top:none;
        +  padding-left:25px;
        +}
        +.jd-deprecated-warning {
        +  margin-top: 0;
        +  margin-bottom: 10px;
        +}
        +
        +tr.alt-color {
        +  background-color: #f6f6f6;
        +}
        +
        +/* expando trigger */
        +#jd-content .jd-expando-trigger-img {
        +  margin:0;
        +}
        +
        +/* jd-expando */
        +.jd-inheritedlinks {
        +  padding:0 0 0 13px
        +}
        +
        +/* SDK PAGE */
        +table.download tr {
        +  background-color:#d9d9d9;
        +}
        +
        +table.download tr.alt-color {
        +  background-color:#ededed;
        +}
        +
        +table.download td,
        +table.download th {
        +  border:2px solid #fff;
        +  padding:10px 5px;
        +}
        +
        +table.download th {
        +  background-color:#6d8293;
        +  color:#fff;
        +}
        +
        +/* INLAY 180 COPY and 240PX EXTENSION */
        +/* modified to 43px so that all browsers eliminate the package panel h-scroll */
        +.g-tpl-240 .g-unit,
        +.g-unit .g-tpl-240 .g-unit,
        +.g-unit .g-unit .g-tpl-240 .g-unit {
        +  display: block;
        +  margin: 0 0 0 243px;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-240 .g-first,
        +.g-unit .g-tpl-240 .g-first,
        +.g-tpl-240 .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 243px;
        +  float: left;
        +}
        +/* 240px alt */
        +.g-tpl-240-alt .g-unit,
        +.g-unit .g-tpl-240-alt .g-unit,
        +.g-unit .g-unit .g-tpl-240-alt .g-unit {
        +  display: block;
        +  margin: 0 243px 0 0;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-240-alt .g-first,
        +.g-unit .g-tpl-240-alt .g-first,
        +.g-tpl-240-alt .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 243px;
        +  float: right;
        +}
        +
        +/* 200px */
        +.g-tpl-200 .g-unit,
        +.g-unit .g-tpl-200 .g-unit,
        +.g-unit .g-unit .g-tpl-200 .g-unit {
        +  display: block;
        +  margin: 0 0 0 200px;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-200 .g-first,
        +.g-unit .g-tpl-200 .g-first,
        +.g-tpl-200 .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 200px;
        +  float: left;
        +}
        +/* 200px alt */
        +.g-tpl-200-alt .g-unit,
        +.g-unit .g-tpl-200-alt .g-unit,
        +.g-unit .g-unit .g-tpl-200-alt .g-unit {
        +  display: block;
        +  margin: 0 200px 0 0;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-200-alt .g-first,
        +.g-unit .g-tpl-200-alt .g-first,
        +.g-tpl-200-alt .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 200px;
        +  float: right;
        +}
        +
        +/* 190px */
        +.g-tpl-190 .g-unit,
        +.g-unit .g-tpl-190 .g-unit,
        +.g-unit .g-unit .g-tpl-190 .g-unit {
        +  display: block;
        +  margin: 0 0 0 190px;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-190 .g-first,
        +.g-unit .g-tpl-190 .g-first,
        +.g-tpl-190 .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 190px;
        +  float: left;
        +}
        +/* 190px alt */
        +.g-tpl-190-alt .g-unit,
        +.g-unit .g-tpl-190-alt .g-unit,
        +.g-unit .g-unit .g-tpl-190-alt .g-unit {
        +  display: block;
        +  margin: 0 190px 0 0;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-190-alt .g-first,
        +.g-unit .g-tpl-190-alt .g-first,
        +.g-tpl-190-alt .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 190px;
        +  float: right;
        +}
        +
        +/* 180px */
        +.g-tpl-180 .g-unit,
        +.g-unit .g-tpl-180 .g-unit,
        +.g-unit .g-unit .g-tpl-180 .g-unit {
        +  display: block;
        +  margin: 0 0 0 180px;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-180 .g-first,
        +.g-unit .g-tpl-180 .g-first,
        +.g-tpl-180 .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 180px;
        +  float: left;
        +}
        +/* 180px alt */
        +.g-tpl-180-alt .g-unit,
        +.g-unit .g-tpl-180-alt .g-unit,
        +.g-unit .g-unit .g-tpl-180-alt .g-unit {
        +  display: block;
        +  margin: 0 180px 0 0;
        +  width: auto;
        +  float: none;
        +}
        +.g-unit .g-unit .g-tpl-180-alt .g-first,
        +.g-unit .g-tpl-180-alt .g-first,
        +.g-tpl-180-alt .g-first {
        +  display: block;
        +  margin: 0;
        +  width: 180px;
        +  float: right;
        +}
        +
        +
        +/* JQUERY RESIZABLE STYLES */
        +.ui-resizable { position: relative; }
        +.ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; z-index:1; }
        +.ui-resizable .ui-resizable-handle { display: block; }
        +body .ui-resizable-disabled .ui-resizable-handle { display: none; }
        +body .ui-resizable-autohide .ui-resizable-handle { display: none; }
        +.ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px;
        +  background: transparent url("images/resizable-s2.gif") repeat scroll center top; }
        +.ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%;
        +  background: transparent url("images/resizable-e2.gif") repeat scroll right center; }
        +
        +@media print {
        +
        +  body {
        +    overflow:visible;
        +  }
        +
        +  #header {
        +    height:60px;
        +  }
        +
        +  #headerLeft {
        +    padding:0;
        +  }
        +
        +  #header-tabs,
        +  #headerRight,
        +  #side-nav,
        +  #api-info-block {
        +    display:none;
        +  }
        +
        +  #body-content {
        +    position:inherit;
        +  }
        +
        +  #doc-content {
        +    margin-left:0 !important;
        +    height:auto !important;
        +    width:auto !important;
        +    overflow:inherit;
        +    display:inline;
        +  }
        +
        +  #jd-header {
        +    padding:10px 0;
        +  }
        +
        +  #jd-content {
        +    padding:15px 0 0;
        +  }
        +
        +  #footer {
        +    float:none;
        +    margin:2em 0 0;
        +  }
        +
        +  h4.jd-details-title {
        +    border-bottom:1px solid #666;
        +  }
        +
        +  pre {
        +    /* these allow lines to break (if there's a white space) */
        +    overflow: visible;
        +    text-wrap: unrestricted;
        +    white-space: -moz-pre-wrap; /* Moz */
        +    white-space: -pre-wrap; /* Opera 4-6 */
        +    white-space: -o-pre-wrap; /* Opera 7 */
        +    white-space: pre-wrap; /* CSS3  */
        +    word-wrap: break-word; /* IE 5.5+ */
        +  }
        +
        +  h1, h2, h3, h4, h5, h6 {
        +    page-break-after: avoid;
        +  }
        +
        +  table, img {
        +    page-break-inside: avoid;
        +  }
        +}
        diff --git a/make/tools/droiddoc/templates-pdk/assets/android-developer-docs.js b/make/tools/droiddoc/templates-pdk/assets/android-developer-docs.js
        new file mode 100644
        index 0000000..5fc7e09
        --- /dev/null
        +++ b/make/tools/droiddoc/templates-pdk/assets/android-developer-docs.js
        @@ -0,0 +1,614 @@
        +var resizePackagesNav;
        +var classesNav;
        +var devdocNav;
        +var sidenav;
        +var content;
        +var HEADER_HEIGHT = 117;
        +var cookie_namespace = 'android_developer';
        +var NAV_PREF_TREE = "tree";
        +var NAV_PREF_PANELS = "panels";
        +var nav_pref;
        +var toRoot;
        +var isMobile = false; // true if mobile, so we can adjust some layout
        +var isIE6 = false; // true if IE6
        +
        +// TODO: use $(document).ready instead
        +function addLoadEvent(newfun) {
        +  var current = window.onload;
        +  if (typeof window.onload != 'function') {
        +    window.onload = newfun;
        +  } else {
        +    window.onload = function() {
        +      current();
        +      newfun();
        +    }
        +  }
        +}
        +
        +var agent = navigator['userAgent'].toLowerCase();
        +// If a mobile phone, set flag and do mobile setup
        +if ((agent.indexOf("mobile") != -1) ||      // android, iphone, ipod
        +    (agent.indexOf("blackberry") != -1) ||
        +    (agent.indexOf("webos") != -1) ||
        +    (agent.indexOf("mini") != -1)) {        // opera mini browsers
        +  isMobile = true;
        +  addLoadEvent(mobileSetup);
        +// If not a mobile browser, set the onresize event for IE6, and others
        +} else if (agent.indexOf("msie 6") != -1) {
        +  isIE6 = true;
        +  addLoadEvent(function() {
        +    window.onresize = resizeAll;
        +  });
        +} else {
        +  addLoadEvent(function() {
        +    window.onresize = resizeHeight;
        +  });
        +}
        +
        +function mobileSetup() {
        +  $("body").css({'overflow':'auto'});
        +  $("html").css({'overflow':'auto'});
        +  $("#body-content").css({'position':'relative', 'top':'0'});
        +  $("#doc-content").css({'overflow':'visible', 'border-left':'3px solid #DDD'});
        +  $("#side-nav").css({'padding':'0'});
        +  $("#nav-tree").css({'overflow-y': 'auto'});
        +}
        +
        +/* loads the lists.js file to the page.
        +Loading this in the head was slowing page load time */
        +addLoadEvent( function() {
        +  var lists = document.createElement("script");
        +  lists.setAttribute("type","text/javascript");
        +  lists.setAttribute("src", toRoot+"reference/lists.js");
        +  document.getElementsByTagName("head")[0].appendChild(lists);
        +} );
        +
        +addLoadEvent( function() {
        +  $("pre:not(.no-pretty-print)").addClass("prettyprint");
        +  prettyPrint();
        +} );
        +
        +function setToRoot(root) {
        +  toRoot = root;
        +  // note: toRoot also used by carousel.js
        +}
        +
        +function restoreWidth(navWidth) {
        +  var windowWidth = $(window).width() + "px";
        +  content.css({marginLeft:parseInt(navWidth) + 6 + "px"}); //account for 6px-wide handle-bar
        +
        +  if (isIE6) {
        +    content.css({width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"}); // necessary in order for scrollbars to be visible
        +  }
        +
        +  sidenav.css({width:navWidth});
        +  resizePackagesNav.css({width:navWidth});
        +  classesNav.css({width:navWidth});
        +  $("#packages-nav").css({width:navWidth});
        +}
        +
        +function restoreHeight(packageHeight) {
        +  var windowHeight = ($(window).height() - HEADER_HEIGHT);
        +  var swapperHeight = windowHeight - 13;
        +  $("#swapper").css({height:swapperHeight + "px"});
        +  sidenav.css({height:windowHeight + "px"});
        +  content.css({height:windowHeight + "px"});
        +  resizePackagesNav.css({maxHeight:swapperHeight + "px", height:packageHeight});
        +  classesNav.css({height:swapperHeight - parseInt(packageHeight) + "px"});
        +  $("#packages-nav").css({height:parseInt(packageHeight) - 6 + "px"}); //move 6px to give space for the resize handle
        +  devdocNav.css({height:sidenav.css("height")});
        +  $("#nav-tree").css({height:swapperHeight + "px"});
        +}
        +
        +function readCookie(cookie) {
        +  var myCookie = cookie_namespace+"_"+cookie+"=";
        +  if (document.cookie) {
        +    var index = document.cookie.indexOf(myCookie);
        +    if (index != -1) {
        +      var valStart = index + myCookie.length;
        +      var valEnd = document.cookie.indexOf(";", valStart);
        +      if (valEnd == -1) {
        +        valEnd = document.cookie.length;
        +      }
        +      var val = document.cookie.substring(valStart, valEnd);
        +      return val;
        +    }
        +  }
        +  return 0;
        +}
        +
        +function writeCookie(cookie, val, section, expiration) {
        +  if (val==undefined) return;
        +  section = section == null ? "_" : "_"+section+"_";
        +  if (expiration == null) {
        +    var date = new Date();
        +    date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
        +    expiration = date.toGMTString();
        +  }
        +  document.cookie = cookie_namespace + section + cookie + "=" + val + "; expires=" + expiration+"; path=/";
        +}
        +
        +function init() {
        +  $("#side-nav").css({position:"absolute",left:0});
        +  content = $("#doc-content");
        +  resizePackagesNav = $("#resize-packages-nav");
        +  classesNav = $("#classes-nav");
        +  sidenav = $("#side-nav");
        +  devdocNav = $("#devdoc-nav");
        +
        +  var cookiePath = "";
        +  if (location.href.indexOf("/reference/") != -1) {
        +    cookiePath = "reference_";
        +  } else if (location.href.indexOf("/guide/") != -1) {
        +    cookiePath = "guide_";
        +  } else if (location.href.indexOf("/sdk/") != -1) {
        +    cookiePath = "sdk_";
        +  } else if ((location.href.indexOf("/resources/") != -1) || 
        +             (location.href.indexOf("/training/") != -1)) {
        +    cookiePath = "resources_";
        +  }
        +
        +  if (!isMobile) {
        +    $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizePackagesHeight(); } });
        +    $("#side-nav").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } });
        +    var cookieWidth = readCookie(cookiePath+'width');
        +    var cookieHeight = readCookie(cookiePath+'height');
        +    if (cookieWidth) {
        +      restoreWidth(cookieWidth);
        +    } else if ($("#side-nav").length) {
        +      resizeWidth();
        +    }
        +    if (cookieHeight) {
        +      restoreHeight(cookieHeight);
        +    } else {
        +      resizeHeight();
        +    }
        +  }
        +
        +  if (devdocNav.length) { // only dev guide, resources, and sdk
        +    tryPopulateResourcesNav();
        +    highlightNav(location.href);
        +  }
        +}
        +
        +function tryPopulateResourcesNav() {
        +  var sampleList = $('#devdoc-nav-sample-list');
        +  var articleList = $('#devdoc-nav-article-list');
        +  var tutorialList = $('#devdoc-nav-tutorial-list');
        +  var topicList = $('#devdoc-nav-topic-list');
        +
        +  if (!topicList.length || !ANDROID_TAGS || !ANDROID_RESOURCES)
        +    return;
        +
        +  var topics = [];
        +  for (var topic in ANDROID_TAGS['topic']) {
        +    topics.push({name:topic,title:ANDROID_TAGS['topic'][topic]});
        +  }
        +  topics.sort(function(x,y){ return (x.title < y.title) ? -1 : 1; });
        +  for (var i = 0; i < topics.length; i++) {
        +    topicList.append(
        +        $('
      • ').append( + $('') + .attr('href', toRoot + "resources/browser.html?tag=" + topics[i].name) + .append($('') + .addClass('en') + .html(topics[i].title) + ) + ) + ); + } + + var _renderResourceList = function(tag, listNode) { + var resources = []; + var tags; + var resource; + var i, j; + for (i = 0; i < ANDROID_RESOURCES.length; i++) { + resource = ANDROID_RESOURCES[i]; + tags = resource.tags || []; + var hasTag = false; + for (j = 0; j < tags.length; j++) + if (tags[j] == tag) { + hasTag = true; + break; + } + if (!hasTag) + continue; + resources.push(resource); + } + //resources.sort(function(x,y){ return (x.title.en < y.title.en) ? -1 : 1; }); + for (i = 0; i < resources.length; i++) { + resource = resources[i]; + var listItemNode = $('
      • ').append( + $('') + .attr('href', toRoot + "resources/" + resource.path) + .append($('') + .addClass('en') + .html(resource.title.en) + ) + ); + tags = resource.tags || []; + for (j = 0; j < tags.length; j++) { + if (tags[j] == 'new') { + listItemNode.get(0).innerHTML += ' new!'; + break; + } else if (tags[j] == 'updated') { + listItemNode.get(0).innerHTML += ' updated!'; + break; + } + } + listNode.append(listItemNode); + } + }; + + _renderResourceList('sample', sampleList); + _renderResourceList('article', articleList); + _renderResourceList('tutorial', tutorialList); +} + +function highlightNav(fullPageName) { + var lastSlashPos = fullPageName.lastIndexOf("/"); + var firstSlashPos; + if (fullPageName.indexOf("/guide/") != -1) { + firstSlashPos = fullPageName.indexOf("/guide/"); + } else if (fullPageName.indexOf("/sdk/") != -1) { + firstSlashPos = fullPageName.indexOf("/sdk/"); + } else if (fullPageName.indexOf("/resources/") != -1) { + firstSlashPos = fullPageName.indexOf("/resources/"); + } else if (fullPageName.indexOf("/training/") != -1) { + firstSlashPos = fullPageName.indexOf("/training/"); + } + if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html') + fullPageName = fullPageName + "index.html"; + } + + // get the path and page name from the URL (such as 'guide/topics/graphics/index.html') + var htmlPos = fullPageName.indexOf(".html"); + var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); // +5 advances past ".html" + // find instances of the page name in the side nav + var link = $("#devdoc-nav a[href$='"+ pathPageName+"']"); + // if there's no match, then let's backstep through the directory until we find an index.html + // page that matches our ancestor directories (only for dev guide and resources) + if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || + (fullPageName.indexOf("/resources/") != -1))) { + lastBackstep = pathPageName.lastIndexOf("/"); + while (link.length == 0) { + backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep); + link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + + 1)+"index.html']"); + lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1); + if (lastBackstep == 0) break; + } + } + + // add 'selected' to the
      • or
      • ) and the parent
          is hidden + else if (link.parent().parent().is(':hidden')) { + toggle(link.parent().parent().parent(), false); // open the parent list + // then also check if the parent list is also nested in a hidden list + if (link.parent().parent().parent().parent().is(':hidden')) { + toggle(link.parent().parent().parent().parent().parent(), false); // open the super parent list + } + } +} + +/* Resize the height of the nav panels in the reference, + * and save the new size to a cookie */ +function resizePackagesHeight() { + var windowHeight = ($(window).height() - HEADER_HEIGHT); + var swapperHeight = windowHeight - 13; // move 13px for swapper link at the bottom + resizePackagesNav.css({maxHeight:swapperHeight + "px"}); + classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); + + $("#swapper").css({height:swapperHeight + "px"}); + $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle + + var basePath = getBaseUri(location.pathname); + var section = basePath.substring(1,basePath.indexOf("/",1)); + writeCookie("height", resizePackagesNav.css("height"), section, null); +} + +/* Resize the height of the side-nav and doc-content divs, + * which creates the frame effect */ +function resizeHeight() { + var docContent = $("#doc-content"); + + // Get the window height and always resize the doc-content and side-nav divs + var windowHeight = ($(window).height() - HEADER_HEIGHT); + docContent.css({height:windowHeight + "px"}); + $("#side-nav").css({height:windowHeight + "px"}); + + var href = location.href; + // If in the reference docs, also resize the "swapper", "classes-nav", and "nav-tree" divs + if (href.indexOf("/reference/") != -1) { + var swapperHeight = windowHeight - 13; + $("#swapper").css({height:swapperHeight + "px"}); + $("#classes-nav").css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); + $("#nav-tree").css({height:swapperHeight + "px"}); + + // Also resize the "devdoc-nav" div + } else if ($("#devdoc-nav").length) { + $("#devdoc-nav").css({height:sidenav.css("height")}); + } + + // Hide the "Go to top" link if there's no vertical scroll + if ( parseInt($("#jd-content").css("height")) <= parseInt(docContent.css("height")) ) { + $("a[href='#top']").css({'display':'none'}); + } else { + $("a[href='#top']").css({'display':'inline'}); + } +} + +/* Resize the width of the "side-nav" and the left margin of the "doc-content" div, + * which creates the resizable side bar */ +function resizeWidth() { + var windowWidth = $(window).width() + "px"; + var sidenav = $("#side-nav"); + if (sidenav.length) { + var sidenavWidth = sidenav.css("width"); + } else { + var sidenavWidth = 0; + } + content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px"}); //account for 6px-wide handle-bar + + if (isIE6) { + content.css({width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"}); // necessary in order to for scrollbars to be visible + } + + resizePackagesNav.css({width:sidenavWidth}); + classesNav.css({width:sidenavWidth}); + $("#packages-nav").css({width:sidenavWidth}); + + if (sidenav.length) { // Must check if the nav exists because IE6 calls resizeWidth() from resizeAll() for all pages + var basePath = getBaseUri(location.pathname); + var section = basePath.substring(1,basePath.indexOf("/",1)); + section = section.indexOf("training") != -1 ? "resources" : section; + writeCookie("width", sidenavWidth, section, null); + } +} + +/* For IE6 only, + * because it can't properly perform auto width for "doc-content" div, + * avoiding this for all browsers provides better performance */ +function resizeAll() { + resizeHeight(); + resizeWidth(); +} + +function getBaseUri(uri) { + var intlUrl = (uri.substring(0,6) == "/intl/"); + if (intlUrl) { + base = uri.substring(uri.indexOf('intl/')+5,uri.length); + base = base.substring(base.indexOf('/')+1, base.length); + //alert("intl, returning base url: /" + base); + return ("/" + base); + } else { + //alert("not intl, returning uri as found."); + return uri; + } +} + +function requestAppendHL(uri) { +//append "?hl= to an outgoing request (such as to blog) + var lang = getLangPref(); + if (lang) { + var q = 'hl=' + lang; + uri += '?' + q; + window.location = uri; + return false; + } else { + return true; + } +} + +function loadLast(cookiePath) { + var location = window.location.href; + if (location.indexOf("/"+cookiePath+"/") != -1) { + return true; + } + var lastPage = readCookie(cookiePath + "_lastpage"); + if (lastPage) { + window.location = lastPage; + return false; + } + return true; +} + +$(window).unload(function(){ + var path = getBaseUri(location.pathname); + if (path.indexOf("/reference/") != -1) { + writeCookie("lastpage", path, "reference", null); + } else if (path.indexOf("/guide/") != -1) { + writeCookie("lastpage", path, "guide", null); + } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) { + writeCookie("lastpage", path, "resources", null); + } +}); + +function toggle(obj, slide) { + var ul = $("ul:first", obj); + var li = ul.parent(); + if (li.hasClass("closed")) { + if (slide) { + ul.slideDown("fast"); + } else { + ul.show(); + } + li.removeClass("closed"); + li.addClass("open"); + $(".toggle-img", li).attr("title", "hide pages"); + } else { + ul.slideUp("fast"); + li.removeClass("open"); + li.addClass("closed"); + $(".toggle-img", li).attr("title", "show pages"); + } +} + +function buildToggleLists() { + $(".toggle-list").each( + function(i) { + $("div:first", this).append(""); + $(this).addClass("closed"); + }); +} + +function getNavPref() { + var v = readCookie('reference_nav'); + if (v != NAV_PREF_TREE) { + v = NAV_PREF_PANELS; + } + return v; +} + +function chooseDefaultNav() { + nav_pref = getNavPref(); + if (nav_pref == NAV_PREF_TREE) { + $("#nav-panels").toggle(); + $("#panel-link").toggle(); + $("#nav-tree").toggle(); + $("#tree-link").toggle(); + } +} + +function swapNav() { + if (nav_pref == NAV_PREF_TREE) { + nav_pref = NAV_PREF_PANELS; + } else { + nav_pref = NAV_PREF_TREE; + init_default_navtree(toRoot); + } + var date = new Date(); + date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years + writeCookie("nav", nav_pref, "reference", date.toGMTString()); + + $("#nav-panels").toggle(); + $("#panel-link").toggle(); + $("#nav-tree").toggle(); + $("#tree-link").toggle(); + + if ($("#nav-tree").is(':visible')) scrollIntoView("nav-tree"); + else { + scrollIntoView("packages-nav"); + scrollIntoView("classes-nav"); + } +} + +function scrollIntoView(nav) { + var navObj = $("#"+nav); + if (navObj.is(':visible')) { + var selected = $(".selected", navObj); + if (selected.length == 0) return; + if (selected.is("div")) selected = selected.parent(); // when the selected item is a parent + + var scrolling = document.getElementById(nav); + var navHeight = navObj.height(); + var offsetTop = selected.position().top; + + // handle nested items + if (selected.parent().parent().is(".toggle-list")) { + selected = selected.parent().parent(); + // handle second level nested items + if (selected.parent().parent().is(".toggle-list")) { + selected = selected.parent().parent(); + } + offsetTop += selected.position().top; + } + + // 180px from the bottom of the list is the threshold + if(offsetTop > navHeight - 180) { + scrolling.scrollTop = offsetTop - navHeight + 180; + } + } +} + +function changeTabLang(lang) { + var nodes = $("#header-tabs").find("."+lang); + for (i=0; i < nodes.length; i++) { // for each node in this language + var node = $(nodes[i]); + node.siblings().css("display","none"); // hide all siblings + if (node.not(":empty").length != 0) { //if this languages node has a translation, show it + node.css("display","inline"); + } else { //otherwise, show English instead + node.css("display","none"); + node.siblings().filter(".en").css("display","inline"); + } + } +} + +function changeNavLang(lang) { + var nodes = $("#side-nav").find("."+lang); + for (i=0; i < nodes.length; i++) { // for each node in this language + var node = $(nodes[i]); + node.siblings().css("display","none"); // hide all siblings + if (node.not(":empty").length != 0) { // if this languages node has a translation, show it + node.css("display","inline"); + } else { // otherwise, show English instead + node.css("display","none"); + node.siblings().filter(".en").css("display","inline"); + } + } +} + +function changeDocLang(lang) { + changeTabLang(lang); + changeNavLang(lang); +} + +function changeLangPref(lang, refresh) { + var date = new Date(); + expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); // keep this for 50 years + //alert("expires: " + expires) + writeCookie("pref_lang", lang, null, expires); + //changeDocLang(lang); + if (refresh) { + l = getBaseUri(location.pathname); + window.location = l; + } +} + +function loadLangPref() { + var lang = readCookie("pref_lang"); + if (lang != 0) { + $("#language").find("option[value='"+lang+"']").attr("selected",true); + } +} + +function getLangPref() { + var lang = $("#language").find(":selected").attr("value"); + if (!lang) { + lang = readCookie("pref_lang"); + } + return (lang != 0) ? lang : 'en'; +} + + +/* Used to hide and reveal supplemental content, such as long code samples. + See the companion CSS in android-developer-docs.css */ +function toggleContent(obj) { + var div = $(obj.parentNode.parentNode); + var toggleMe = $(".toggle-content-toggleme",div); + if (div.hasClass("closed")) { // if it's closed, open it + toggleMe.slideDown(); + $(".toggle-content-text", obj).toggle(); + div.removeClass("closed").addClass("open"); + $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot + "assets/images/triangle-opened.png"); + } else { // if it's open, close it + toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow + $(".toggle-content-text", obj).toggle(); + div.removeClass("open").addClass("closed"); + $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot + "assets/images/triangle-closed.png"); + }); + } + return false; +} diff --git a/make/tools/droiddoc/templates-pdk/assets/android-developer-reference.js b/make/tools/droiddoc/templates-pdk/assets/android-developer-reference.js new file mode 100644 index 0000000..ba47d4d --- /dev/null +++ b/make/tools/droiddoc/templates-pdk/assets/android-developer-reference.js @@ -0,0 +1,411 @@ + +/* API LEVEL TOGGLE */ +addLoadEvent(changeApiLevel); + +var API_LEVEL_ENABLED_COOKIE = "api_level_enabled"; +var API_LEVEL_COOKIE = "api_level"; +var minLevel = 1; +var maxLevel = 1; + +function toggleApiLevelSelector(checkbox) { + var date = new Date(); + date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years + var expiration = date.toGMTString(); + if (checkbox.checked) { + $("#apiLevelSelector").removeAttr("disabled"); + $("#api-level-toggle label").removeClass("disabled"); + writeCookie(API_LEVEL_ENABLED_COOKIE, 1, null, expiration); + } else { + $("#apiLevelSelector").attr("disabled","disabled"); + $("#api-level-toggle label").addClass("disabled"); + writeCookie(API_LEVEL_ENABLED_COOKIE, 0, null, expiration); + } + changeApiLevel(); +} + +function buildApiLevelSelector() { + maxLevel = SINCE_DATA.length; + var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE); + var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE)); + userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default + + if (userApiLevelEnabled == 0) { + $("#apiLevelSelector").attr("disabled","disabled"); + } else { + $("#apiLevelCheckbox").attr("checked","checked"); + $("#api-level-toggle label").removeClass("disabled"); + } + + minLevel = parseInt($("body").attr("class")); + // Handle provisional api levels; the provisional level will always be the highest possible level + // Provisional api levels will also have a length; other stuff that's just missing a level won't, + // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class) + if (isNaN(minLevel) && minLevel.length) { + minLevel = maxLevel; + } + var select = $("#apiLevelSelector").html("").change(changeApiLevel); + for (var i = maxLevel-1; i >= 0; i--) { + var option = $("