android 13 from xiaosuan

This commit is contained in:
cpeng 2025-08-25 08:23:45 +08:00
commit e6bbafb4ff
2455 changed files with 491055 additions and 0 deletions

1
CleanSpec.mk Symbolic link
View File

@ -0,0 +1 @@
make/CleanSpec.mk

1
bazel/OWNERS Normal file
View File

@ -0,0 +1 @@
include platform/build/soong:/OWNERS

5
bazel/README.md Normal file
View File

@ -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.

14
bazel/bazel.BUILD Normal file
View File

@ -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"])

91
bazel/bazel.WORKSPACE Normal file
View File

@ -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",
)

198
bazel/bazel.sh Executable file
View File

@ -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 <targets>"
exit 1
fi
}
function create_bazelrc() {
cat > "${ABSOLUTE_OUT_DIR}/bazel/path.bazelrc" <<EOF
# This file is generated by tools/bazel. Do not edit manually.
build --action_env=PATH=${RESTRICTED_PATH}
EOF
}
case "x${ANDROID_BAZELRC_PATH}" in
x)
# Path not provided, use default.
ANDROID_BAZELRC_PATH="${TOP}/build/bazel"
;;
x/*)
# Absolute path, take it as-is.
ANDROID_BAZELRC_PATH="${ANDROID_BAZELRC_PATH}"
;;
x*)
# Relative path, consider it relative to TOP.
ANDROID_BAZELRC_PATH="${TOP}/${ANDROID_BAZELRC_PATH}"
;;
esac
if [ -d "${ANDROID_BAZELRC_PATH}" ]; then
# If we're given a directory, find the correct bazelrc file there.
ANDROID_BAZELRC_PATH="${ANDROID_BAZELRC_PATH}/${ANDROID_BAZELRC_NAME}"
fi
if [ -n "$ANDROID_BAZEL_PATH" -a -f "$ANDROID_BAZEL_PATH" ]; then
export ANDROID_BAZEL_PATH
else
>&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[@]}" \
"$@"

130
bazel/ci/bp2build.sh Executable file
View File

@ -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"

88
bazel/ci/diffs.sh Executable file
View File

@ -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

12
bazel/ci/dist/BUILD vendored Normal file
View File

@ -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,
)

30
bazel/ci/mixed_droid.sh Executable file
View File

@ -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

41
bazel/ci/mixed_libc.sh Executable file
View File

@ -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

467
bazel/ci/rbc_dashboard.py Executable file
View File

@ -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'<td style="background-color: {"lightgreen" if success and not diffs else "salmon"}">{message}</td>'
return f'''
<tr>
<td>{num}</td>
<td>{product if results.success() and results.baseline_success else f'<a href="{product}/">{product}</a>'}</td>
{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)}
</tr>
'''
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'''
<body>
<h2>RBC Product/Board conversion status</h2>
Generated on {datetime.date.today()} for branch {get_branch()}
<table>
<tr>
<th>#</th>
<th>product</th>
<th>baseline</th>
<th>RBC product config</th>
<th>RBC board config</th>
</tr>\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'''
<tr>
<td></td>
<td># Successful</td>
<td>{baseline_successes}</td>
<td>{product_successes}</td>
<td>{board_successes}</td>
</tr>
<tr>
<td></td>
<td># Failed</td>
<td>N/A</td>
<td>{baseline_successes - product_successes}</td>
<td>{baseline_successes - board_successes}</td>
</tr>
</table>
Finished running successfully.
</body>\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())

92
bazel/ci/rbc_regression_test.sh Executable file
View File

@ -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 <<EOF >&2
Usage: $myname [-p] [-b] [-q] [-r] <product-variant> [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 <product>-<variant>\n" $myname >&2
exit $rc

109
bazel/common.bazelrc Normal file
View File

@ -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

3
bazel/darwin.bazelrc Normal file
View File

@ -0,0 +1,3 @@
import %workspace%/build/bazel/common.bazelrc
build --host_platform //build/bazel/platforms:darwin_x86_64

198
bazel/docs/concepts.md Normal file
View File

@ -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.|
|`<script>.sh`|Running arbitrary scripts in AOSP.|Conceptually similar to `bazel run` command.|
|Make (replaced in-place by Kati)|No longer in use. Entire build system, replaced by the tools above.|Loading, analysis, execution phases. Conceptually similar to `bazel build` command.|
|`Android.bp`|Build definition file for Soong.|`BUILD.bazel` or `BUILD`.|
|`Android.mk`|Build definition file for Kati.|`BUILD.bazel` or `BUILD`.|
## Communication between components
* Kati product configuration component to generate config variables (config.mk, AndroidProducts.mk)
* **“Kati-config” for the purpose of this document**
* Kati component to generate build actions in Ninja files (main.mk, Android.mk files)
* **“Kati-build” for the purpose of this document**
* Kati component to generate packaging actions in Ninja files (packaging.mk file)
* **“Kati-package” for the purpose of this document**
* Kati component to generate cleaning actions in Ninja files (cleanbuild.mk, CleanSpec.mk files)
* **"Kati-cleanspec" for the purpose of this document**
* **soong\_build** (and **Blueprint**) component to generate build actions (Android.bp, Blueprints files)
* **Ninja** component to execute actions from Kati-build, Kati-package and soong\_build
* **Bazel** as the next generation of the entire build system, starting as a Ninja executor drop-in replacement
* **soong\_ui** as the tool to orchestrate all of the above, and with auxiliary tools like finder, path\_interposer, soong\_env, minibp and bpglob.
The current build system architecture primarily uses **files** as the medium
for inter-process communication (IPC), with one known case of unix socket
communication (e.g.
[path\_interposer](https://cs.android.com/android/platform/superproject/+/master:build/soong/ui/build/paths/logs.go;l=112-133;drc=184901135cda8bdcc51cab4f16c401a28a510593)),
and a fifo between Ninja and soong_ui for the Protobuf stream for build
status reporting.
## Component order
The build system components run in the following order, orchestrated by soong\_ui:
1. soong\_ui bootstraps itself with microfactory (`go build` replacement) and launches.
1. soong\_ui runs auxiliary tools to aggregate files into filelists, for
Android.mk, Android.bp, AndroidProducts.mk and several others.
1. soong\_ui runs Kati-config with
[the config.mk entry point](https://cs.android.com/android/platform/superproject/+/master:build/soong/ui/build/dumpvars.go;l=89;drc=9f43597ff7349c4facd9e338e5b4b277e625e518).
1. soong\_ui orchestrates 3 Blueprint/Soong phases to generate the main out/soong/build.ninja file:
minibootstrap, bootstrap, and primary.
1. Minibootstrap phase uses Blueprint/Microfactory to build itself
(minibp) so that Android.bp and Blueprint files can be used to define
Soong.
1. Bootstrap phase runs Ninja on a build.ninja file that runs minibp to
read all Android.bp files across the source tree that describes Soong and
plugins, and builds soong\_build.
1. Primary phase runs Ninja on a build.ninja file that runs soong_build
to generate the final out/soong/build.ninja file.
1. soong\_build also runs its own tests alongside generating
out/soong/build.ninja, which can be skipped with the `--skip-soong-tests`
argument.
1. soong\_ui runs Kati-cleanspec with
[the cleanbuild.mk entry point](https://cs.android.com/android/platform/superproject/+/master:build/soong/ui/build/kati.go;l=362;drc=b1d30d63c5d1b818ea38e77cd155da2016fe8b6c).
1. soong\_ui runs Kati-build to generate a Ninja file, with
[the main.mk entry point.](https://cs.android.com/android/platform/superproject/+/master:build/soong/ui/build/kati.go;l=202;drc=b1d30d63c5d1b818ea38e77cd155da2016fe8b6c)
1. soong\_ui runs Kati-package to generate a Ninja file, with
[the packaging/main.mk](https://cs.android.com/android/platform/superproject/+/master:build/soong/ui/build/kati.go;l=314;drc=b1d30d63c5d1b818ea38e77cd155da2016fe8b6c)
entry point.
1. soong\_ui generates a Ninja file to combine above Ninja files.
1. soong\_ui runs either Ninja or Bazel to execute the build, with the
combined Ninja file as entry point.
soong\_ui has a --skip-make flag that will skip Kati-config, Kati cleanspec,
Kati-build and Kati-package, used for Soong-only builds in NDK and some
Mainline projects.
### soong\_ui
soong\_ui is primarily responsible for orchestrating the build, cleaning the
build environment, and running auxiliary tools. These tools (minibp,
microfactory) can bootstrap other tools (soong\_build), aggregate file lists
(finder.go), improve hermeticity (path\_interposer, nsjail) or perform checks
against the environment (soong\_env).
soong\_ui uses finder.go to generate &lt;filename>.list files for other
tools. For example, it generates Android.mk.list for Kati-build,
AndroidProducts.mk.list for Kati-config, and Android.bp.list for
soong\_build.
soong\_ui uses path\_interposer to prepare an hermetic $PATH with runtime
checks against allowlisted system tools. The $PATH contains these system
tools with checked-in prebuilts, and uses path\_interposer to intercept calls
and error out whenever non-allowlisted tools are used (see out/.path for
directory of intercepted tool symlinks).
soong\_ui generates a Kati suffix to ensure that Kati-generated files are
regenerated if inputs to Kati have changed between builds.
soong\_ui calls Soong and Kati to generate Ninja files, and eventually
creates another Ninja file (out/combined-&lt;product>.ninja) to combine the
others, and executes either Ninja or Bazel to complete the build.
soong\_ui sets up the sandbox and environment for the Ninja/Bazel process.
## Kati-config
As a product configuration tool, soong\_ui runs Kati-config in
**[--dumpvars-mode](https://cs.android.com/android/platform/superproject/+/master:build/soong/cmd/soong_ui/main.go;l=298-305;drc=master)**
to dump the values of specified Make variables at the end of an evaluation,
with build/make/core/config.mk as the entry point. During this phase,
Kati-config eventually evaluates[
soong\_config.mk](https://cs.android.com/android/platform/superproject/+/master:build/make/core/soong_config.mk;l=2?q=soong.variables)
to generate the **[soong.variables JSON
file](https://cs.android.com/android/platform/superproject/+/master:build/make/core/soong_config.mk;l=16-222;drc=341928ecc5da205401bcfd86f098662b0cee7857)**.
This way, Kati-config can communicate product configuration to soong\_build,
as soong\_build parses the dumped variables from the JSON on startup, and
stores them into an in-memory Config object.
To communicate
[dexpreopt](https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt.go;l=115;drc=8cbc5d269b20b5743679bfb8684ed174dcf58a30)
variables to soong\_build, [dexpreopt.config is also
generated](https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config.mk;l=71-142;drc=f26015449f0747b9fdeceb5ce70e30ecd76e20e8)
as a $(shell) action and [read by
soong\_build](https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/config.go;l=175-196;drc=1af783fae74715bcf1a94733bd75b2e6cc688e8c)
in a similar way as Kati-config evaluates dex\_preopt\_config.mk included in
soong\_config.mk.
soong\_ui sets up a **KatiReader** to monitor Kati-configs stdout/err for UI
reporting and error handling purposes.
## soong\_build
soong\_builds primary role is to evaluate all Android.bp files, run a series
of mutators, and generate out/soong/build.ninja file.
soong\_build communicates with Kati-build by generating Make Vars and running
the AndroidMk singleton to generate .mk files in the output directory
(out/soong/{Android, late, make\_vars}-&lt;product>.mk).
* Android-&lt;product>.mk contains Soong modules as Make modules so Make
modules can depend on Soong modules.
* make\_vars-&lt;product>.mk contains Make variables for Kati-build, exported
from Soong modules. There are also checks built into this .mk file to ensure that
if a duplicate Make variable of the same name comes from another source, the Soong
and Make variable values are identical.
* late-&lt;product>.mk contains Make variables that are not read while Kati-build
parses the Android.mk file. (Late variables)
* soong\_ui invokes Kati to parse make\_vars .mk file earlier than the Android.mk
files,and late.mk after parsing the Android.mk files.
* late.mk is used to define phony rules to take advantage of Makes ability to
add extra dependencies to an existing rule. late.mk is not strictly necessary to
make this happen at this moment, since late.mk rules dont currently depend on any
variables defined during Android.mk processing (e.g. ALL\_MODULES$(module).INSTALLED).
## Kati-build / Kati-package
Kati-builds primary role is to evaluate all Android.mk files with
build/make/core/main.mk as entry point, and generate
out/build-&lt;product>.ninja. It also generates cleanspec.ninja for the
product, containing statements on how to remove stale output files.
Kati-builds primary role is to evaluate all packaging .mk files with
build/make/packaging/main.mk as entry point, including
build/make/packaging/distdir.mk for dist-for-goals calls, and generate
out/package-&lt;product>.ninja.
Kati-build/Kati-packages stdout/stderr is monitored by soong\_uis
KatiReader to UI and error handling.
As Kati-build/Kati-package generates Ninja files, they also generate
out/ninja-&lt;product>.sh and out/env-&lt;product>.sh. These scripts are
wrappers for soong\_ui to execute Ninja with the correct Ninja files, in a
controlled environment.
## Ninja
As Ninja executes files from Kati-build, Kati-package, soong\_build and other
bootstrapping tools like Blueprint, it writes to a fifo in a proto front end
that soong\_ui monitors with NinjaReader. NinjaReader ensures that the user
interface for Ninja progress is consistent with the rest of the build.
## Bazel
As more Soong modules are converted to BUILD files, soong\_build serializes
information about converted modules to BUILD/bzl files on disk. soong\_build
then consumes information about these targets from Bazel by directly calling
the Bazel client to issue `cquery` calls about these targets.

View File

@ -0,0 +1,616 @@
# Soong-Bazel equivalents
This doc aims to describe *internal*-facing implementation concepts. For
external-facing, see
https://android.googlesource.com/platform/build/bazel/+/refs/heads/master/docs/concepts.md.
[TOC]
## Overview
Soong/Ninja | Bazel | Remarks
--------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -------
make phony goal, e.g. "dist", "sdk", "apps_only", "droidcore" | Top level `filegroup` rule target | [Details](#phony-goal)
Ninja build target (phony) | (readable) alias to a file target |
Ninja build target (non-phony) | File target |
`ModuleFactory` | `RuleConfiguredTargetFactory` |
`Module type` (e.g. `cc_library`) | Rule class (e.g. `cc_library`) |
Module object instance | Target (instance of a rule) | [Details](#instance)
Module properties | [Rule attributes](https://docs.bazel.build/versions/main/skylark/rules.html#attributes) | [Details](#props)
Module name | Target label |
Module variant | (Split) configured target |
[LoadHooks](#loadhooks) | [macros (ish)](https://docs.bazel.build/versions/main/skylark/macros.html) |
Top-down mutators on modules | Split configuration on targets | Allows building multiple "variants" of the same build artifact in the same build.
Bottom-up mutators on modules | [Aspects](https://docs.bazel.build/versions/main/skylark/aspects.html) on targets |
[Build statement (Ninja)](#ninja-build-statement) | Action (result of ctx.actions.run) |
[Rule statement (Ninja)](#ninja-rules) | [ctx.actions.run() API](https://docs.bazel.build/versions/main/skylark/lib/actions.html) |
`out/soong/build.ninja` and `out/build-<target>.ninja` | Action graph (serialized) |
Pool (ninja) | Thread pools / `ExecutorService` |
Blueprint's Registration and Parse, `ResolveDependencies` phase | Loading phase |
Blueprint's [Generate and Write phases](#blueprint-analysis) | Analysis Phase |
Ninja execution | Execution phase |
Blueprints/`Android.bp` files | `BUILD`/`BUILD.bazel` files |
[Namespaces](#namespaces) | [Packages](#pkgs) | Most Soong modules are within the global namespace
[Mutators](#mutators) | Configuration keys (ish) |
[Variation](#variation) | Configuration value |
[Singleton](#singleton) | Aspect-ish |
Target (system + vendor + product) | [Platform](https://docs.bazel.build/versions/main/platforms.html) |
Bash scripts e.g. envsetup functions, `soong_ui.bash`) | Repository rule |
Product and board configuration makefile and env variables | Configuration in Bazel (ish) | [Details](#config)
[Dependency Tags](#deptags) | Provider names |
## Remarks
### Phony goals {#phony-goal}
Soong maintains the make terminology of
[goals](https://www.gnu.org/software/make/manual/html_node/Goals.html) to denote
what should be built. All modules can be specified by name as a goal, in
addition, phony goals are supported.
A Phony goal creates a Make-style phony rule, a rule with no commands that can
depend on other phony rules or real files. Phony can be called on the same name
multiple times to add additional dependencies. These are often used to build
many targets at once. The default goal for Android's build system is `droid`.
Some other common phony goals include: `nothing` (perform loading/analysis),
`docs`, `checkbuild`, `apps_only`.
Some common phony goals are defined in
[`build/make/core/main.mk`](http://cs.android.com/android/platform/superproject/+/master:build/make/core/main.mk)
The purpose is to help `soong_ui` to determine what top level files to build.
### Module/Target {#instance}
When a Module is instantiated by Blueprint (which calls the appropriate
`ModuleFactory`), the [property structs](#props) are populated by Blueprint.
Blueprint performs no additional operations on these properties, such that
dependencies on other modules and references to source files are unresolved
initially. [`Mutators`](#mutators) then introspect the values of properties to
[specify dependencies](https://cs.android.com/android/platform/superproject/+/master:build/blueprint/module_ctx.go;l=871-886,918-960;drc=030150d8f9d164783ea661f07793c45198739cca)
between modules, which
[Blueprint resolves](https://cs.android.com/android/platform/superproject/+/master:build/blueprint/context.go;l=1630,1667;drc=5c4abb15e3b84ab0bcedfa119e2feb397d1fb106).
Source files (including globs) and output paths for references to other modules
are resolved during [blueprint analysis](#blueprint-analysis) via the various
`Path[s]ForModuleSrc[Excludes]` functions within
[build/soong/android/paths.go](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/paths.go).
For a Bazel target instance, the dependencies and source file references within
[`attrs`](#attributes) have been resolved by Bazel.
Bazel
[implementation](https://github.com/bazelbuild/bazel/blob/a20b32690a71caf712d1d241f01fef16649562ba/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java#L113-L140)
to collect deps.
### Properties/Attributes {#props}
#### Properties
Within Soong/Blueprint, properties are represented as Go structs, which can be
nested, with no depth limit. Properties can be primitive or pointer types, but
they must be one of these types: `int64`, `string`, `bool`, `list`.
These properties can be defined from various structs within the module type
factory itself (via
[AddProperties](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=1276;drc=8631cc7327919845c9d9037188cbd483d22ba077))
or from common helper functions such as:
* `InitAndroidModule`:
[specifies](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=1042-1045;drc=8631cc7327919845c9d9037188cbd483d22ba077)
name-related, common, and dist properties.
* `InitAndroidArchModule`: adds
[host/device properies](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=1077;drc=8631cc7327919845c9d9037188cbd483d22ba077)
Go comments for a property will be treated as documentation to describe the
property. In some cases, these comments describe a default value for the
property. However, the default value is not based on the comment or field
definition but resolved somewhere within the module's mutators or build. These
defaults are often determined using Blueprint
[`proptools`](https://cs.android.com/android/platform/superproject/+/master:build/blueprint/proptools/proptools.go)
`*Default` functions. For example, `cc` modules have a property
[`include_build_directory`](https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/compiler.go;l=265;drc=135bf55281d79576f33469ce4f9abc517a614af5),
which is described in the comments. The default value is
[resolved](https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/compiler.go;l=265;drc=135bf55281d79576f33469ce4f9abc517a614af5)
when compiler flags are being determined.
In general, these can be set in an Android.bp file. However, if the property is
tagged with `` `blueprint:"mutated"` ``, it can only be set programmatically
within Blueprint/Soong. Additionally, `mutated` tagged properties also support
`map` and `int` types in addition to those mentioned above. These `mutated`
properties are used to propagate data that gets set during mutations, which
ensures that the information is copied successfully to module variants during
mutation.
Soong supports additional property tags to provide additional
functionality/information about a property:
* `` `android:arch_variant` ``: This specifies that a property can be
configured for different architectures, operating systems, targets, etc. The
[arch mutator](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/arch.go;l=597;drc=135bf55281d79576f33469ce4f9abc517a614af5),
will merge target-specific properties into the correct variant for
properties with this tag.
Note: if a nested property is arch-variant, all recursively nesting structs
that can be specified in an Android.bp file must also be tagged as
arch-variant.
* `` `android:variant_prepend` ``: When merging properties for the arch
variant, the arch-specific values should be *prepended* rather than appended
to existing property values.
* `` `android:path` ``: This specifies that this property will contain some
combination of:
* module-relative paths
* references to other modules in the form:
* `":<name>{.<tag>}"`, where `{.<tag>}` is optional to specify a
non-default output file, specific to the module type
* `"<namespace>:<name>{.<tag>}""`
Note: Dependencies to other modules for these properties will be
automatically added by the
[pathdeps mutator](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/path_properties.go;l=40;drc=40131a3f9e5ac974a44d3bd1293d31d585dc3a07).
#### Attributes
Similar to properties,
[attributes](https://docs.bazel.build/versions/main/skylark/lib/attr.html) only
support a few types. The difference is that Bazel attributes cannot be nested .
Some attributes are
[common](https://docs.bazel.build/versions/2.1.0/be/common-definitions.html#common-attributes)
across many/all rule classes, including (but not limited to) `name`, `tag`,
`visibility`.
The definition of an attribute can contain settings, such as: its default value,
whether it is mandatory ot have a value, and its documentation.
To specify a source file or reference to another module, use `label` or
`label_list` attribute types (rather than regular `string` or `string_list`
types). These support additional restrictions (as compared to `string*` types),
such as:
* whether files are supported
* the providers that must be given by a dependency
* whether the dependency should be executable
* the configuration (host, target)
* aspects
Unlike Soong, when accessing this attribute within the rule's implementation (at
anlysis time), the label(s) will be resolved to the file or target they refer
to.
Attributes do not need to specify whether they accept
[configurable attribute](https://docs.bazel.build/versions/main/configurable-attributes.html).
However, the rule definition can specify the configuration or specify a
[configuration transition](https://docs.bazel.build/versions/main/skylark/lib/transition.html).
However, not all target definitions within a `BUILD` file are invoking a rule.
Instead, they may invoke a Starlark macro, which is a load-time wrapper around
rules. Arguments for a macro are not typed. If macros are used, their arguments
would have to be wrangled into an attribute-compatible type.
### LoadHooks
[LoadHooks](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/hooks.go;l=24-36;drc=07656410df1836a70bea3054e50bb410ecbf8e07)
provide access to :
* append/prepend additional properties to the module
(`AppendProperties`/`PrependProperties`)
* create a new module `CreateModule`
`LoadHooks` make it easier to extend existing module factories to always specify
certain properties or to split a single `Android.bp` definition into multiple
Module instances .
### Build Statement (ninja) {#ninja-build-statement}
[Ninja build statements](https://ninja-build.org/manual.html#_build_statements) can be
expanded from [Ninja rules](https://ninja-build.org/manual.html#_rules), which are like
templates.
```
# rule
rule cattool
depfile = out/test/depfile.d
command = ${in} ${out}
# build statement
build out/test/output.txt: cattool test/cattool.sh test/one test/two
# build statement
build out/test/other_output.txt: cattool test/cattool.sh test/three test/four
```
Rules for `Android.mk` modules (`out/build-<target>.ninja`) and build statements
are 1:1. That is every rule is only used once by a single build statement.
Soong (`out/soong/build.ninja`) rules are reused extensively in build statements
(1:many). For example the `Cp` rule is a commonly used rule for creating build
statements which copy files.
### Ninja Rules in Soong {#ninja-rules}
In Soong, Ninja rules can be defined in two ways:
* [rule_builder](http://cs.android.com/android/platform/superproject/+/master:build/soong/android/rule_builder.go)
* [package_ctx](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/package_ctx.go;l=102-293;drc=77cdcfdeafd383ef1f1214226c47eb20c902a28f)
### Blueprint Generate & Write phase {#blueprint-analysis}
1. [`ResolveDependencies`](https://cs.android.com/android/platform/superproject/+/master:build/blueprint/context.go;l=1547;drc=5c4abb15e3b84ab0bcedfa119e2feb397d1fb106)
Running a series of Mutators, to add dependencies, split modules with
variations, etc
1. [`PrepareBuildActions`](https://cs.android.com/android/platform/superproject/+/master:build/blueprint/context.go;l=2367;drc=5c4abb15e3b84ab0bcedfa119e2feb397d1fb106):
1. Running Modules `GenerateBuildActions` to generate Ninja statements,
which in turn calls each module's
[`GenerateAndroidBuildActions`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=445-448;drc=8631cc7327919845c9d9037188cbd483d22ba077).
1. Running Singletons to generate Ninja statements that generate docs,
android.mk statements, etc
### Soong namespaces {#namespace}
Module
[Namespaces](https://android.googlesource.com/platform/build/soong/+/master/README.md#namespaces)
can import other namespaces, and theres a module name lookup algorithm which
terminates in the global namespace.
Note: this is not widely used and most Soong modules are in the global
namespace.
### Bazel packages {#pkgs}
[Packages](https://docs.bazel.build/versions/main/build-ref.html#packages) can
nest subpackages recursively, but they are independent containers of Bazel
targets. This means that Bazel target names only need to be unique within a
package.
### Mutators
blueprint invokes mutators are invoking in the order they are registered (e.g.
top-down and bottom-up can be interleaved). Each mutator applys a single
visitation to every module in the graph.
Mutators visiting module can parallelized, while maintaining their ordering, by
calling `.Parallel()`.
While top-down and bottom-up mutators differ in their purposes, the interface
available to each contains many similarities. Both have access to:
[`BaseModuleContext`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=139;drc=8631cc7327919845c9d9037188cbd483d22ba077)
and
[`BaseMutatorContext`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/mutator.go;l=246;drc=2ada09a5463a0108d713773679c5ba2c35450fa4).
In addition to the registration order, Soong supports phase-based ordering of
mutators:
1. Pre-Arch: mutators that need to run before arch-variation. For example,
defaults are handled at this stage such properties from defaults are
correctly propagated to arch-variants later.
1. (Hard-coded)
[`archMutator`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/arch.go;l=597;drc=135bf55281d79576f33469ce4f9abc517a614af5)
splits a module into the appropriate target(s). Next, the arch- and
OS-specific properties are merged into the appropriate variant.
1. Pre-Deps: mutators that can/need to run before deps have been resolved, for
instance, creating variations that have an impact on dependency resolution.
1. (Hard-coded)
[`depsMutator`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/mutator.go;l=502;drc=2ada09a5463a0108d713773679c5ba2c35450fa4),
which calls the `DepsMutator` function that *must* be part of a Soong
`Module`'s interface.
1. Post-Deps: mutators that need to run after deps have been resolved
1. Final-Deps like post-deps but variations cannot be created
#### Top-down Mutator
A top-down mutator is invoked on a module before its dependencies.
The general purpose is to propagate dependency info from a module to its
dependencies.
#### Bottom-up Mutator
A bottom-up mutator is invoked on a module only after the mutator has been
invoked on all its dependencies.
The general purpose of a bottom-up mutator is to split modules into variants.
### Soong/Blueprint Variation {#variation}
A tuple (name of mutator, variation / config value) passed to
`CreateVariations`.
### Configuration {#config}
Soong's config process encompasses both *what* should build and *how* it should
build. This section focuses on the *how* aspect.
We do not cover how Soong's configuration will be implemented in Bazel, but the
general capabilities of Bazel to configure builds.
#### Soong
Android users can configure their builds based on:
* Specifying a target (via lunch, banchan, tapas, or Soongs command line
options)
* Environment variables
Some environment variables or command line options are used directly to alter
the build. However, specification of target product encompasses many aspects of
both *what* and *how* things are built. This configuration is currently handled
within Make but is in the process of being migrated to Starlark.
Soong
[invokes Kati](https://cs.android.com/android/platform/superproject/+/master:build/soong/ui/build/dumpvars.go;drc=7ae80a704494bbb934dced97ed97eb55a21a9a00)
to run in a "config" mode, also commonly known as "product config". This mode
limits the scope of what `.mk` files are parsed. The product-specific handlers
are largely in:
* [`product_config.mk`](https://cs.android.com/android/platform/superproject/+/master:build/make/core/product_config.mk;drc=d189ab71f3505ea28324ebfaced2466af5eb0af7):
this subset of functionality is also commonly referred to as "product
config"
* [`board_config.mk`](https://cs.android.com/android/platform/superproject/+/master:build/make/core/board_config.mk)
However, these cover only a subset of
[`config.mk`](https://cs.android.com/android/platform/superproject/+/master:build/make/core/config.mk).
This ensures that all values have appropriate defaults and specify details
necessary to the build. Some examples:
* [handling of version defaults](https://cs.android.com/android/platform/superproject/+/master:build/make/core/version_defaults.mk)
* [rbe setup](https://cs.android.com/android/platform/superproject/+/master:build/make/core/rbe.mk)
* [user-defined config](https://cs.android.com/android/platform/superproject/+/master:build/make/core/config.mk;l=300-308;drc=ee20ae1a8dcdfe7b843d65099000708800d9b93a):
[buildspec.mk](http://cs.android.com/android/platform/superproject/+/master:build/make/buildspec.mk.default)
is similar to
[`.bazelrc`](https://docs.bazel.build/versions/main/guide.html#bazelrc-the-bazel-configuration-file)
file.
* ensuring
[`PRODUCT_SHIPPING_API_LEVEL`](https://cs.android.com/android/platform/superproject/+/master:build/make/core/config.mk;l=729-745;drc=ee20ae1a8dcdfe7b843d65099000708800d9b93a)
is defaulted if not specified by the target.
Finally, Kati dumps variables to be consumed by Soong:
* environment variables specifically requested by Soong
* writes
[`soong.variables`](http://cs.android.com/android/platform/superproject/+/master:build/make/core/soong_config.mk),
a JSON file
Throughout Soong, environment variables can be accessed to alter the build via
the `Config`:
* [`GetEnv`](http://cs.android.com/search?q=f:soong%20%5C.GetEnv%5C%28%20-f:%2Fui%2F%20-f:%2Fcmd%2F&sq=)
* [`GetEnvWithDefault`](http://cs.android.com/search?q=f:soong%20%5C.GetEnvWithDefault%5C%28%20-f:%2Fui%2F%20-f:%2Fcmd%2F&sq=)
* [`IsEnvTrue`](http://cs.android.com/search?q=f:soong%20%5C.IsEnvTrue%5C%28%20-f:%2Fui%2F%20-f:%2Fcmd%2F&sq=)
* [`IsEnvFalse`](http://cs.android.com/search?q=f:soong%20%5C.IsEnvFalse%5C%28%20-f:%2Fui%2F%20-f:%2Fcmd%2F&sq=)
Soong
[loads the `soong.variables`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/config.go;l=174;drc=b078ade28d94c85cec78e9776eb31948a5647070)
config file, stored as
[`productVariables`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/variable.go;l=163;drc=16e77a9b303a71018eb6630f12f1414cd6ad615c).
These variables are used in three ways:
* Direct access from `Config`, for example: paths can be
[opted out](https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/sanitize.go;l=364,371,393;drc=582fc2d1dde6c70687e6a0bea192f2a2ef67bbd5)
of specific sanitizers
* In limited cases, users can use these within their `Android.bp` file to
control what is built or perform variable replacement.
[`variableProperties`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/variable.go;l=38;drc=16e77a9b303a71018eb6630f12f1414cd6ad615c)
limits which configuration variables can be specified within an `Android.bp`
file and which properties they can apply to. The values specified within an
`Android.bp` file, are merged/replaced by the
[`VariableMutator`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/variable.go;l=539;drc=16e77a9b303a71018eb6630f12f1414cd6ad615c),
which appends performs string replacement if requested and merges the
properties into the modules.
* Through
[Soong Config Variables](https://android.googlesource.com/platform/build/soong/+/refs/heads/master/README.md#soong-config-variables):
which allow users to specify additional configuration variables that can be
used within an `Android.bp` file for the module type and properties they
request. Soong config variable structs are
[dynamically generated](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/soongconfig/modules.go;l=257;drc=997f27aa0353dabf76d063d78ee5d4495da85651)
via reflection. In the
[factory](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/soong_config_modules.go;l=423;drc=18fd09998223d004a926b02938e4cb588e4cc934),
the properties to merge into the module instance are
[identified](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/soongconfig/modules.go;l=416;drc=997f27aa0353dabf76d063d78ee5d4495da85651)
based on the config variable's type.
The product configuration also provides information about architecture and
operating system, both for target(s) and host. This is used within the
[`archMutator`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/arch.go;l=569-597;drc=135bf55281d79576f33469ce4f9abc517a614af5)
to split a module into the required variants and merge target-specific
properties into the appropriate variant. Only properties which have been tagged
with `android:"arch_variant"` can be specified within an `Android.bp` as
arch/os/target-specific. For example:
```go
type properties struct {
// this property will be arch-variant
Arch_variant_not_nested *string `android:"arch_variant"`
Nested_with_arch_variant struct {
// this property is arch-variant
Arch_variant_nested *string `android:"arch_variant"`
// this property is **not** arch-variant
Not_arch_variant_nested *string
} `android:"arch_variant"`
Nested_no_arch_variant struct {
// this property is **NOT** arch-variant
No_arch_variant_nested_not_arch_variant *string `android:"arch_variant"`
// this property is **not** arch-variant
No_arch_variant_nested *string
}
}
```
The arch/os/target-specific structs are
[dynamically generated](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/arch.go;l=780-787;drc=135bf55281d79576f33469ce4f9abc517a614af5)
based on the tags using reflection.
#### Bazel
Bazel documentation covers configurable builds fairly extensively, so this is a
short overview that primarily links to existing Bazel documentation rather than
repeating it here.
[Configurable attributes](https://docs.bazel.build/versions/main/configurable-attributes.html),
(aka `select()`) allows users to toggle values of build rule attributes on the
command line.
Within a `rule`, the value of a `select` will have been resolved based on the
configuration at analysis phase. However, within a macro (at loading phase,
before analysis phase), a `select()` is an opaque type that cannot be inspected.
This restricts what operations are possible on the arguments passed to a macro.
The conditions within a `select` statement are one of:
* [`config_setting`](https://docs.bazel.build/versions/main/be/general.html#config_setting)
* [`constraint_value`](https://docs.bazel.build/versions/main/be/platform.html#constraint_value)
A `config_setting` is a collection of build settings, whether defined by Bazel,
or user-defined.
User-defined
[build settings](https://docs.bazel.build/versions/main/skylark/config.html#defining-build-settings)
allow users to specify additional configuration, which *optionally* can be
specified as a flag. In addition to specifying build settings within a
`config_setting`, rules can depend directly on them.
In addition, Bazel supports
[`platform`s](https://docs.bazel.build/versions/main/be/platform.html#platform),
which is a named collection of constraints. Both a target and host platform can
be specified on the command line.
[More about platforms](https://docs.bazel.build/versions/main/platforms.html).
## Communicating between modules/targets
### Soong communication
There are many mechanisms to communicate between Soong modules. Because of this,
it can be difficult to trace the information communicated between modules.
#### Dependency Tags {#deptags}
Dependency tags are the primary way to filter module dependencies by what
purpose the dependency serves. For example, to filter for annotation processor
plugins in the deps of a Java library module, use `ctx.VisitDirectDeps` and
check the tags:
```
ctx.VisitDirectDeps(func(module android.Module) {
tag := ctx.OtherModuleDependencyTag(module)
if tag == pluginTag { patchPaths += ":" + strings.Split(ctx.OtherModuleDir(module), "/")[0] }
}
)
```
At this point the module managing the dependency, may have enough information to
cast it to a specific type or interface and perform more specific operations.
For instance, shared libraries and executables have
[special handling](http://cs.android.com/android/platform/superproject/+/master:build/soong/cc/cc.go;l=2771-2776;drc=5df7bd33f7b64e2b880856e3193419697a8fb693)
for static library dependencies: where the coverage files and source based ABI
dump files are needed explicitly. Based on the dependency tag, the module is
cast to a concrete type, like `cc.Module`, where internal fields are accessed
and used to obtain the desired data.
Usage of dependency tags can be more evident when used between module types
representing different langauges, as the functions must be exported in Go due to
Soong's language-based package layout. For example, rust uses `cc` module's
[`HasStubVariants`](http://cs.android.com/android/platform/superproject/+/master:build/soong/rust/rust.go;l=1457-1458;drc=9f59e8db270f58a3f2e4fe5bc041f84363a5877e).
#### Interfaces
A common mechanism for a module to communicate information about itself is to
define or implement a Go interface.
Some interfaces are common throughout Soong:
* [`SourceFileProducer`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=2967;drc=8707cd74bf083fe4a31e5f5aa5e74bd2a47e9e58),
by implementing `Srcs() Paths`
* [`OutputFileProducer`](http://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=2974;drc=8707cd74bf083fe4a31e5f5aa5e74bd2a47e9e58)
by implementing `OutputFiles(string) (Paths, error)`
* [`HostToolProvider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=3032;drc=8707cd74bf083fe4a31e5f5aa5e74bd2a47e9e58)
by implementing `HostToolPath() OptionalPath`
`SourceFileProducer` and `OutputFileProducer` are used to resolve references to
other modules via `android:"path"` references.
Modules may define additional interfaces. For example, `genrule` defines a
[`SourceFileGenerator` interface](http://cs.android.com/android/platform/superproject/+/master:build/soong/genrule/genrule.go;l=98-102;drc=2ada09a5463a0108d713773679c5ba2c35450fa4).
#### Providers
Soong has Bazel-inspired providers, but providers are not used in all cases yet.
Usages of providers are the easiest, simplest, and cleanest communication
approach in Soong.
In the module providing information, these are specified via
[`SetProvider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=212;drc=5a34ffb350fb295780e5c373fd1c78430fa4e3ed)
and
[`SetVariationProvider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/mutator.go;l=719;drc=5a34ffb350fb295780e5c373fd1c78430fa4e3ed).
In the module retrieving information,
[`HasProvider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=205-206;drc=8631cc7327919845c9d9037188cbd483d22ba077)
and
[`Provider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=198-203;drc=8631cc7327919845c9d9037188cbd483d22ba077)
or
[`OtherModuleHasProvider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=195-196;drc=8631cc7327919845c9d9037188cbd483d22ba077)
and
[`OtherModuleProvider`](https://cs.android.com/android/platform/superproject/+/master:build/soong/android/module.go;l=189-193;drc=8631cc7327919845c9d9037188cbd483d22ba077)
are used to test existence and retrieve a provider.
### Bazel communication
Targets primarily communicate with each other via providers in Bazel rule
implementations. All rules have access to any of the providers but rules will
pick and choose which ones to access based on their needs. For example, all
rules can access `JavaInfo` provider, which provides information about compile
and rolled-up runtime jars for javac and java invocations downstream. However,
the `JavaInfo` provider is only useful to `java_*` rules or rules that need jvm
information.
#### Starlark rules
[Providers](https://docs.bazel.build/versions/main/skylark/rules.html#providers)
are pieces of information exposed to other modules.
One such provider is `DefaultInfo`, which contains the default output files and
[`runfiles`](https://docs.bazel.build/versions/main/skylark/rules.html#runfiles).
Rule authors can also create
[custom providers](https://docs.bazel.build/versions/main/skylark/lib/Provider.html#modules.Provider)
or implement existing providers to communicate information specific to their
rule logic. For instance, in Android Starlark
[`cc_object`](http://cs/android/build/bazel/rules/cc_object.bzl?l=86-87&rcl=42607e831f8ff73c82825b663609cafb777c18e1)
rule implementation, we return a
[`CcInfo`](https://docs.bazel.build/versions/main/skylark/lib/CcInfo.html)
provider and a custom
[`CcObjectInfo`](http://cs/android/build/bazel/rules/cc_object.bzl?l=17-21&rcl=42607e831f8ff73c82825b663609cafb777c18e1)
provider.
#### Native rules
For implementation of native rules in Java,
[`ruleContext.getPrerequisite`](https://github.com/bazelbuild/bazel/blob/a20b32690a71caf712d1d241f01fef16649562ba/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java#L911-L983)
is used to extract providers from dependencies.
#### `depset` construction
[`depset`](https://docs.bazel.build/versions/main/glossary.html#depset) are used
in conjunction with providers to accumulate data efficiently from transitive
dependencies. used to accumulate data from transitive dependencies.
#### `exports`
Some target have an `exports` attribute by convention, like
[`java_library.exports`](https://docs.bazel.build/versions/main/be/java.html#java_import.exports).
This attribute is commonly used to propagate transitive dependencies to the
dependent as though the dependent has a direct edge to the transitive
dependencies.

View File

@ -0,0 +1,20 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="21" />
<application android:label="Test App" >
<activity
android:name="com.app.MainActivity"
android:label="App" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,62 @@
load("//build/bazel/rules/android:android_binary.bzl", "android_binary")
load("//build/bazel/rules/cc:cc_library_static.bzl", "cc_library_static")
load("//build/bazel/rules/cc:cc_library_shared.bzl", "cc_library_shared")
load("@rules_android//rules:rules.bzl", "android_library")
android_binary(
name = "app",
manifest = "AndroidManifest.xml",
deps = [
":applib",
],
)
android_binary(
name = "app-cert-string",
certificate_name = "platform",
manifest = "AndroidManifest.xml",
deps = [
":applib",
],
)
android_binary(
name = "app-cert-module",
certificate = "//build/make/target/product/security:aosp-testkey",
manifest = "AndroidManifest.xml",
deps = [
":applib",
],
)
android_library(
name = "applib",
srcs = [
"Jni.java",
"MainActivity.java",
],
manifest = "AndroidManifest.xml",
resource_files = glob(["res/**"]),
deps = [
":jni",
":lib",
],
)
android_library(
name = "lib",
srcs = ["Lib.java"],
)
cc_library_shared(
name = "jni",
srcs = ["jni.cc"],
deps = [":jni_dep"],
)
cc_library_static(
name = "jni_dep",
srcs = ["jni_dep.cc"],
hdrs = ["jni_dep.h"],
deps = ["//libnativehelper:jni_headers"],
)

View File

@ -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.
*/
package com.app;
public class Jni {
public static native String hello();
}

View File

@ -0,0 +1,23 @@
/*
* 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 com.app;
public class Lib {
public static String message() {
return "Hello Lib";
}
}

View File

@ -0,0 +1,42 @@
/*
* 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 com.app;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.view.View;
import android.util.Log;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
Log.i("tag", Lib.message());
final TextView textView = findViewById(R.id.text);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
textView.setText("Button clicked");
}
});
}
}

View File

@ -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.
*/
#include <jni.h>
#include <string>
#include "build/bazel/examples/android_app/java/com/app/jni_dep.h"
extern "C" JNIEXPORT jstring JNICALL
Java_com_app_Jni_hello(JNIEnv *env, jclass clazz) {
std::string hello = "Hello";
std::string jni = "JNI";
return NewStringLatin1(env, (hello + " " + jni).c_str());
}

View File

@ -0,0 +1,33 @@
/*
* 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.
*/
#include "build/bazel/examples/android_app/java/com/app/jni_dep.h"
#include <stdlib.h>
#include <string.h>
jstring NewStringLatin1(JNIEnv *env, const char *str) {
int len = strlen(str);
jchar *str1;
str1 = reinterpret_cast<jchar *>(malloc(len * sizeof(jchar)));
for (int i = 0; i < len; i++) {
str1[i] = (unsigned char)str[i];
}
jstring result = env->NewString(str1, len);
free(str1);
return result;
}

View File

@ -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.
*/
#pragma once
#include <jni.h>
jstring NewStringLatin1(JNIEnv *env, const char *str);

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a TextView" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>

View File

@ -0,0 +1,133 @@
// 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.
// This is a minimal apex that contains no files.
// Build with `m build.bazel.examples.apex.minimal`.
//
// Generated by system/apex/tools/create_apex_skeleton.sh.
// WARNING: These keys are for test and dev purposes only.
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
apex_key {
name: "build.bazel.examples.apex.minimal.key",
public_key: "build.bazel.examples.apex.minimal.avbpubkey",
private_key: "build.bazel.examples.apex.minimal.pem",
}
android_app_certificate {
name: "build.bazel.examples.apex.minimal.certificate",
certificate: "build.bazel.examples.apex.minimal",
}
filegroup {
name: "build.bazel.examples.apex.minimal-file_contexts",
srcs: [
"file_contexts",
],
}
cc_library {
name: "build.bazel.examples.apex.minimal_dummy_cc_lib",
srcs: ["dummy_cc_lib.cc"],
apex_available: [
"build.bazel.examples.apex.minimal",
"build.bazel.examples.apex.minimal_compressed",
],
// Because the APEX sets this
product_specific: true,
// Because the APEX sets this
min_sdk_version: "30",
}
prebuilt_etc {
name: "build.bazel.examples.apex.minimal_dummy_named_prebuilt_etc",
src: "dummy_prebuilt_etc_data_1",
filename: "dummy_prebuilt_etc_data_1_renamed",
sub_dir: "dummy_sub_dir",
}
prebuilt_etc {
name: "build.bazel.examples.apex.minimal_dummy_unnamed_prebuilt_etc",
src: "dummy_prebuilt_etc_data_2",
sub_dir: "dummy_sub_dir",
}
prebuilt_etc {
name: "build.bazel.examples.apex.minimal_dummy_prebuilt_etc_without_subdir",
src: "dummy_prebuilt_etc_data_3",
}
cc_binary {
name: "build.bazel.examples.apex.cc_binary",
srcs: ["main.cc"],
apex_available: [
"build.bazel.examples.apex.minimal",
"build.bazel.examples.apex.minimal_compressed"
],
// Because the APEX sets these
product_specific: true,
min_sdk_version: "30",
}
apex_defaults {
name: "build.bazel.examples.apex.minimal_defaults",
manifest: "manifest.json",
file_contexts: ":build.bazel.examples.apex.minimal-file_contexts",
// So that we aren't considered a "platform APEX" and can use a file_context that lives outside of system/sepolicy/apex
product_specific: true,
key: "build.bazel.examples.apex.minimal.key",
min_sdk_version: "30",
native_shared_libs: [
"build.bazel.examples.apex.minimal_dummy_cc_lib",
],
prebuilts: [
"build.bazel.examples.apex.minimal_dummy_named_prebuilt_etc",
"build.bazel.examples.apex.minimal_dummy_unnamed_prebuilt_etc",
"build.bazel.examples.apex.minimal_dummy_prebuilt_etc_without_subdir",
],
binaries: [
"build.bazel.examples.apex.cc_binary",
],
certificate: ":build.bazel.examples.apex.minimal.certificate",
}
apex {
name: "build.bazel.examples.apex.minimal",
defaults: [
"build.bazel.examples.apex.minimal_defaults",
]
}
apex {
name: "build.bazel.examples.apex.minimal_compressed",
compressible: true,
defaults: [
"build.bazel.examples.apex.minimal_defaults",
]
}

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEA3kVUTZJFAc0jOjcBQeikubCeYb6AGWzowgGurkwkX7zGY0rN
s30r8vTuFtmQreU1vjp3e5mOTas7TE9A6BevTSZGgaZEHPujolgf0hfPaCLlZw3p
BwEwE+lQppbRV/PEWLWM5suHUtFgAmch7o3dXO+5Kxy3T1wdivUT7rgIsWZZLwt7
JNtTkm0YucmSBsN3QLhbevqb8msZrt8GgvJyd2jbGlTsP4j0R0DgSehkWwNFsxzn
abdT1Uk+uwEY0p3uHhlLxkIDoOV0LmKmrwTmfEDJFMqlhdJNWfZ80sYG+vYcb1YF
+5YwqnGiZwPUb4ARzdXwg7kjZVnKegi9tuTbVdkCT3MbS5r/uLQ1/SyD1aCg0iHS
pltJhqa5VddvuvNjuWgjDrvPJlub8DMfn3YSX6qaPSEBqDCDhbERzpAIGhAV8rUL
VbSS1E5F+YNvfNwGXVxd9u4D6HqS0sxA30RERYwYiBl8f8g7n9Kyw/pKNQ+ii5q7
MtXcN7mBUPUwj4NpFnMyXjPWje1r2T0ZdwxXnU+hw1Kzgp5o/r5WJsh/GhJq52wt
WcMR3AnNf4IB1UAVKhlY9/6zHLqNDH/dRnuYkhLvUqheh/rVrdbwPnPhbSnxDE4D
WGXVM/dwoB5cqt2ZOQnIm4Ro4R5NR2mJ354Mene51pTx5qenZUKvo/UUC3UCAwEA
AQKCAgEAyezOA5LIYjSMtkxWNhw12gQcPswj7/VDF00T9oBx4w/KY1YHvfIPRS9C
RTvt9izvQBw5g+4im2jd/Btb6f3qYfpNv2bfJj1tkQTiE6lR4VcidRbsBlML7Grf
vBfzoyVv0O9OTDXGgHR59nTfHKuA2Pdnj0UNO2mB5UV0kEBclV1X1Cdn/jnKmJHI
DrCSmtZktkVFxll55ydpM7alYb0ERHrb4fYYkjfHRBwqJrqawRwd6/RsP1wvKurZ
tGXwuRaExo9QiZJbXYZWn/U7XHDlOyhYBS18ZjCB2SLWj10b5k5D0tmhocf1+kI1
ucR+77S3d/nOALzm7SI/kdHzF/6dSpQEptNeYaBxfNnDe+tjTseuINN0L+q/gU/j
tt2+HtAKzEwNsZTbbU6CHkMXIndiKzMRNtQQvqL0st+5EY3LNzAhq81xPpIEk+lR
3/lh1utDExr5APIzGzreNTthmZ/mUBtb412retgtvqZenUqmOw6bE/HsesuvNcT5
kwPgwv6pXnuVUJpofBmq3iH9q1oNfNapLWMVSzsqYncMG0rJx98WcZzcuXFrpYic
fnDLJ7f38PguX5IUTAVj4vxbAmPizDz4pByW6pwKtHegSHf35Zj7dZovV7dsjHg5
KKOpEi86bo4jBlGtAQ41/mZi38yrnd2WZlYHb542gimDWPJb3FkCggEBAP5mqdJw
NCDDCKlp/TMiP1MfgyLIgy4AELuU8AV6pSxYSd6bJZmEe6IWxgrzmBn4HdtL1W4B
asSjRTKeTxcStB3Kang5OAYWFP/IoWprNXcNWdHZmfLXcK4kU4JoXxGZawsBwGip
2ZlXdvWcAc/FABVGOoj0SHDT6/9GCXBw5+6u9aQLJ05E75hd3t6kMZAXaKfL1di+
EzGhkT7bTIWDBfAGWVfAv9fbWki3/Tr4S3TCRZdDry3oLtum9f9dvdRch3Zmx3WS
wKCVsD5cAb1HMc1wc6ftb0AHRaeC/Ff5mBjXASPf6FrcJoqn0xvIwrjO+haUlsJa
+evcIY+ZzEzMGscCggEBAN+q976fvGwCNIV+aocE5FBJlwTvPaAGTnUx54VZLD9Y
b0IlZVOHQjS+33kl35WqTAm2byziWn05YN6Twgmz8V18GBg72Ow6JOKKV1F/1f77
aVnX1uIY4XGzQAyUNRmmzZkFhINPdfeKwVDij4bJdnxZz9TC6emRkqFFF2xygRm3
sIeFEbJAKU8VFQPqDl89rHAnS99ey6uZsmiaRh0Fiz4zz0sMD7b95FRrhF7ISF0w
2f+75tsud/JIVg00O6HAY1NN7uNBHUrVe6WUDmC5zX1dgfKOrLTs8y9SBMHUmvDw
xMYBp16LBeC2AX1QH2H4eqw8Un2KfcZsKREQNS3AS+MCggEBAJ3JfzsObMaFMTIi
kvDMJQqhWOySAQre74Ho9pXvY9MFT4vKHqABE82M4niYbZZt1dbWSiJYrqgXvCuR
FOzypNR1X2QB2UWtHIkpHzSqGhclKLiHhnygMztPPJx9r7lytnm1NGm2L0h0f0wG
3vjG7y5CyLt5CBUy3AUQo3oiDTh1O9XrNQ3Oo+yVDE56+GKuojKwsookGjOGSlf6
HJQSl/Qve+p5moN/gZfoxh91MRfSBuezC0wl3ipOe/VPZxX37ez6AmxiASeYsB99
YXbMWY9aV+Gn2oCUAhfLnkfSfwupDwP5NFxmf2EwhjeNZXjKp0KqFnelha1Mc2Rl
t9lKHrECggEAINhieUPLz7CjNddSnXFCdefYnYjka9OBQgSKEzL9JHBAJl6HerSq
LES7XHcLVlcjw2le+iBjWXBLoWPhYrth/nByuVxQhqAjOoFGQVmce01AM0lW7Ozq
bl/8T9yTz6iHGKmOxBmvISLnMJGUsh5zn98wffYBk6Mk/TnALtTB0J0A92W7K4nw
hwR5iZGdeq7725xpr+ujzRdM879z/F8MC55gGo0y4ZR3K05+qcyPk9/Qo5VcWWOB
ibrFqDq7Zw1xiJIxOn4dZxfiiHYthlbCrl5E7vG0uvzw2UoaGLy35Bn3S3yCr4eK
3Wzc2yIg+aobmZ9iHb1wqurHPRI8PPO4MQKCAQBU7kJwRhJ1BUd0mG59aJSIwWOn
PBl7oMBwGGrKp7/lquK+2VAR/Q0uFnGtYkjFg4n2kzi72F1IkLS02BTo5QR5Jwfr
oJmH9ZldB+StNuqA4Zhg53dSW0WSkVN/xBGIWBY99b+yJi0NONrIL11xbBwPTMMp
3CI0kfNnl9iWuNq/7q0eilf1S2P+L4uaE+N5fmfS2VVRTV1ChiSZcZEHBKBp32MC
e04kkElHzDGaK5Jsi6AlPppH2TYsewmbMwseBUTH/pst0HJK2pTLpbsNQKA7e2v9
4O3DzvgWLrXNqdvwSnd4efahsm+aoE9eyj3rIMj1JKsvAtc5WEmXhEke22dX
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF5TCCA80CFG12QQebWMaR+Kj0TNs0Y7VzDDVEMA0GCSqGSIb3DQEBCwUAMIGt
MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEpMCcGA1UEAwwgYnVp
bGQuYmF6ZWwuZXhhbXBsZXMubWluaW1hbGFwZXgwIBcNMjEwNzE2MDU1MTExWhgP
NDc1OTA2MTIwNTUxMTFaMIGtMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZv
cm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQ
MA4GA1UECwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lk
LmNvbTEpMCcGA1UEAwwgYnVpbGQuYmF6ZWwuZXhhbXBsZXMubWluaW1hbGFwZXgw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCu0WkwooTpX9wzJoM79KjD
ZNj1tl74H/v2Iac/j4QYZiYBtcZelPCHISY5CWYPjkA5HY3dka6zjMeh7gUjRLre
7pYiFWwdeQmv6sAO3q0bYEK4+WqLdiYQR9tV8bkSPWy+3DUqKkHEfbNCPip0tlYw
h4rnZ0b5YZPGRLHsFtX1SS+RRtlg91eiAgpWdhhL4fELJUv2jD2o/DKYZI+4fwwx
P/sgoiS18IAxiHvN6a3AcC70YvWape+GhVtptlYBaFNNQ7jmVLXsXIyCMrfFypbT
jopjMhuk/pnazA/3+eK1Yay3OPtPM9NGE2VVBvHDqwCeQT0EIe8hIsrHIUVy067q
/byNk3hVM3tK9z8/OPYRDlLyzxshfdt0Q0JpkTdzXXBT2t5i0bow6+9e1rJyw7xM
zRczYxfmyKti4KZ9ZrykENW8P2vYQNj10ZANvC1WGM8nFSet5I61xnxuQMy4wZwz
4a3yz3PPdD1iFxjw0r57z1/8k9qSsRCVXipLOo7ZxweoGyyEgUuTcvrel6fAcvct
V+m0oBNLWG6+uAViPKWdNr0Wl0odamjj2IOowaw5/1QHWGADpXGob6Dy7k0fVL8Z
U/3VeNA/e293MHM9M7eWRxrMduUGCDYicyTUsHKPgFVpk5A9kHe4b6iQb/6fztyy
IZ5lz/jGQOITXe0HE4CRywIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQA9gnyIrSW3
2/oOfApLdPcKLXxOKF0MGykV4OQjWJTCwIOojmPVmFfd2nDFdAy6yW5kSVas0Om0
CABQqMHqfayCzgECgNkzdNRwwczKgPMBijt+SpSqpA9ma9KTTyWI20vZfXj/5d4G
3cZXCJ20hYP5eCjz2JoqEKuHvzcf4k4U6hPOyGHw7Zj8XQuoHHUJzpWZzn6/8Qh5
ESAmQ6JJWT2JrpKVvHcaO+SNQmox1+s0+4e3L6WqtoMAHkaKSNGq/8/VH1A+0qg8
BQGTycPQb0lhV3laDxkKVnBNC5tB1qi+i8mHxQnTGh7GsZWgvFwyPMdqchJa/c69
oZX06Ip99UZSQpMCh6GOxs7KoD7idN9mmsKtE7ycu6mYr4/tEGbAv0/rBiZvxIXU
K22GfQlGKpFRd2rxOcguRj+KavdM8N+zokT9i+k+w0xJQWnHWp6faw/oYBkahAkr
GKh60mALHcU6L7SGM3TAJV4Xsy+wy6KwhkJZVFgMGKsdcx9aj7tSwmgW8RMiYrpQ
B6j8s4jxmRQn0yFBkmjRr9dgWFQh8I2hoMt+Wu4AuIH6Ui4PvE7gcV0h2ws+QMsA
+64a702ESExURkhtamWlxiKbnmy4rcDycdE1rb2XnZM1hic1R0PYmZV9jZo1tPk+
YAqGsO9wGSNNs9Lej85K8DzErmYEVQZuTQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,9 @@
int main(int argc, char** argv) {
// Unused
(void)argc;
// Unused
(void)argv;
return 0;
}

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
2

View File

@ -0,0 +1 @@
3

View File

@ -0,0 +1,5 @@
/bin/apex_test_preInstallHook u:object_r:apex_test_prepostinstall_exec:s0
/bin/apex_test_postInstallHook u:object_r:apex_test_prepostinstall_exec:s0
/bin/surfaceflinger u:object_r:surfaceflinger_exec:s0
/lib(64)?(/.*)? u:object_r:system_lib_file:s0
(/.*)? u:object_r:system_file:s0

View File

@ -0,0 +1 @@
int main() { return 0; }

View File

@ -0,0 +1,4 @@
{
"name": "build.bazel.examples.apex.minimal",
"version": 1
}

View File

@ -0,0 +1,11 @@
java_binary(
name = "hello_java",
srcs = ["example/HelloWorld.java"],
main_class = "com.bazel.example.HelloWorld",
deps = [":hello_java_lib"],
)
java_library(
name = "hello_java_lib",
srcs = ["example_lib/HelloLib.java"],
)

View File

@ -0,0 +1,10 @@
package com.bazel.example;
import com.bazel.example_lib.HelloLib;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world!");
System.out.println("Library says: " + HelloLib.libValue());
}
}

View File

@ -0,0 +1,7 @@
package com.bazel.example_lib;
public class HelloLib {
public static String libValue() {
return "Hello Library!";
}
}

View File

@ -0,0 +1,37 @@
# Examples of Bazel queries in the QueryView
**Warning**: This feature is undergoing active development and the generated
Bazel BUILD files and other user-facing APIs will change without warning. We
recommend to use this feature for learning, exploration, information gathering
and debugging purposes only.
## Usage
Setup your AOSP product config and create the queryview directory:
```
source build/envsetup.sh
lunch aosp_arm # or your preferred target
m queryview
```
Then, run `bazel query` with the `queryview` config, and `--query_file` pointing
to the file containing the query expression to run for the current product
configuration:
```
bazel query --config=queryview --query_file=build/bazel/examples/queries/android_app.txt
```
Alternatively, you can also write the expression directly in the command:
```
bazel query --config=queryview 'kind("android_app rule", //...)'
```
## Examples
* `android_apps`: lists all `android_app` modules for the current product configuration
* `nocrt`: modules with `nocrt: True`
* `apex_available`: modules with `//apex_available:platform` in the `apex_available` list property
* `libc_variant`: all variants for `libc`

View File

@ -0,0 +1 @@
kind("android_app rule", //...)

View File

@ -0,0 +1 @@
attr(apex_available, //apex_available:platform, //...)

View File

@ -0,0 +1 @@
attr(module_name, \Alibc$, //bionic/libc:all)

View File

@ -0,0 +1 @@
attr(nocrt, 1, //...)

View File

@ -0,0 +1,30 @@
#!/bin/bash
#
# 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.
set -euo pipefail
function run_query() {
local f=$1; shift;
bazel query --config=queryview --query_file="${f}"
}
function run_all_queries() {
for f in $(ls -1 | grep .txt); do
run_query "${f}"
done
}
run_all_queries

View File

@ -0,0 +1,101 @@
// Use local copy of the soong_config_module_type dep graph to keep this Android.bp
// self-contained.
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
soong_config_string_variable {
name: "fake_library_linking_strategy",
values: [
"prefer_static",
],
}
soong_config_module_type {
name: "fake_library_linking_strategy_cc_defaults",
module_type: "cc_defaults",
config_namespace: "bp2build",
variables: ["fake_library_linking_strategy"],
properties: [
"shared_libs",
"static_libs",
],
}
// Fake cc_library modules for testing
cc_library {
name: "bp2build_foo",
srcs: ["main.cpp"],
}
cc_library {
name: "bp2build_bar",
srcs: ["main.cpp"],
}
cc_library {
name: "bp2build_baz",
srcs: ["main.cpp"],
}
cc_library {
name: "bp2build_qux",
srcs: ["main.cpp"],
}
cc_library {
name: "bp2build_quux",
srcs: ["main.cpp"],
}
fake_library_linking_strategy_cc_defaults {
name: "fake_libadbd_binary_dependencies",
static_libs: [
"bp2build_foo",
],
shared_libs: [
"bp2build_bar",
],
soong_config_variables:{
fake_library_linking_strategy: {
prefer_static: {
static_libs: [
"bp2build_baz",
],
},
conditions_default: {
shared_libs: [
"bp2build_qux",
],
},
},
},
target: {
android: {
shared_libs: ["bp2build_quux"],
},
linux_glibc: {
enabled: false,
},
linux_musl: {
enabled: false,
},
linux_bionic: {
enabled: false,
},
},
}
// Experimental "stub" adbd for bp2build development
cc_binary {
name: "bp2build_adbd",
defaults: ["adbd_defaults", "host_adbd_supported", "fake_libadbd_binary_dependencies"],
srcs: [
"main.cpp",
],
use_version_lib: false,
}

View File

@ -0,0 +1,17 @@
/*
* 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.
*/
int main() {return 0; }

View File

@ -0,0 +1,39 @@
# JSON module graph queries
This directory contains `jq` scripts that query Soong's module graph.
`jq` may be installed through your distribution's repository.
Usage:
```
m json-module-graph
query.sh [-C] <command> <base-of-your-tree>/out/soong/module-graph.json [argument]
```
The following commands are available:
* `directDeps` prints the names of the direct dependencies of the given module
* `distanceFromLeaves` prints the longest distance each module has from a leaf
in the module graph within the transitive closure of given module
* `filterSubtree` dumps only those modules that are in the given subtree of the
source tree
* `fullTransitiveDeps` returns the full transitive dependencies of the given
module
* `moduleTypeStats`: returns of a summary of the module types present on the
input
* `modulesOfType`: returns the names of modules of the input type
* `printModule` prints all variations of a given module
* `printModule`: returns a slightly more consise view of the input module
* `properties`: returns the properties set in the input module, includes
properties set via defaults
* `transitiveDeps` prints the names of the transitive dependencies of the given
module
* `usedVariations` returns a map that shows which variations are used in the
input and what values they take
* `variantTransitions` summarizes the variant transitions in the transitive
closure of the given module
* `fullTransitiveDepsProperties` returns the properties set (including via
defaults) grouped by module type of the modules in the transitive closure of
the given module
It's best to filter the full module graph to the part you are interested in
because `jq` isn't too fast on the full graph.

View File

@ -0,0 +1,5 @@
# CMD: Returns the names of the direct dependencies of the module named $arg
include "library";
[.[] | select(.Name == $arg) | .Deps | map(.Name)] | flatten | unique | sort

View File

@ -0,0 +1,56 @@
# CMD: Returns the maximum distance from a leaf for each module
include "library";
def onlyDeps:
{ Name: .Name, Deps: .Deps | map(.Name) }
;
def mergeDepsForSameModule:
group_by(.Name) | map({Name: .[0].Name, Deps: map(.Deps) | flatten | unique | sort})
;
def toMergeMap:
map({key: .Name, value: .Deps}) | from_entries
;
def moduleGraphNoVariants:
map(onlyDeps) | mergeDepsForSameModule | toMergeMap
;
def removeSelfEdges:
to_entries |
map(.key as $key | {key: .key, value: .value | [.[] | select(. != $key)]}) |
from_entries
;
def nextDepths($m):
. as $old |
to_entries |
map({
key: .key,
value: (
.key as $key |
$m[.key] // [] |
map($old[.]) |
if any(. == -1) then -1 else (max // -1) + 1 end
)
}) |
from_entries
;
def maxDepths($m):
map({key: ., value: -1}) | from_entries |
{Prev: [], Next: .} |
until (.Prev == .Next; {Prev: .Next, Next: .Next | nextDepths($m)}) |
.Next
;
def variantlessDistancesFromLeaves($root):
(moduleGraphNoVariants | removeSelfEdges) as $m |
[$root] |
transitiveDeps($m) |
maxDepths($m)
;
variantlessDistancesFromLeaves($arg)

View File

@ -0,0 +1,11 @@
# CMD: Returns modules defined under the directory $arg
include "library";
def isBlueprint($p): .Blueprint | index($p) != null
;
def isBlueprintPrefix($p): .Blueprint | startswith($p)
;
[.[] | select(isBlueprintPrefix($arg))]

View File

@ -0,0 +1,61 @@
# CMD: Finds all modules whose input files cross package boundaries.
include "library";
def getBlueprintDirPaths:
[.[] | .Blueprint | getDirPath] | sort_by(.) | unique | map({(.):""}) | add
;
def getNonNullActionModules:
[.[] | select(nonNullAction)]
;
def getOutputsOfModule:
[.Module.Actions | .[] | .Outputs | if . == null then [] else . end | .[]]
;
def getOutputsOfModules($nonNullActionModules):
$nonNullActionModules | map({(.Name):getOutputsOfModule}) | add
;
def getDepOutputs($outputsOfModules):
. as $depName |
if in($outputsOfModules) then ($outputsOfModules | ."\($depName)")
else [] end | .[]
;
def getDepOutputsOfModule($outputsOfModules):
[.Deps | .[] | .Name | getDepOutputs($outputsOfModules)]
| map({(.):""}) | add
;
def isDirPathMatch($blueprintDirPath; $allBlueprintDirPaths):
def _isDirPathMatch($blueprintDirPath; $allBlueprintDirPaths):
# True if there's a Blueprint file in the path and the path isn't
# equal to $blueprintDirPath of the module.
if in($allBlueprintDirPaths) and . != $blueprintDirPath then true
# Stops checking if the current path is already the $blueprintDirPath.
elif . == $blueprintDirPath then false
# Usually it should not hit this logic as it stops when the path is
# equal to $blueprintDirPath.
elif (contains("/") | not) then false
else (getDirPath | _isDirPathMatch($blueprintDirPath; $allBlueprintDirPaths))
end
;
_isDirPathMatch($blueprintDirPath; $allBlueprintDirPaths)
;
def isActionInputMatch($outputsOfModules; $allBlueprintDirPaths):
. as $moduleVariant | .Blueprint | getDirPath as $blueprintDirPath |
$moduleVariant | getDepOutputsOfModule($outputsOfModules) as $depOutputs |
$moduleVariant | getActionInputs | select(in($depOutputs) | not) |
select(startswith($blueprintDirPath)) | getDirPath |
isDirPathMatch($blueprintDirPath; $allBlueprintDirPaths)
;
getBlueprintDirPaths as $allBlueprintDirPaths |
getNonNullActionModules as $nonNullActionModules |
getOutputsOfModules($nonNullActionModules) as $outputsOfModules |
[$nonNullActionModules | .[] |
select(isActionInputMatch($outputsOfModules; $allBlueprintDirPaths)) |
.Name] | sort_by(.) | unique

View File

@ -0,0 +1,16 @@
# CMD: Finds all modules whose name is equal to the name of one of its input
# files.
include "library";
def isActionInputMatch($name; $blueprintDirPath): . as $actionInput |
getDirPath as $inputDirPath | $actionInput | split("/") |
last | . == $name and $inputDirPath == $blueprintDirPath
;
def isActionInputsMatch($name; $blueprint): getActionInputs as $actionInputs |
$blueprint | getDirPath as $blueprintDirPath | $actionInputs |
isActionInputMatch($name; $blueprintDirPath)
;
[.[] | select(nonNullAction) | select(isActionInputsMatch(.Name; .Blueprint)) | .Name] | sort_by(.) | unique

View File

@ -0,0 +1,13 @@
# CMD: Returns the modules of type $arg that have property $arg2
def hasPropertyWithName($a):
map(select(.Name == $a)) |
length |
. > 0
;
[.[] |
select(.Type == $arg) |
select(.Module.Android.SetProperties |
hasPropertyWithName($arg2)) |
.Name] | unique | sort | .[]

View File

@ -0,0 +1,8 @@
# CMD: Finds all modules whose input files contain the specific file type $arg.
include "library";
def isActionInputMatch($fileType): getActionInputs | split(".") | last | . == $fileType
;
[.[] | select(nonNullAction) | select(isActionInputMatch($arg)) | .Name] | sort_by(.) | unique

View File

@ -0,0 +1,7 @@
# CMD: Returns the modules in the transitive closure of module $arg
include "library";
fullTransitiveDeps([$arg])

View File

@ -0,0 +1,16 @@
# CMD: Returns the properties of module types in the transitive closure of module $arg
include "library";
[((moduleGraphNoVariants | removeSelfEdges) as $m |
[$arg] |
transitiveDeps($m)) as $names |
.[] |
select (IN(.Name; $names | .[]))] |
group_by(.Type) |
map({Type: .[0].Type,
Props: map(.Module.Android.SetProperties) | flatten | map(.Name) | unique | sort }) |
sort_by(.Type)

View File

@ -0,0 +1,7 @@
# CMD: Returns all modules of type $arg and all modules in their transitive closure.
include "library";
fullTransitiveDeps(modulesOfType($arg))

View File

@ -0,0 +1,145 @@
# Applies "f" to all variation maps in modules and deps
def transformModule(f):
.Variations = (.Variations | f) |
.DependencyVariations = (.DependencyVariations | f)
;
def transformModuleReferences(f):
transformModule(f) |
.Deps = [.Deps | .[] | transformModule(f)]
;
# Utility functions for transforming modules
def deleteDependencyVariations:
if .DependencyVariations == .Variations then del(.DependencyVariations) else . end
;
# Utility functions for transforming variation maps
def emptyIfNull: if . == null then {} else . end
;
def flattenVariations:
[. as $m | . | keys | sort | .[] | . + "=" + ($m[.] | tostring)] | join(", ")
;
def removeLinkVariation:
del(.link)
;
def removeEmptyVariations:
del(.[] | select(. == ""))
;
# Computes the difference of two maps, returns it as a single string
def mapDelta($outer; $inner):
$outer | keys as $outerKeys |
$inner | keys as $innerKeys |
($outerKeys - $innerKeys) as $removed |
($innerKeys - $outerKeys) as $added |
[($removed | .[] | "-" + . + "=" + $outer[.]), ($added | .[] | "+" + . + "=" + $inner[.])] |
join(", ")
;
# Transforms the variation map of dependencies (as specified by f) to a delta
# from the variation map of the module that depends on them
def depDelta(f):
f as $outer |
(.Deps | .[] | f) |= (. | mapDelta($outer; .))
;
# "filterMatchingDeps" filters deps that have different variations
def differentDep($od; $ov):
(.DependencyVariations != $od or .Variations != $ov) and
(.DependencyVariations != {} or .Variations != {})
;
def filterMatchingDeps: .Variations as $ov |
.DependencyVariations as $od |
.Deps = [.Deps[] | select(differentDep($ov; $od))]
;
def groupDeps:
group_by({Variations, DependencyVariations, Tag}) |
map({
DependencyVariations: .[0].DependencyVariations,
Variations: .[0].Variations,
Tag: .[0].Tag | sub(" {BaseDependencyTag:{}(?<g>.*)}"; "\(.g)"),
Modules: map(.Name)
} | del(if has("DependencyVariations") then .DependencyVariations else empty end))
;
# Utilities for filtering out interesting modules (deps remain untouched)
def onlyDeps:
{ Name: .Name, Deps: .Deps | map(.Name) }
;
def mergeDepsForSameModule:
group_by(.Name) | map({Name: .[0].Name, Deps: map(.Deps) | flatten | unique | sort})
;
def toMergeMap:
map({key: .Name, value: .Deps}) | from_entries
;
def moduleGraphNoVariants:
map(onlyDeps) | mergeDepsForSameModule | toMergeMap
;
def removeSelfEdges:
to_entries |
map(.key as $key | {key: .key, value: .value | [.[] | select(. != $key)]}) |
from_entries
;
def directDeps($m):
map($m[.] // []) + [.] | flatten | unique
;
def transitiveDeps($m):
{Prev: [], Next: .} |
until (.Prev == .Next; {Prev: .Next, Next: .Next | directDeps($m)}) |
.Next
;
def findEdge($from;$to):
.[] | select(.Name == $from) |
.Deps |= [.[] | select(.Name == $to)] |
select((.Deps | length) > 0)
;
def nonNullAction: .Module.Actions != null
;
def getActionInputs: .Module.Actions | .[] |
.Inputs | if . == null then [] else . end | .[]
;
# Gets the directory path by the given file path.
def getDirPath: sub("(?<p>.*)\\/.*"; "\(.p)")
;
# Returns the names of modules of type $arg
def modulesOfType($arg):
[.[] | select(.Type == $arg) | .Name] | unique
;
# Returns the modules in the transitive closure of $arg.
# $arg must be an array of modules names
def fullTransitiveDeps($arg):
[((moduleGraphNoVariants | removeSelfEdges) as $m |
$arg |
transitiveDeps($m)) as $names |
.[] |
select (IN(.Name; $names | .[]))] |
sort_by(.Name)
;

View File

@ -0,0 +1,15 @@
# CMD: Returns a summary of the module types present on the input
include "library";
def moduleTypeStats($arg):
group_by(.Type) |
map({
Type: .[0].Type,
Count: map(.Name) | unique | length,
VariantCount: length,
}) |
sort_by(.Count)
;
moduleTypeStats($arg)

View File

@ -0,0 +1,5 @@
# CMD: Returns the names of modules of type $arg
include "library";
modulesOfType($arg)

View File

@ -0,0 +1,15 @@
# CMD: Prints the module named $arg in a slightly more concise way
include "library";
def printModule($mod):
.[] | select(.Name == $mod) |
transformModuleReferences(emptyIfNull | removeLinkVariation | removeEmptyVariations) |
depDelta(.Variations) | depDelta(.DependencyVariations) |
transformModule(flattenVariations) |
deleteDependencyVariations |
.Deps |= map(deleteDependencyVariations) |
.Deps |= groupDeps
;
printModule($arg)

View File

@ -0,0 +1,7 @@
# CMD: Returns the names of properties used by $arg
[.[] |
select (.Name == $arg) |
.Module.Android.SetProperties |
map(.Name)] |
flatten | unique | sort

View File

@ -0,0 +1,54 @@
#!/bin/bash -eu
LIBDIR="$(dirname "$(readlink -f "$0")")"
function print_usage() {
echo "Usage: query.sh [-C] <command> <graph JSON> [argument]" 1>&2
echo " -C: colorized output" 1>&2
echo
echo "Commands":
for jq in "$LIBDIR"/*.jq; do
if ! grep -q "^# CMD:" "$jq"; then
continue
fi
local CMD="$(echo $(basename "$jq") | sed 's/\..*$//')"
echo " $CMD": $(cat "$jq" | grep "^# CMD:" | head -n 1 | sed 's/^# CMD://')
done
exit 1
}
JQARGS=""
while getopts "C" arg; do
case "$arg" in
C)
JQARGS="$JQARGS -C"
shift
;;
*)
print_usage
;;
esac
done
if [[ "$#" -lt 2 ]]; then
print_usage
fi
COMMAND="$1"
GRAPH="$2"
if [[ "$#" -gt 2 ]]; then
ARG="$3"
else
ARG=""
fi
if [[ "$#" -gt 3 ]]; then
ARG2="$4"
else
ARG2=""
fi
jq $JQARGS -L "$LIBDIR" -f "$LIBDIR/$COMMAND".jq "$GRAPH" --arg arg "$ARG" --arg arg2 "$ARG2"

View File

@ -0,0 +1,7 @@
# CMD: Returns the names of the transitive dependencies of the module named $arg
include "library";
(moduleGraphNoVariants | removeSelfEdges) as $m |
[$arg] |
transitiveDeps($m)

View File

@ -0,0 +1,8 @@
# CMD: Prints the set of variations and their values used in the input
[[.[] | .Variations | select(. != null) | to_entries] |
flatten |
group_by(.key) |
.[] |
{key: .[0].key, value: map(.value) | unique}] |
from_entries

View File

@ -0,0 +1,16 @@
# CMD: Groups outgoing dependency edges by the differences in variants
include "library";
# This filters out modules with "interesting" deps
def filterVariantTransitions:
.[] | transformModuleReferences(emptyIfNull | removeLinkVariation | removeEmptyVariations) |
filterMatchingDeps | select(.Deps | length > 0) |
depDelta(.Variations) | depDelta(.DependencyVariations) |
transformModule(flattenVariations) |
deleteDependencyVariations |
.Deps |= map(deleteDependencyVariations) |
.Deps |= groupDeps
;
[filterVariantTransitions] | sort_by(.Name) | sort_by(.Type) | .[]

10
bazel/linux.bazelrc Normal file
View File

@ -0,0 +1,10 @@
import %workspace%/build/bazel/common.bazelrc
build --host_platform //build/bazel/platforms:linux_x86_64
# Workaround JVM segfault issue as suggested at
# https://github.com/bazelbuild/bazel/issues/3236#issuecomment-310656266
build --sandbox_tmpfs_path=/tmp/
# Create a build number that will be injected later.
build --workspace_status_command=build/bazel/scripts/gen_build_number.sh

View File

@ -0,0 +1,45 @@
#! /bin/bash
# Run given scripted change and commit the changes.
#
# Assumes that the current directory is the top-level directory of
# the Android source code, created with 'repo init', and that 'repo'
# tool is on the path.
# For each of the given repo git repositories:
# 1. Checks there are neither modified not untracked files in it.
# 2. Runs the given script, which is supposed to change some files
# 3. Creates a development branch if necessary
# 4. Commits changed files. The commit message is extracted from the
# script and contains all the lines in it starting with ##CL
#
# As an example, running
# build/bazel/mk2rbc/apply_scripted_change.sh build/bazel/mk2rbc/replace_is_board_platform.sh hardware/qcom/media
# replaces the old is-board-platform calls with the new is-board-platform2 calls.
set -eu
function die() { format=$1; shift; printf "$format\n" $@; exit 1; }
function usage() { die "Usage: %s script git-repo ..." ${0##*/}; }
(($#>=2)) || usage
declare -r script=$(realpath $1); shift
rc=0
[[ -x $script ]] || die "%s is not an executable script" $script
declare -r bugid="$(sed -nr 's/^##CL (Bug:|Fixes:) +([0-9]+)$/\2/p' $script)"
[[ -n "$bugid" ]] || die "$script contains neither '##CL Bug: ' nor '##CL Fixes: 'tag"
for gr in $@; do
[[ -d $gr/.git ]] || { echo $gr is not a Git directory; rc=1; continue; }
out=$(git -C $gr status --porcelain --untracked-files=no) || { die "so skipping $gr because of the above"; rc=1; continue; }
[[ -z "$out" ]] || { echo $gr contains uncommitted changes:; echo "$out" >&2; rc=1; continue; }
(cd $gr && $script)
modified="$(git -C $gr status --porcelain | sed -nr 's/^ M (.*)/\1/p')"
[[ -n "$modified" ]] || { echo no files changed in $gr; continue; }
[[ -z "$(repo status -q $gr 2>/dev/null)" ]] || repo start b$bugid $gr
git -C $gr add $modified
git -C $gr commit -q \
-F <(sed -nr 's/^##CL *//p' $script; echo -e '\nThis change has been generated by the following script:\n\n```'; grep -vP '^##CL' $script; echo '```')
done
exit $rc

View File

@ -0,0 +1,11 @@
#! /bin/bash
##CL Provide location hint for the dynamically calculated paths.
##CL
##CL For the paths using QC_PROP_PATH or QC_PROP_ROOT
##CL Bug: 203582721
##CL Test: treehugger
declare -r files="$(grep -rlP '^ *(\$\(call inherit-product|-?include).*\$\(QC_PROP_(PATH|ROOT)\)' --include 'BoardConfig*.mk')"
[[ -z "$files" ]] || sed -i -r -f <(cat <<"EOF"
/^ *(\$\(call inherit-product|-?include).*\$\(QC_PROP_(PATH|ROOT)\)/i#RBC# include_top vendor/qcom
EOF
) $files

View File

@ -0,0 +1,31 @@
#! /bin/bash
##CL Replace is-board-platform[-in-list] with is-board-platform[-in-list]2
##CL
##CL The regular is-board-platform[-in-list] functions are defined in
##CL some product/board configuration makefiles, and sometimes also
##CL used in Android.mk files. When the product/board configuration
##CL is converted to starlark, the functions will no longer be defined
##CL for the Android.mk files to use. Switch to using a version of
##CL these functions that is defined inside the core build system
##CL makefiles, so it will still be defined when the configuration
##CL is in Starlark.
##CL
##CL The new function returns either an empty string or the matching
##CL platform, while the old one returned either an empty string or true.
##CL So now if statements are compared against an empty string instead of
##CL true.
##CL
##CL Bug: 201477826
##CL Test: treehugger
declare -r files="$(grep -rlP '^[^#]*call +is-board-platform' --include '*.mk' --exclude 'utils_test.mk' --exclude 'utils_sample_usage.mk')"
[[ -z "$files" ]] || sed -i -r -f <(cat <<"EOF"
s/ifeq +\(\$\(call is-board-platform,(.*)\), *true\)/ifneq (,$(call is-board-platform2,\1))/
s/ifeq +\(\$\(call is-board-platform,(.*)\), *\)/ifeq (,$(call is-board-platform2,\1))/
s/ifneq +\(\$\(call is-board-platform,(.*)\), *true\)/ifeq (,$(call is-board-platform2,\1))/
s/ifeq +\(\$\(call is-board-platform-in-list,(.*)\), *true\)/ifneq (,$(call is-board-platform-in-list2,\1))/
s/ifeq +\(\$\(call is-board-platform-in-list,(.*)\), *\)/ifeq (,$(call is-board-platform-in-list2,\1))/
s/ifeq +\(\$\(call is-board-platform-in-list,(.*)\), *false\)/ifeq (,T) # TODO: remove useless check/
s/ifneq +\(\$\(call is-board-platform-in-list,(.*)\), *true\)/ifeq (,$(call is-board-platform-in-list2,\1))/
s/\$\(call is-board-platform,(.*)\)/$(call is-board-platform2,\1)/
EOF
) $files

View File

@ -0,0 +1,16 @@
#! /bin/bash
##CL Replace is-platform-sdk-version-at-least calls with checking IS_AT_LEAST_xxx.
##CL
##CL Bug: 201477826
##CL Test: treehugger
declare -r files="$(grep -rlP '^[^#]*call +is-platform-sdk-version-at-least' --include '*.mk')"
[[ -z "$files" ]] || sed -i -r -f <(cat <<"EOF"
s/^([^#]*ifn?eq) +\(\$\(call is-platform-sdk-version-at-least, *(16|17|18|19|20|21|22|23|24|25)\), *true\)/\1 \(T,T\) \# TODO: Obsolete, please remove/
s/^([^#]*)if(n?)eq +\(\$\(call is-platform-sdk-version-at-least, *26\), *true\)/\1if\2def IS_AT_LEAST_OPR1/
s/^([^#]*)if(n?)eq +\(\$\(call is-platform-sdk-version-at-least, *27\), *true\)/\1if\2def IS_AT_LEAST_OPM1/
s/^([^#]*)if(n?)eq +\(\$\(call is-platform-sdk-version-at-least, *28\), *true\)/\1if\2def IS_AT_LEAST_PPR1/
s/^([^#]*)if(n?)eq +\(\$\(call is-platform-sdk-version-at-least, *29\), *true\)/\1if\2def IS_AT_LEAST_QP1A/
s/^([^#]*)if(n?)eq +\(\$\(call is-platform-sdk-version-at-least, *30\), *true\)/\1if\2def IS_AT_LEAST_RP1A/
s/^([^#]*)if(n?)eq +\(\$\(call is-platform-sdk-version-at-least, *31\), *true\)/\1if\2def IS_AT_LEAST_SP1A/
EOF
) $files

View File

@ -0,0 +1,11 @@
#! /bin/bash
##CL Replace is-vendor-board-platform with is-vendor-board-qcom.
##CL
##CL Bug: 201477826
##CL Test: treehugger
declare -r files="$(grep -rlP '^[^#]*call +is-vendor-board-platform' --include '*.mk')"
[[ -z "$files" ]] || sed -i -r -f <(cat <<"EOF"
s/ifeq \(\$\(call is-vendor-board-platform,QCOM\),true\)/ifneq (,$(call is-vendor-board-qcom))/
s/ifneq \(\$\(call is-vendor-board-platform,QCOM\),true\)/ifeq (,$(call is-vendor-board-qcom))/
EOF
) $files

135
bazel/platforms/BUILD.bazel Normal file
View File

@ -0,0 +1,135 @@
# Common target platforms for Android Platform builds.
#
# Platforms represent distinct hardware environments:
#
# - CPU architecture
# - Hardware capabilities
# - Firmware differences
#
# Platforms do NOT represent different software packages, that is the
# reposibility of the top-level targets being built.
#
# These model after the arch and OS definitions in build/soong/android/arch.go.
load("@soong_injection//product_config:product_variables.bzl", "product_vars")
load("//build/bazel/platforms:product_variables/product_platform.bzl", "android_platform", "product_variable_config")
load("//build/bazel/platforms/arch/variants:constants.bzl", "constants")
load(
"//prebuilts/clang/host/linux-x86:cc_toolchain_constants.bzl",
"arch_to_variants",
"variant_constraints",
"variant_name",
)
package(default_visibility = ["//visibility:public"])
product_variable_config(
name = "android_target",
product_config_vars = product_vars,
)
# Linux is the OS
# for the Linux kernel plus the glibc runtime.
android_platform(
name = "linux_x86",
constraint_values = [
"//build/bazel/platforms/arch:x86",
"//build/bazel/platforms/os:linux",
],
product = ":android_target",
)
android_platform(
name = "linux_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:linux",
],
product = ":android_target",
)
# linux_bionic is the OS for the Linux kernel plus the Bionic libc runtime, but
# without the rest of Android.
android_platform(
name = "linux_bionic_arm64",
constraint_values = [
"//build/bazel/platforms/arch:arm64",
"//build/bazel/platforms/os:linux_bionic",
],
product = ":android_target",
)
android_platform(
name = "linux_bionic_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:linux_bionic",
],
product = ":android_target",
)
# Darwin is the OS for MacOS host machines.
android_platform(
name = "darwin_arm64",
constraint_values = [
"//build/bazel/platforms/arch:arm64",
"//build/bazel/platforms/os:darwin",
],
product = ":android_target",
)
android_platform(
name = "darwin_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:darwin",
],
product = ":android_target",
)
# Windows is the OS for Windows host machines.
android_platform(
name = "windows_x86",
constraint_values = [
"//build/bazel/platforms/arch:x86",
"//build/bazel/platforms/os:windows",
],
product = ":android_target",
)
android_platform(
name = "windows_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:windows",
],
product = ":android_target",
)
alias(
name = "android_arm",
actual = ":android_arm_armv7-a-neon", # default to armv7-a-neon
)
alias(
name = "android_arm64",
actual = ":android_arm64_armv8-a", # default to armv8-a
)
[
[
android_platform(
name = "android_" + arch + variant_name(variant),
constraint_values = [
"//build/bazel/platforms/arch:" + arch,
"//build/bazel/platforms/os:android",
] + variant_constraints(
variant,
constants.AndroidArchToVariantToFeatures[arch],
),
product = ":android_target",
)
for variant in variants
]
for arch, variants in arch_to_variants.items()
]

View File

@ -0,0 +1,29 @@
# Standard cpu name constraint_setting and constraint_values
licenses(["notice"])
package(
default_visibility = ["//visibility:public"],
)
constraint_value(
name = "arm",
constraint_setting = "@platforms//cpu:cpu",
)
constraint_value(
name = "arm64",
constraint_setting = "@platforms//cpu:cpu",
)
constraint_value(
name = "x86",
constraint_setting = "@platforms//cpu:cpu",
)
# Alias to the local_jdk's toolchain constraint to make local_jdk resolve
# correctly with --tool_java_runtime_version=local_jdk and the checked-in JDK.
alias(
name = "x86_64",
actual = "@platforms//cpu:x86_64"
)

View File

@ -0,0 +1,89 @@
# Cpu/Arch Variants and features
load("//build/bazel/product_variables:constants.bzl", _product_variable_constants = "constants")
load(":constants.bzl", "constants")
constraint_setting(
name = "arch_variant_constraint",
)
licenses(["notice"])
package(
default_visibility = ["//visibility:public"],
)
[
constraint_value(
name = arch_variant,
constraint_setting = "arch_variant_constraint",
)
for arch_variant in constants.AvailableArchVariants
]
[
[
config_setting(
name = variant + "-" + arch,
constraint_values = [
_product_variable_constants.ArchVariantToConstraints[arch],
":" + variant,
],
)
for variant in variants
]
for arch, variants in constants.ArchToVariants.items()
]
constraint_setting(
name = "cpu_variant_constraint",
)
[
constraint_value(
name = cpu_variant,
constraint_setting = "cpu_variant_constraint",
)
for cpu_variant in constants.AvailableCpuVariants
]
[
[
config_setting(
name = variant + "-" + arch,
constraint_values = [
_product_variable_constants.ArchVariantToConstraints[arch],
":" + variant,
],
)
for variant in variants
]
for arch, variants in constants.CpuToVariants.items()
]
[
(
constraint_setting(
name = "arch_feature_constraint_" + arch_feature,
),
constraint_value(
name = arch_feature,
constraint_setting = "arch_feature_constraint_" + arch_feature,
),
)
for arch_feature in constants.AvailableArchFeatures
]
[
[
config_setting(
name = feature + "-" + arch,
constraint_values = [
_product_variable_constants.ArchVariantToConstraints[arch],
":" + feature,
],
)
for feature in features
]
for arch, features in constants.ArchToFeatures.items()
]

View File

@ -0,0 +1,44 @@
"""Constants for arch/cpu variants/features."""
# 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.
load(
"@soong_injection//product_config:arch_configuration.bzl",
_android_arch_feature_for_arch_variant = "android_arch_feature_for_arch_variants",
_arch_to_cpu_variants = "arch_to_cpu_variants",
_arch_to_features = "arch_to_features",
_arch_to_variants = "arch_to_variants",
)
def _flatten_string_list_dict_to_set(string_list_dict):
ret = {}
for l in string_list_dict.values():
for i in l:
ret[i] = True
return ret
_arch_variants = _flatten_string_list_dict_to_set(_arch_to_variants)
_cpu_variants = _flatten_string_list_dict_to_set(_arch_to_cpu_variants)
_arch_features = _flatten_string_list_dict_to_set(_arch_to_features)
constants = struct(
AvailableArchVariants = _arch_variants,
AvailableCpuVariants = _cpu_variants,
AvailableArchFeatures = _arch_features,
ArchToVariants = _arch_to_variants,
CpuToVariants = _arch_to_cpu_variants,
ArchToFeatures = _arch_to_features,
AndroidArchToVariantToFeatures = _android_arch_feature_for_arch_variant,
)

68
bazel/platforms/os/BUILD Normal file
View File

@ -0,0 +1,68 @@
# Standard constraint_setting and constraint_values to be used in platforms.
load("@bazel_skylib//lib:selects.bzl", "selects")
licenses(["notice"])
package(
default_visibility = ["//visibility:public"],
)
constraint_value(
name = "android",
constraint_setting = "@platforms//os:os",
)
config_setting(
name = "android_config_setting",
constraint_values = [
":android",
],
)
# Alias to the local_jdk's toolchain constraint to make local_jdk resolve
# correctly with --tool_java_runtime_version=local_jdk and the checked-in JDK.
alias(
name = "linux",
actual = "@platforms//os:linux",
)
alias(
name = "linux_glibc",
actual = "@platforms//os:linux",
)
constraint_value(
name = "linux_musl",
constraint_setting = "@platforms//os:os",
)
constraint_value(
name = "linux_bionic",
constraint_setting = "@platforms//os:os",
)
config_setting(
name = "linux_bionic_config_setting",
constraint_values = [
":linux_bionic",
],
)
constraint_value(
name = "windows",
constraint_setting = "@platforms//os:os",
)
constraint_value(
name = "darwin",
constraint_setting = "@platforms//os:os",
)
selects.config_setting_group(
name = "bionic",
match_any = [
":android_config_setting",
":linux_bionic_config_setting",
],
)

View File

@ -0,0 +1,111 @@
config_setting(
name = "android_arm",
constraint_values = [
"//build/bazel/platforms/arch:arm",
"//build/bazel/platforms/os:android",
],
)
config_setting(
name = "android_arm64",
constraint_values = [
"//build/bazel/platforms/arch:arm64",
"//build/bazel/platforms/os:android",
],
)
config_setting(
name = "android_x86",
constraint_values = [
"//build/bazel/platforms/arch:x86",
"//build/bazel/platforms/os:android",
],
)
config_setting(
name = "android_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:android",
],
)
config_setting(
name = "darwin_arm64",
constraint_values = [
"//build/bazel/platforms/arch:arm64",
"//build/bazel/platforms/os:darwin",
],
)
config_setting(
name = "darwin_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:darwin",
],
)
config_setting(
name = "linux_glibc_x86",
constraint_values = [
"//build/bazel/platforms/arch:x86",
"//build/bazel/platforms/os:linux_glibc",
],
)
config_setting(
name = "linux_glibc_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:linux_glibc",
],
)
config_setting(
name = "linux_bionic_arm64",
constraint_values = [
"//build/bazel/platforms/arch:arm64",
"//build/bazel/platforms/os:linux_bionic",
],
)
config_setting(
name = "linux_bionic_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:linux_bionic",
],
)
config_setting(
name = "linux_musl_x86",
constraint_values = [
"//build/bazel/platforms/arch:x86",
"//build/bazel/platforms/os:linux_musl",
],
)
config_setting(
name = "linux_musl_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:linux_musl",
],
)
config_setting(
name = "windows_x86",
constraint_values = [
"//build/bazel/platforms/arch:x86",
"//build/bazel/platforms/os:windows",
],
)
config_setting(
name = "windows_x86_64",
constraint_values = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:windows",
],
)

View File

@ -0,0 +1,148 @@
"""Parallels variable.go to provide variables and create a platform based on converted config."""
load("//build/bazel/product_variables:constants.bzl", "constants")
load("//prebuilts/clang/host/linux-x86:cc_toolchain_constants.bzl", "variant_name")
def _product_variables_providing_rule_impl(ctx):
return [
platform_common.TemplateVariableInfo(ctx.attr.product_vars),
]
# Provides product variables for templated string replacement.
product_variables_providing_rule = rule(
implementation = _product_variables_providing_rule_impl,
attrs = {
"product_vars": attr.string_dict(),
},
)
_arch_os_only_suffix = "_arch_os"
_product_only_suffix = "_product"
def add_providing_var(providing_vars, typ, var, value):
if typ == "bool":
providing_vars[var] = "1" if value else "0"
elif typ == "list":
providing_vars[var] = ",".join(value)
elif typ == "int":
providing_vars[var] = str(value)
elif typ == "string":
providing_vars[var] = value
def product_variable_config(name, product_config_vars):
constraints = []
local_vars = dict(product_config_vars)
# Native_coverage is not set within soong.variables, but is hardcoded
# within config.go NewConfig
local_vars["Native_coverage"] = (
local_vars.get("ClangCoverage", False) or
local_vars.get("GcovCoverage", False)
)
providing_vars = {}
# Generate constraints for Soong config variables (bool, value, string typed).
vendor_vars = local_vars.pop("VendorVars", default = {})
for (namespace, variables) in vendor_vars.items():
for (var, value) in variables.items():
# All vendor vars are Starlark string-typed, even though they may be
# boxed bools/strings/arbitrary printf'd values, like numbers, so
# we'll need to do some translation work here by referring to
# soong_injection's generated data.
if value == "":
# Variable is not set so skip adding this as a constraint.
continue
# Create the identifier for the constraint var (or select key)
config_var = namespace + "__" + var
# List of all soong_config_module_type variables.
if not config_var in constants.SoongConfigVariables:
continue
# Normalize all constraint vars (i.e. select keys) to be lowercased.
constraint_var = config_var.lower()
if config_var in constants.SoongConfigBoolVariables:
constraints.append("//build/bazel/product_variables:" + constraint_var)
elif config_var in constants.SoongConfigStringVariables:
# The string value is part of the the select key.
constraints.append("//build/bazel/product_variables:" + constraint_var + "__" + value.lower())
elif config_var in constants.SoongConfigValueVariables:
# For value variables, providing_vars add support for substituting
# the value using TemplateVariableInfo.
constraints.append("//build/bazel/product_variables:" + constraint_var)
add_providing_var(providing_vars, "string", constraint_var, value)
for (var, value) in local_vars.items():
# TODO(b/187323817): determine how to handle remaining product
# variables not used in product_variables
constraint_var = var.lower()
if not constants.ProductVariables.get(constraint_var):
continue
# variable.go excludes nil values
add_constraint = (value != None)
add_providing_var(providing_vars, type(value), var, value)
if type(value) == "bool":
# variable.go special cases bools
add_constraint = value
if add_constraint:
constraints.append("//build/bazel/product_variables:" + constraint_var)
native.platform(
name = name + _product_only_suffix,
constraint_values = constraints,
)
arch = local_vars.get("DeviceArch")
arch_variant = local_vars.get("DeviceArchVariant")
cpu_variant = local_vars.get("DeviceCpuVariant")
os = "android"
native.alias(
name = name,
actual = "{os}_{arch}{variant}".format(os = os, arch = arch, variant = _variant_name(arch, arch_variant, cpu_variant)),
)
arch = local_vars.get("DeviceSecondaryArch")
arch_variant = local_vars.get("DeviceSecondaryArchVariant")
cpu_variant = local_vars.get("DeviceSecondaryCpuVariant")
if arch:
native.alias(
name = name + "_secondary",
actual = "{os}_{arch}{variant}".format(os = os, arch = arch, variant = _variant_name(arch, arch_variant, cpu_variant)),
)
product_variables_providing_rule(
name = name + "_product_vars",
product_vars = providing_vars,
)
def _is_variant_default(arch, variant):
return variant == None or variant in (arch, "generic")
def _variant_name(arch, arch_variant, cpu_variant):
if _is_variant_default(arch, arch_variant):
arch_variant = ""
if _is_variant_default(arch, cpu_variant):
cpu_variant = ""
variant = struct(
arch_variant = arch_variant,
cpu_variant = cpu_variant,
)
return variant_name(variant)
def android_platform(name = None, constraint_values = [], product = None):
""" android_platform creates a platform with the specified constraint_values and product constraints."""
native.platform(
name = name,
constraint_values = constraint_values,
parents = [product + _product_only_suffix],
)

View File

@ -0,0 +1,47 @@
# 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.
"""Utilities for rule implementations to interact with platform definitions."""
# Merge ARCH_CONSTRAINT_ATTRS with the rule attrs to use get_arch(ctx).
ARCH_CONSTRAINT_ATTRS = {
"_x86_constraint": attr.label(default = Label("//build/bazel/platforms/arch:x86")),
"_x86_64_constraint": attr.label(default = Label("//build/bazel/platforms/arch:x86_64")),
"_arm_constraint": attr.label(default = Label("//build/bazel/platforms/arch:arm")),
"_arm64_constraint": attr.label(default = Label("//build/bazel/platforms/arch:arm64")),
}
# get_arch takes a rule context with ARCH_CONSTRAINT_ATTRS and returns the string representation
# of the target platform by executing the target_platform_has_constraint boilerplate.
def get_arch(ctx):
if not hasattr(ctx.attr, "_x86_constraint") or \
not hasattr(ctx.attr, "_x86_64_constraint") or \
not hasattr(ctx.attr, "_arm_constraint") or \
not hasattr(ctx.attr, "_arm64_constraint"):
fail("Could not get the target architecture of this rule due to missing constraint attrs.",
"Have you merged ARCH_CONSTRAINT_ATTRS into this rule's attributes?")
x86_constraint = ctx.attr._x86_constraint[platform_common.ConstraintValueInfo]
x86_64_constraint = ctx.attr._x86_64_constraint[platform_common.ConstraintValueInfo]
arm_constraint = ctx.attr._arm_constraint[platform_common.ConstraintValueInfo]
arm64_constraint = ctx.attr._arm64_constraint[platform_common.ConstraintValueInfo]
if ctx.target_platform_has_constraint(x86_constraint):
return "x86"
elif ctx.target_platform_has_constraint(x86_64_constraint):
return "x86_64"
elif ctx.target_platform_has_constraint(arm_constraint):
return "arm"
elif ctx.target_platform_has_constraint(arm64_constraint):
return "arm64"

View File

@ -0,0 +1,78 @@
"""Constraints corresponding to product variables."""
load(":constants.bzl", "constants")
load(":settings.bzl", "soong_config_variables")
package(default_visibility = ["//visibility:public"])
# Unlike product config variables below, these are dynamically generated from
# Soong, since the list of config variables are dynamically defined in
# Android.bp files and not hardcoded into Soong.
soong_config_variables(
bool_vars = constants.SoongConfigBoolVariables,
string_vars = constants.SoongConfigStringVariables,
value_vars = constants.SoongConfigValueVariables,
)
# Generate one constraint_value for each product_variable
# The constraint_value for <var> can be within a select() to specify an
# attribute value for the same conditions product_variable.<var>, for most
# cases, that is when the value of <var> is true. For example,
#
# product_variables: {
# debuggable: {
# cflags: ["debug_flag1", "debug_flag2"],
# },
# }
#
# translates into:
#
# cflags = select({
# "//build/bazel/product_variables:debuggable": ["debug_flag1", "debug_flag2"],
# "//conditions:default": [],
# }),
[
(
constraint_setting(name = product_variable + "_constraint"),
constraint_value(
name = product_variable,
constraint_setting = product_variable + "_constraint",
),
)
for product_variable in constants.ProductVariables
]
# Caution: do not use these arch-variant product variables directly.
# If you have a complex combination of product variable and architecture/os/etc,
# prefer instead to craft an appropriate configuration in your BUILD file.
# See: https://docs.bazel.build/versions/master/configurable-attributes.html
# Within bp2build, :safestack-android should be used when an attribute value is
# conditional on both safestack:true and the os is android.
#
# e.g.
# target: {
# android: {
# product_variables: {
# safestack: {
# cflags: ["-Dsafestack-android"],
# },
# },
# },
# },
#
# would translate to:
#
# cflags = select({
# "//build/bazel/product_variables:safestack-android": ["-Dsafestack-android"],
# "//conditions:default": [],
# }),
[
[config_setting(
name = product_variable + "-" + variant,
constraint_values = [
":" + product_variable,
variantConstraint,
],
) for variant, variantConstraint in constants.ArchVariantToConstraints.items()]
for product_variable in constants.ArchVariantProductVariables
]

View File

@ -0,0 +1,49 @@
"""Constants for product variables based on information in variable.go"""
load(
"@soong_injection//product_config:soong_config_variables.bzl",
_soong_config_bool_variables = "soong_config_bool_variables",
_soong_config_string_variables = "soong_config_string_variables",
_soong_config_value_variables = "soong_config_value_variables",
)
load(
"@soong_injection//product_config:product_variables.bzl",
_arch_variant_product_var_constraints = "arch_variant_product_var_constraints",
_product_var_constraints = "product_var_constraints",
)
_soong_config_variables = _soong_config_bool_variables.keys() + \
_soong_config_string_variables.keys() + \
_soong_config_value_variables.keys()
_product_variables = {
var: True
for var in _product_var_constraints
}
_arch_variant_product_variables = {
var: True
for var in _arch_variant_product_var_constraints
}
_arch_variant_to_constraints = {
"arm": "//build/bazel/platforms/arch:arm",
"arm64": "//build/bazel/platforms/arch:arm64",
"x86": "//build/bazel/platforms/arch:x86",
"x86_64": "//build/bazel/platforms/arch:x86_64",
"android": "//build/bazel/platforms/os:android",
"darwin": "//build/bazel/platforms/os:darwin",
"linux": "//build/bazel/platforms/os:linux",
"linux_bionic": "//build/bazel/platforms/os:linux_bionic",
"windows": "//build/bazel/platforms/os:windows",
}
constants = struct(
SoongConfigVariables = _soong_config_variables,
SoongConfigBoolVariables = _soong_config_bool_variables,
SoongConfigStringVariables = _soong_config_string_variables,
SoongConfigValueVariables = _soong_config_value_variables,
ProductVariables = _product_variables,
ArchVariantProductVariables = _arch_variant_product_variables,
ArchVariantToConstraints = _arch_variant_to_constraints,
)

View File

@ -0,0 +1,22 @@
"""Macros to generate constraint settings and values for Soong variables."""
def soong_config_variables(bool_vars, value_vars, string_vars):
for variable in bool_vars.keys() + value_vars.keys():
variable = variable.lower()
native.constraint_setting(
name = variable + "_constraint",
)
native.constraint_value(
name = variable,
constraint_setting = variable + "_constraint",
)
for variable, choices in string_vars.items():
for choice in choices:
var_with_choice = (variable + "__" + choice).lower()
native.constraint_setting(
name = var_with_choice + "_constraint",
)
native.constraint_value(
name = var_with_choice,
constraint_setting = var_with_choice + "_constraint",
)

1
bazel/rules/BUILD.bazel Normal file
View File

@ -0,0 +1 @@
exports_files(["lunch.bzl"])

17
bazel/rules/README.md Normal file
View File

@ -0,0 +1,17 @@
# Bazel rules for Android Platform.
This directory contains Starlark extensions for building the Android Platform with Bazel.
## APEX
Run the following command to build a miminal APEX example.
```
$ b build //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal
```
Verify the contents of the APEX with `zipinfo`:
```
$ zipinfo bazel-bin/build/bazel/examples/apex/minimal/build.bazel.examples.apex.minimal.apex
```

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,49 @@
"""
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.
"""
AndroidAppCertificateInfo = provider(
"Info needed for Android app certificates",
fields = {
"pem": "Certificate .pem file",
"pk8": "Certificate .pk8 file",
},
)
def _android_app_certificate_rule_impl(ctx):
return [
AndroidAppCertificateInfo(pem = ctx.file.pem, pk8 = ctx.file.pk8),
]
_android_app_certificate = rule(
implementation = _android_app_certificate_rule_impl,
attrs = {
"pem": attr.label(mandatory = True, allow_single_file = [".pem"]),
"pk8": attr.label(mandatory = True, allow_single_file = [".pk8"]),
},
)
def android_app_certificate(
name,
certificate,
**kwargs):
"Bazel macro to correspond with the Android app certificate Soong module."
_android_app_certificate(
name = name,
pem = certificate + ".x509.pem",
pk8 = certificate + ".pk8",
**kwargs
)

View File

@ -0,0 +1,131 @@
"""
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.
"""
load("@bazel_skylib//lib:paths.bzl", "paths")
load("android_app_certificate.bzl", "AndroidAppCertificateInfo")
AndroidAppKeystoreInfo = provider(
"Info needed for Android app keystores",
fields = {
"keystore": "JKS .keystore file housing certificate info",
},
)
def _pk8_to_private_pem(ctx, openssl, pk8_file, private_pem_file):
"""Converts a .pk8 private key file in DER format to a .pem private key file in PEM format."""
args = ctx.actions.args()
args.add("pkcs8")
args.add_all(["-in", pk8_file])
args.add_all(["-inform", "DER"])
args.add_all(["-outform", "PEM"])
args.add_all(["-out", private_pem_file])
args.add("-nocrypt") # don't bother encrypting this private key since it is just an intermediate file
ctx.actions.run(
inputs = [pk8_file],
executable = openssl,
outputs = [private_pem_file],
arguments = [args],
mnemonic = "CreatePrivPEM",
)
def _pem_to_pk12(ctx, openssl, certificate_pem, private_key_pem, pk12_file):
"""Converts an X.509 certificate and private key pair of PEM files to a single PKCS12 keystore file."""
args = ctx.actions.args()
args.add("pkcs12")
args.add("-export")
args.add_all(["-in", certificate_pem])
args.add_all(["-inkey", private_key_pem])
args.add_all(["-out", pk12_file])
args.add_all(["-name", "android"])
# openssl requires a password and will request a
# password from STDIN if we don't supply one here
args.add_all(["-passout", "pass:android"])
ctx.actions.run(
inputs = [
certificate_pem,
private_key_pem,
],
executable = openssl,
outputs = [pk12_file],
arguments = [args],
mnemonic = "CreatePK12",
)
def _pk12_to_keystore(ctx, keytool, pk12_file, keystore_file):
"""Converts a PKCS12 keystore file to a JKS keystore file."""
args = ctx.actions.args()
args.add("-importkeystore")
args.add_all(["-destkeystore", keystore_file])
args.add_all(["-srckeystore", pk12_file])
args.add_all(["-srcstoretype", "PKCS12"])
args.add_all(["-srcstorepass", "android"])
# apksigner expects keystores provided by the debug_signing_keys attribute
# to be secured with the password "android"
args.add_all(["-deststorepass", "android"])
ctx.actions.run(
inputs = [pk12_file],
executable = keytool,
outputs = [keystore_file],
arguments = [args],
mnemonic = "CreateKeystore",
)
def _android_app_keystore_rule_impl(ctx):
openssl = ctx.executable._openssl
keytool = ctx.executable._keytool
private_pem = ctx.actions.declare_file(ctx.attr.name + ".priv.pem")
pk12 = ctx.actions.declare_file(ctx.attr.name + ".pk12")
keystore = ctx.actions.declare_file(ctx.attr.name + ".keystore")
pk8_file = ctx.attr.certificate[AndroidAppCertificateInfo].pk8
pem_file = ctx.attr.certificate[AndroidAppCertificateInfo].pem
_pk8_to_private_pem(ctx, openssl, pk8_file, private_pem)
_pem_to_pk12(ctx, openssl, pem_file, private_pem, pk12)
_pk12_to_keystore(ctx, keytool, pk12, keystore)
return [
AndroidAppKeystoreInfo(
keystore = keystore,
),
DefaultInfo(files = depset(direct = [keystore]))
]
"""Converts an android_app_certificate (i.e. pem/pk8 pair) into a JKS keystore"""
android_app_keystore = rule(
implementation = _android_app_keystore_rule_impl,
attrs = {
"certificate": attr.label(mandatory = True, providers = [AndroidAppCertificateInfo]),
"_openssl": attr.label(
default = Label("//prebuilts/build-tools:linux-x86/bin/openssl"),
allow_single_file = True,
executable = True,
cfg = "exec",
doc = "An OpenSSL compatible tool."
),
"_keytool": attr.label(
default = Label("//prebuilts/jdk/jdk11:linux-x86/bin/keytool"),
allow_single_file = True,
executable = True,
cfg = "exec",
doc = "The keytool binary."
),
},
provides = [AndroidAppKeystoreInfo],
)

View File

@ -0,0 +1,99 @@
"""
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.
"""
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_android//rules:rules.bzl", _android_binary = "android_binary")
load("@soong_injection//product_config:product_variables.bzl", "product_vars")
load("android_app_certificate.bzl", "android_app_certificate")
load("android_app_keystore.bzl", "android_app_keystore")
def _default_cert_prod_var():
return product_vars["DefaultAppCertificate"]
def _default_app_certificate_package():
default_cert = _default_cert_prod_var()
if default_cert:
return "//" + paths.dirname(default_cert)
# if product variable is not set, default to Soong default:
return "//build/make/target/product/security"
def _default_app_certificate():
default_cert = _default_cert_prod_var()
if default_cert:
return default_cert
return _default_app_certificate_package() + ":testkey"
def _android_app_certificate_with_default_cert(name, cert_name):
if cert_name:
# if a specific certificate name is given, check the default directory
# for that certificate
certificate = _default_app_certificate_package() + ":" + cert_name
else:
certificate = _default_app_certificate()
android_app_certificate(
name = name,
certificate = certificate,
)
def android_binary(
name,
certificate = None,
certificate_name = None,
**kwargs):
"""Bazel macro to find and create a keystore to use for debug_signing_keys
with @rules_android android_binary.
This module emulates the Soong behavior which allows a developer to specify
a specific module name for the android_app_certificate or the name of a
.pem/.pk8 certificate/key pair in a directory specified by the
DefaultAppCertificate product variable. In either case, we convert the specified
.pem/.pk8 certificate/key pair to a JKS .keystore file before passing it to the
android_binary rule.
Arguments:
certificate: Bazel target
certificate_name: string, name of private key file in default certificate directory
**kwargs: map, additional args to pass to android_binary
"""
if certificate and certificate_name:
fail("Cannot use both certificate_name and certificate attributes together. Use only one of them.")
debug_signing_keys = kwargs.pop("debug_signing_keys", [])
if certificate or certificate_name:
if certificate_name:
app_cert_name = name + "_app_certificate"
_android_app_certificate_with_default_cert(app_cert_name, certificate_name)
certificate = ":" + app_cert_name
app_keystore_name = name + "_keystore"
android_app_keystore(
name = app_keystore_name,
certificate = certificate
)
debug_signing_keys.append(app_keystore_name)
_android_binary(
name = name,
debug_signing_keys = debug_signing_keys,
**kwargs
)

432
bazel/rules/apex.bzl Normal file
View File

@ -0,0 +1,432 @@
"""
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.
"""
load(":apex_key.bzl", "ApexKeyInfo")
load(":prebuilt_file.bzl", "PrebuiltFileInfo")
load(":sh_binary.bzl", "ShBinaryInfo")
load("//build/bazel/rules/cc:stripped_cc_common.bzl", "StrippedCcBinaryInfo")
load("//build/bazel/rules/android:android_app_certificate.bzl", "AndroidAppCertificateInfo")
load("//build/bazel/rules/apex:transition.bzl", "apex_transition", "shared_lib_transition_32", "shared_lib_transition_64")
load("//build/bazel/rules/apex:cc.bzl", "ApexCcInfo", "apex_cc_aspect")
DIR_LIB = "lib"
DIR_LIB64 = "lib64"
ApexInfo = provider(
"ApexInfo has no field currently and is used by apex rule dependents to ensure an attribute is a target of apex rule.",
fields = {},
)
# Prepare the input files info for bazel_apexer_wrapper to generate APEX filesystem image.
def _prepare_apexer_wrapper_inputs(ctx):
# dictionary to return in the format:
# apex_manifest[(image_file_dirname, image_file_basename)] = bazel_output_file
apex_manifest = {}
x86_constraint = ctx.attr._x86_constraint[platform_common.ConstraintValueInfo]
x86_64_constraint = ctx.attr._x86_64_constraint[platform_common.ConstraintValueInfo]
arm_constraint = ctx.attr._arm_constraint[platform_common.ConstraintValueInfo]
arm64_constraint = ctx.attr._arm64_constraint[platform_common.ConstraintValueInfo]
if ctx.target_platform_has_constraint(x86_constraint):
_add_libs_32_target(ctx, "x86", apex_manifest)
elif ctx.target_platform_has_constraint(x86_64_constraint):
_add_libs_64_target(ctx, "x86", "x86_64", apex_manifest)
elif ctx.target_platform_has_constraint(arm_constraint):
_add_libs_32_target(ctx, "arm", apex_manifest)
elif ctx.target_platform_has_constraint(arm64_constraint):
_add_libs_64_target(ctx, "arm", "arm64", apex_manifest)
# Handle prebuilts
for dep in ctx.attr.prebuilts:
prebuilt_file_info = dep[PrebuiltFileInfo]
if prebuilt_file_info.filename:
filename = prebuilt_file_info.filename
else:
filename = dep.label.name
apex_manifest[(prebuilt_file_info.dir, filename)] = prebuilt_file_info.src
# Handle binaries
for dep in ctx.attr.binaries:
if ShBinaryInfo in dep:
# sh_binary requires special handling on directory/filename construction.
sh_binary_info = dep[ShBinaryInfo]
default_info = dep[DefaultInfo]
if sh_binary_info != None:
directory = "bin"
if sh_binary_info.sub_dir != None and sh_binary_info.sub_dir != "":
directory = "/".join([directory, sh_binary_info.sub_dir])
if sh_binary_info.filename != None and sh_binary_info.filename != "":
filename = sh_binary_info.filename
else:
filename = dep.label.name
apex_manifest[(directory, filename)] = default_info.files_to_run.executable
elif CcInfo in dep:
# cc_binary just takes the final executable from the runfiles.
apex_manifest[("bin", dep.label.name)] = dep[DefaultInfo].files_to_run.executable
apex_content_inputs = []
bazel_apexer_wrapper_manifest = ctx.actions.declare_file("%s_bazel_apexer_wrapper_manifest" % ctx.attr.name)
file_lines = []
# Store the apex file target directory, file name and the path in the source tree in a file.
# This file will be read by the bazel_apexer_wrapper to create the apex input directory.
# Here is an example:
# {etc/tz,tz_version,system/timezone/output_data/version/tz_version}
for (apex_dirname, apex_basename), bazel_input_file in apex_manifest.items():
apex_content_inputs.append(bazel_input_file)
file_lines += [",".join([apex_dirname, apex_basename, bazel_input_file.path])]
ctx.actions.write(bazel_apexer_wrapper_manifest, "\n".join(file_lines))
return apex_content_inputs, bazel_apexer_wrapper_manifest
def _add_libs_32_target(ctx, key, apex_manifest):
if len(ctx.split_attr.native_shared_libs_32.keys()) > 0:
_add_lib_file(DIR_LIB, ctx.split_attr.native_shared_libs_32[key], apex_manifest)
def _add_libs_64_target(ctx, key_32, key_64, apex_manifest):
_add_libs_32_target(ctx, key_32, apex_manifest)
if len(ctx.split_attr.native_shared_libs_64.keys()) > 0:
_add_lib_file(DIR_LIB64, ctx.split_attr.native_shared_libs_64[key_64], apex_manifest)
def _add_lib_file(dir, libs, apex_manifest):
for dep in libs:
apex_cc_info = dep[ApexCcInfo]
for lib_file in apex_cc_info.transitive_shared_libs.to_list():
apex_manifest[(dir, lib_file.basename)] = lib_file
# conv_apex_manifest - Convert the JSON APEX manifest to protobuf, which is needed by apexer.
def _convert_apex_manifest_json_to_pb(ctx, apex_toolchain):
apex_manifest_json = ctx.file.manifest
apex_manifest_pb = ctx.actions.declare_file("apex_manifest.pb")
ctx.actions.run(
outputs = [apex_manifest_pb],
inputs = [ctx.file.manifest],
executable = apex_toolchain.conv_apex_manifest,
arguments = [
"proto",
apex_manifest_json.path,
"-o",
apex_manifest_pb.path,
],
mnemonic = "ConvApexManifest",
)
return apex_manifest_pb
# apexer - generate the APEX file.
def _run_apexer(ctx, apex_toolchain, apex_content_inputs, bazel_apexer_wrapper_manifest, apex_manifest_pb):
# Inputs
file_contexts = ctx.file.file_contexts
apex_key_info = ctx.attr.key[ApexKeyInfo]
privkey = apex_key_info.private_key
pubkey = apex_key_info.public_key
android_jar = apex_toolchain.android_jar
android_manifest = ctx.file.android_manifest
# Outputs
apex_output_file = ctx.actions.declare_file(ctx.attr.name + ".apex.unsigned")
# Arguments
args = ctx.actions.args()
args.add_all(["--manifest", apex_manifest_pb.path])
args.add_all(["--file_contexts", file_contexts.path])
args.add_all(["--key", privkey.path])
args.add_all(["--pubkey", pubkey.path])
min_sdk_version = ctx.attr.min_sdk_version
# TODO(b/215339575): This is a super rudimentary way to convert "current" to a numerical number.
# Generalize this to API level handling logic in a separate Starlark utility, preferably using
# API level maps dumped from api_levels.go
if min_sdk_version == "current":
min_sdk_version = "10000"
args.add_all(["--min_sdk_version", min_sdk_version])
args.add_all(["--bazel_apexer_wrapper_manifest", bazel_apexer_wrapper_manifest])
args.add_all(["--apexer_path", apex_toolchain.apexer])
# apexer needs the list of directories containing all auxilliary tools invoked during
# the creation of an apex
avbtool_files = apex_toolchain.avbtool[DefaultInfo].files_to_run
e2fsdroid_files = apex_toolchain.e2fsdroid[DefaultInfo].files_to_run
mke2fs_files = apex_toolchain.mke2fs[DefaultInfo].files_to_run
resize2fs_files = apex_toolchain.resize2fs[DefaultInfo].files_to_run
apexer_tool_paths = [
# These are built by make_injection
apex_toolchain.apexer.dirname,
# These are real Bazel targets
apex_toolchain.aapt2.dirname,
avbtool_files.executable.dirname,
e2fsdroid_files.executable.dirname,
mke2fs_files.executable.dirname,
resize2fs_files.executable.dirname,
]
args.add_all(["--apexer_tool_path", ":".join(apexer_tool_paths)])
args.add_all(["--apex_output_file", apex_output_file])
if android_manifest != None:
args.add_all(["--android_manifest", android_manifest.path])
inputs = apex_content_inputs + [
bazel_apexer_wrapper_manifest,
apex_manifest_pb,
file_contexts,
privkey,
pubkey,
android_jar,
]
tools = [
avbtool_files,
e2fsdroid_files,
mke2fs_files,
resize2fs_files,
apex_toolchain.aapt2,
apex_toolchain.apexer,
apex_toolchain.sefcontext_compile,
]
if android_manifest != None:
inputs.append(android_manifest)
ctx.actions.run(
inputs = inputs,
tools = tools,
outputs = [apex_output_file],
executable = ctx.executable._bazel_apexer_wrapper,
arguments = [args],
mnemonic = "BazelApexerWrapper",
)
return apex_output_file
# Sign a file with signapk.
def _run_signapk(ctx, unsigned_file, signed_file, private_key, public_key, mnemonic):
# Inputs
inputs = [
unsigned_file,
private_key,
public_key,
ctx.executable._signapk,
]
# Outputs
outputs = [signed_file]
# Arguments
args = ctx.actions.args()
args.add_all(["-a", 4096])
args.add_all(["--align-file-size"])
args.add_all([public_key, private_key])
args.add_all([unsigned_file, signed_file])
ctx.actions.run(
inputs = inputs,
outputs = outputs,
executable = ctx.executable._signapk,
arguments = [args],
mnemonic = mnemonic,
)
return signed_file
# Compress a file with apex_compression_tool.
def _run_apex_compression_tool(ctx, apex_toolchain, input_file, output_file_name):
# Inputs
inputs = [
input_file,
]
avbtool_files = apex_toolchain.avbtool[DefaultInfo].files_to_run
tools = [
avbtool_files,
apex_toolchain.apex_compression_tool,
apex_toolchain.soong_zip,
]
# Outputs
compressed_file = ctx.actions.declare_file(output_file_name)
outputs = [compressed_file]
# Arguments
args = ctx.actions.args()
args.add_all(["compress"])
tool_dirs = [apex_toolchain.soong_zip.dirname, avbtool_files.executable.dirname]
args.add_all(["--apex_compression_tool", ":".join(tool_dirs)])
args.add_all(["--input", input_file])
args.add_all(["--output", compressed_file])
ctx.actions.run(
inputs = inputs,
tools = tools,
outputs = outputs,
executable = apex_toolchain.apex_compression_tool,
arguments = [args],
mnemonic = "BazelApexCompressing",
)
return compressed_file
# See the APEX section in the README on how to use this rule.
def _apex_rule_impl(ctx):
apex_toolchain = ctx.toolchains["//build/bazel/rules/apex:apex_toolchain_type"].toolchain_info
apex_content_inputs, bazel_apexer_wrapper_manifest = _prepare_apexer_wrapper_inputs(ctx)
apex_manifest_pb = _convert_apex_manifest_json_to_pb(ctx, apex_toolchain)
unsigned_apex_output_file = _run_apexer(ctx, apex_toolchain, apex_content_inputs, bazel_apexer_wrapper_manifest, apex_manifest_pb)
apex_cert_info = ctx.attr.certificate[AndroidAppCertificateInfo]
private_key = apex_cert_info.pk8
public_key = apex_cert_info.pem
signed_apex = ctx.outputs.apex_output
_run_signapk(ctx, unsigned_apex_output_file, signed_apex, private_key, public_key, "BazelApexSigning")
output_file = signed_apex
if ctx.attr.compressible:
compressed_apex_output_file = _run_apex_compression_tool(ctx, apex_toolchain, signed_apex, ctx.attr.name + ".capex.unsigned")
signed_capex = ctx.outputs.capex_output
_run_signapk(ctx, compressed_apex_output_file, signed_capex, private_key, public_key, "BazelCompressedApexSigning")
files_to_build = depset([output_file])
return [DefaultInfo(files = files_to_build), ApexInfo()]
_apex = rule(
implementation = _apex_rule_impl,
attrs = {
"manifest": attr.label(allow_single_file = [".json"]),
"android_manifest": attr.label(allow_single_file = [".xml"]),
"file_contexts": attr.label(allow_single_file = True, mandatory = True),
"key": attr.label(providers = [ApexKeyInfo]),
"certificate": attr.label(providers = [AndroidAppCertificateInfo]),
"min_sdk_version": attr.string(default = "current"),
"updatable": attr.bool(default = True),
"installable": attr.bool(default = True),
"compressible": attr.bool(default = False),
"native_shared_libs_32": attr.label_list(
providers = [ApexCcInfo],
aspects = [apex_cc_aspect],
cfg = shared_lib_transition_32,
doc = "The libs compiled for 32-bit",
),
"native_shared_libs_64": attr.label_list(
providers = [ApexCcInfo],
aspects = [apex_cc_aspect],
cfg = shared_lib_transition_64,
doc = "The libs compiled for 64-bit",
),
"binaries": attr.label_list(
providers = [
# The dependency must produce _all_ of the providers in _one_ of these lists.
[ShBinaryInfo], # sh_binary
[StrippedCcBinaryInfo, CcInfo], # cc_binary (stripped)
],
cfg = apex_transition,
),
"prebuilts": attr.label_list(providers = [PrebuiltFileInfo], cfg = apex_transition),
"apex_output": attr.output(doc = "signed .apex output"),
"capex_output": attr.output(doc = "signed .capex output"),
# Required to use apex_transition. This is an acknowledgement to the risks of memory bloat when using transitions.
"_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
"_bazel_apexer_wrapper": attr.label(
cfg = "host",
doc = "The apexer wrapper to avoid the problem where symlinks are created inside apex image.",
executable = True,
default = "//build/bazel/rules/apex:bazel_apexer_wrapper",
),
"_signapk": attr.label(
cfg = "host",
doc = "The signapk tool.",
executable = True,
default = "//build/make/tools/signapk",
),
"_x86_constraint": attr.label(
default = Label("//build/bazel/platforms/arch:x86"),
),
"_x86_64_constraint": attr.label(
default = Label("//build/bazel/platforms/arch:x86_64"),
),
"_arm_constraint": attr.label(
default = Label("//build/bazel/platforms/arch:arm"),
),
"_arm64_constraint": attr.label(
default = Label("//build/bazel/platforms/arch:arm64"),
),
},
toolchains = ["//build/bazel/rules/apex:apex_toolchain_type"],
fragments = ["platform"],
)
def apex(
name,
manifest = "apex_manifest.json",
android_manifest = None,
file_contexts = None,
key = None,
certificate = None,
min_sdk_version = None,
updatable = True,
installable = True,
compressible = False,
native_shared_libs_32 = [],
native_shared_libs_64 = [],
binaries = [],
prebuilts = [],
**kwargs):
"Bazel macro to correspond with the APEX bundle Soong module."
# If file_contexts is not specified, then use the default from //system/sepolicy/apex.
# https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/builder.go;l=259-263;drc=b02043b84d86fe1007afef1ff012a2155172215c
if file_contexts == None:
file_contexts = "//system/sepolicy/apex:" + name + "-file_contexts"
apex_output = name + ".apex"
capex_output = None
if compressible:
capex_output = name + ".capex"
_apex(
name = name,
manifest = manifest,
android_manifest = android_manifest,
file_contexts = file_contexts,
key = key,
certificate = certificate,
min_sdk_version = min_sdk_version,
updatable = updatable,
installable = installable,
compressible = compressible,
native_shared_libs_32 = native_shared_libs_32,
native_shared_libs_64 = native_shared_libs_64,
binaries = binaries,
prebuilts = prebuilts,
# Enables predeclared output builds from command line directly, e.g.
#
# $ bazel build //path/to/module:com.android.module.apex
# $ bazel build //path/to/module:com.android.module.capex
apex_output = apex_output,
capex_output = capex_output,
**kwargs
)

79
bazel/rules/apex/BUILD Normal file
View File

@ -0,0 +1,79 @@
load("//build/bazel/rules/apex:toolchain.bzl", "apex_toolchain")
load("@bazel_skylib//rules:common_settings.bzl", "string_setting", "string_list_setting")
string_setting(
name = "apex_name",
build_setting_default = "",
visibility = ["//visibility:public"],
)
string_setting(
name = "min_sdk_version",
build_setting_default = "",
visibility = ["//visibility:public"],
)
string_list_setting(
name = "apex_direct_deps",
build_setting_default = [],
visibility = ["//visibility:public"],
)
toolchain_type(name = "apex_toolchain_type")
apex_toolchain(
name = "prebuilt_apex_toolchain",
aapt2 = "//prebuilts/sdk/tools:linux/bin/aapt2",
avbtool = "//external/avb:avbtool",
apexer = "@make_injection//:host/linux-x86/bin/apexer",
mke2fs = "//external/e2fsprogs/misc:mke2fs",
resize2fs = "//external/e2fsprogs/resize:resize2fs",
e2fsdroid = "//external/e2fsprogs/contrib/android:e2fsdroid",
sefcontext_compile = "@make_injection//:host/linux-x86/bin/sefcontext_compile",
conv_apex_manifest = "@make_injection//:host/linux-x86/bin/conv_apex_manifest",
android_jar = "//prebuilts/sdk/current:public/android.jar",
apex_compression_tool = "@make_injection//:host/linux-x86/bin/apex_compression_tool",
soong_zip = "//prebuilts/build-tools:linux-x86/bin/soong_zip",
)
toolchain(
name = "prebuilt_apex_toolchain_def",
exec_compatible_with = [
"//build/bazel/platforms/arch:x86_64",
"//build/bazel/platforms/os:linux",
],
target_compatible_with = [
"//build/bazel/platforms/os:android",
],
toolchain = ":prebuilt_apex_toolchain",
toolchain_type = "//build/bazel/rules/apex:apex_toolchain_type",
)
py_binary(
name = "bazel_apexer_wrapper",
srcs = ["bazel_apexer_wrapper.py"],
visibility = ["//visibility:public"],
)
sh_test(
name = "bazel_apexer_wrapper_test",
srcs = ["bazel_apexer_wrapper_test.sh"],
deps = ["@bazel_tools//tools/bash/runfiles"],
data = [
":bazel_apexer_wrapper",
"test.pem",
"//external/avb:avbtool",
"//external/e2fsprogs/contrib/android:e2fsdroid",
"//external/e2fsprogs/misc:mke2fs",
"//external/e2fsprogs/resize:resize2fs",
"//external/e2fsprogs/debugfs:debugfs",
"//prebuilts/build-tools:linux-x86/bin/soong_zip",
"//prebuilts/sdk/tools:linux/bin/aapt2",
"@make_injection//:host/linux-x86/bin/apex_compression_tool",
"@make_injection//:host/linux-x86/bin/apexer",
"@make_injection//:host/linux-x86/bin/conv_apex_manifest",
"@make_injection//:host/linux-x86/bin/deapexer",
"@make_injection//:host/linux-x86/bin/sefcontext_compile",
"//prebuilts/sdk/current:public/android.jar",
]
)

View File

@ -0,0 +1,207 @@
#!/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.
import argparse
import os
import shutil
import subprocess
import sys
import tempfile
def _create_apex(args, work_dir):
image_apex_dir = "image.apex"
# Used for creating canned_fs_config, since every file and dir in the APEX are represented
# by an entry in the fs_config.
apex_subdirs = []
apex_filepaths = []
input_dir = os.path.join(work_dir, image_apex_dir)
os.makedirs(input_dir, exist_ok=True)
bazel_apexer_wrapper_manifest = open(args.bazel_apexer_wrapper_manifest, 'r')
file_lines = bazel_apexer_wrapper_manifest.readlines()
for line in file_lines:
line = line.strip()
if (len(line) == 0):
continue
apex_dirname, apex_filename, bazel_input_file = line.split(",")
full_apex_dirname = "/".join([input_dir, apex_dirname])
os.makedirs(full_apex_dirname, exist_ok=True)
apex_filepath = "/".join([apex_dirname, apex_filename])
apex_filepaths.append(apex_filepath)
apex_subdirs.append(apex_dirname)
full_apex_filepath = "/".join([input_dir, apex_filepath])
# Because Bazel execution root is a symlink forest, all the input files are symlinks, these
# include the dependency files declared in the BUILD files as well as the files declared
# and created in the bzl files. For sandbox runs the former are two or more level symlinks and
# latter are one level symlinks. For non-sandbox runs, the former are one level symlinks
# and the latter are actual files. Here are some examples:
#
# Two level symlinks:
# system/timezone/output_data/version/tz_version ->
# /usr/local/google/home/...out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
# execroot/__main__/system/timezone/output_data/version/tz_version ->
# /usr/local/google/home/.../system/timezone/output_data/version/tz_version
#
# Three level symlinks:
# bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
# /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
# execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
# /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
# execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
# liblibcrypto_stripped.so ->
# /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
# execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
# liblibcrypto_unstripped.so
#
# One level symlinks:
# bazel-out/android_target-fastbuild/bin/system/timezone/apex/apex_manifest.pb ->
# /usr/local/google/home/.../out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
# execroot/__main__/bazel-out/android_target-fastbuild/bin/system/timezone/apex/
# apex_manifest.pb
if os.path.islink(bazel_input_file):
bazel_input_file = os.readlink(bazel_input_file)
# For sandbox run these are the 2nd level symlinks and we need to resolve
while os.path.islink(bazel_input_file) and 'execroot/__main__' in bazel_input_file:
bazel_input_file = os.readlink(bazel_input_file)
shutil.copyfile(bazel_input_file, full_apex_filepath, follow_symlinks=False)
# Make sure subdirs are unique
apex_subdirs_set = set()
for d in apex_subdirs:
apex_subdirs_set.add(d)
# Make sure all the parent dirs of the current subdir are in the set, too
dirs = d.split("/")
for i in range(0, len(dirs)):
apex_subdirs_set.add("/".join(dirs[:i]))
canned_fs_config = _generate_canned_fs_config(work_dir, apex_subdirs_set, apex_filepaths)
# Construct the main apexer command.
cmd = [args.apexer_path]
cmd.append('--verbose')
cmd.append('--force')
cmd.append('--include_build_info')
cmd.extend(['--file_contexts', args.file_contexts])
cmd.extend(['--canned_fs_config', canned_fs_config])
cmd.extend(['--key', args.key])
cmd.extend(['--payload_type', 'image'])
cmd.extend(['--target_sdk_version', '10000'])
cmd.extend(['--payload_fs_type', 'ext4'])
cmd.extend(['--apexer_tool_path', args.apexer_tool_paths])
if args.android_manifest != None:
cmd.extend(['--android_manifest', args.android_manifest])
if args.pubkey != None:
cmd.extend(['--pubkey', args.pubkey])
if args.manifest != None:
cmd.extend(['--manifest', args.manifest])
if args.min_sdk_version != None:
cmd.extend(['--min_sdk_version', args.min_sdk_version])
if args.android_jar_path != None:
cmd.extend(['--android_jar_path', args.android_jar_path])
cmd.append(input_dir)
cmd.append(args.apex_output_file)
popen = subprocess.Popen(cmd)
popen.wait()
return True
# Generate filesystem config. This encodes the filemode, uid, and gid of each
# file in the APEX, including apex_manifest.json and apex_manifest.pb.
#
# NOTE: every file must have an entry.
def _generate_canned_fs_config(work_dir, dirs, filepaths):
with tempfile.NamedTemporaryFile(mode = 'w+', dir=work_dir, delete=False) as canned_fs_config:
config_lines = []
config_lines += ["/ 1000 1000 0755"]
config_lines += ["/apex_manifest.json 1000 1000 0644"]
config_lines += ["/apex_manifest.pb 1000 1000 0644"]
config_lines += ["/" + filepath + " 1000 1000 0644" for filepath in filepaths]
config_lines += ["/" + d + " 0 2000 0755" for d in dirs]
canned_fs_config.write("\n".join(config_lines))
return canned_fs_config.name
def _parse_args(argv):
parser = argparse.ArgumentParser(description='Build an APEX file')
parser.add_argument(
'--manifest',
help='path to the APEX manifest file (.pb)')
parser.add_argument(
'--apex_output_file',
required=True,
help='path to the APEX image file')
parser.add_argument(
'--bazel_apexer_wrapper_manifest',
required=True,
help='path to the manifest file that stores the info about the files to be packaged by apexer')
parser.add_argument(
'--android_manifest',
help='path to the AndroidManifest file. If omitted, a default one is created and used')
parser.add_argument(
'--file_contexts',
required=True,
help='selinux file contexts file.')
parser.add_argument(
'--key',
required=True,
help='path to the private key file.')
parser.add_argument(
'--pubkey',
help='path to the public key file. Used to bundle the public key in APEX for testing.')
parser.add_argument(
'--apexer_path',
required=True,
help='Path to the apexer binary.')
parser.add_argument(
'--apexer_tool_paths',
required=True,
help='Directories containing all the tools used by apexer, separated by ":" character.')
parser.add_argument(
'--min_sdk_version',
help='Default Min SDK version to use for AndroidManifest.xml')
parser.add_argument(
'--android_jar_path',
help='path to use as the source of the android API.')
return parser.parse_args(argv)
def main(argv):
args = _parse_args(argv)
with tempfile.TemporaryDirectory() as work_dir:
success = _create_apex(args, work_dir)
if not success:
sys.exit(1)
if __name__ == '__main__':
main(sys.argv[1:])

View File

@ -0,0 +1,141 @@
#!/bin/bash
# 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.
#
set -xeuo pipefail
apexer_tool_path="${RUNFILES_DIR}/__main__/external/make_injection/host/linux-x86/bin"
avb_tool_path="${RUNFILES_DIR}/__main__/external/avb"
android_jar="${RUNFILES_DIR}/__main__/prebuilts/sdk/current/public/android.jar"
input_dir=$(mktemp -d)
output_dir=$(mktemp -d)
function cleanup {
rm -rf ${input_dir}
rm -rf ${output_dir}
}
trap cleanup ERR
#############################################
# prepare the inputs
#############################################
# Create the input directory with
# 1. a file with random bits
# 2. a file installed sub dir with random bits
# 3. a one-level symlink
# 4. a two-level symlink with "execroot/__main__" in the path
# 5. a two-level sumlink without "execroot/__main__" in the path
# 6. a three-level symlink with "execroot/__main__" in the path
echo "test file1" > "${input_dir}/file1"
echo "test file2" > "${input_dir}/file2"
mkdir -p "${input_dir}/execroot/__main__"
ln -s "${input_dir}/file1" "${input_dir}/one_level_sym"
ln -s "${input_dir}/file2" "${input_dir}/execroot/__main__/middle_sym"
ln -s "${input_dir}/execroot/__main__/middle_sym" "${input_dir}/two_level_sym_in_execroot"
ln -s "${input_dir}/one_level_sym" "${input_dir}/two_level_sym_not_in_execroot"
ln -s "${input_dir}/two_level_sym_in_execroot" "${input_dir}/three_level_sym_in_execroot"
# Create the APEX manifest file
manifest_dir=$(mktemp -d)
manifest_file="${manifest_dir}/apex_manifest.pb"
echo '{"name": "com.android.example.apex", "version": 1}' > "${manifest_dir}/apex_manifest.json"
"${apexer_tool_path}/conv_apex_manifest" proto "${manifest_dir}/apex_manifest.json" -o ${manifest_file}
# Create the file_contexts file
file_contexts_file=$(mktemp)
echo '
(/.*)? u:object_r:root_file:s0
/execroot(/.*)? u:object_r:execroot_file:s0
' > ${file_contexts_file}
output_file="${output_dir}/test.apex"
# Create the wrapper manifest file
bazel_apexer_wrapper_manifest_file=$(mktemp)
echo "
dir1,file1,"${input_dir}/file1"
dir2/dir3,file2,"${input_dir}/file2"
dir4,one_level_sym,"${input_dir}/one_level_sym"
dir5,two_level_sym_in_execroot,"${input_dir}/two_level_sym_in_execroot"
dir6,two_level_sym_not_in_execroot,"${input_dir}/two_level_sym_not_in_execroot"
dir7,three_level_sym_in_execroot,"${input_dir}/three_level_sym_in_execroot"
" > ${bazel_apexer_wrapper_manifest_file}
#############################################
# run bazel_apexer_wrapper
#############################################
"${RUNFILES_DIR}/__main__/build/bazel/rules/apex/bazel_apexer_wrapper" \
--manifest ${manifest_file} \
--file_contexts ${file_contexts_file} \
--key "${RUNFILES_DIR}/__main__/build/bazel/rules/apex/test.pem" \
--apexer_path ${apexer_tool_path} \
--apexer_tool_paths ${apexer_tool_path}:${avb_tool_path} \
--apex_output_file ${output_file} \
--bazel_apexer_wrapper_manifest ${bazel_apexer_wrapper_manifest_file} \
--android_jar_path ${android_jar}
#############################################
# check the result
#############################################
"${apexer_tool_path}/deapexer" --debugfs_path="${apexer_tool_path}/debugfs" extract ${output_file} ${output_dir}
# The expected mounted tree should be something like this:
# /tmp/tmp.9u7ViPlMr7
# ├── apex_manifest.pb
# ├── apex_payload.img
# ├── mnt
# │   ├── apex_manifest.pb
# │   ├── dir1
# │   │   └── file1
# │   ├── dir2
# │   │   └── dir3
# │   │   └── file2
# │   ├── dir4
# │   │   └── one_level_sym
# (one level symlinks always resolve)
# │   ├── dir5
# │   │   └── two_level_sym_in_execroot
# (two level symlink resolve if the path contains execroot/__main__)
# │   ├── dir6
# │   │   └── two_level_sym_not_in_execroot -> /tmp/tmp.evJh21oYGG/file1
# (two level symlink resolve only one level otherwise)
# │   ├── dir7
# │   │   └── three_level_sym_in_execroot
# (three level symlink resolve if the path contains execroot/__main__)
# └── test.apex
# b/215129834:
# https://android-review.googlesource.com/c/platform/system/apex/+/1944264 made
# it such that the hash of non-payload files in the APEX (like
# AndroidManifest.xml) will be included as part of the apex_manifest.pb via the
# apexContainerFilesHash string to ensure that changes to AndroidManifest.xml
# results in changes in content hash for the apex_payload.img. Since this is
# potentially fragile, we skip diffing the apex_manifest.pb, and just check that
# it exists.
test -f "${output_dir}/apex_manifest.pb" || echo "expected apex_manifest.pb to exist"
# check the contents with diff for the rest of the files
diff ${input_dir}/file1 ${output_dir}/dir1/file1
diff ${input_dir}/file2 ${output_dir}/dir2/dir3/file2
diff ${input_dir}/file1 ${output_dir}/dir4/one_level_sym
diff ${input_dir}/file2 ${output_dir}/dir5/two_level_sym_in_execroot
[ `readlink ${output_dir}/dir6/two_level_sym_not_in_execroot` = "${input_dir}/file1" ]
diff ${input_dir}/file2 ${output_dir}/dir7/three_level_sym_in_execroot
cleanup
echo "Passed for all test cases"

130
bazel/rules/apex/cc.bzl Normal file
View File

@ -0,0 +1,130 @@
"""
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.
"""
load("//build/bazel/rules/cc:cc_library_shared.bzl", "CcStubLibrariesInfo")
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
ApexCcInfo = provider(
"Info needed to use CC targets in APEXes",
fields = {
"transitive_shared_libs": "File references to transitive .so libs produced by the CC targets and should be included in the APEX.",
},
)
# Return True if this target provides stubs that is equal to, or below, the
# APEX's min_sdk_level.
#
# These stable ABI libraries are intentionally omitted from APEXes as they are
# provided from another APEX or the platform. By omitting them from APEXes, we
# ensure that there are no multiple copies of such libraries on a device.
def has_cc_stubs(target, ctx):
if ctx.rule.kind != "_cc_library_shared_proxy":
# only _cc_library_shared_proxy contains merged CcStubLibrariesInfo providers
# (a provider aggregating CcStubInfo and CcSharedLibraryInfo)
return False
if len(target[CcStubLibrariesInfo].infos) == 0:
# Not all shared library targets have stubs
return False
# Minimum SDK version supported by the APEX that transitively depends on
# this target.
min_sdk_version = ctx.attr._min_sdk_version[BuildSettingInfo].value
apex_name = ctx.attr._apex_name[BuildSettingInfo].value
available_versions = []
# Check that the shared library has stubs built for (at least) the
# min_sdk_version of the APEX
for stub_info in target[CcStubLibrariesInfo].infos:
stub_version = stub_info["CcStubInfo"].version
available_versions.append(stub_version)
if stub_version <= min_sdk_version:
return True
fail("cannot find a stub lib version for min_sdk_level %s (%s apex)\navailable versions: %s (%s)" %
(min_sdk_version, apex_name, available_versions, target.label))
# Check if this target is specified as a direct dependency of the APEX,
# as opposed to a transitive dependency, as the transitivity impacts
# the files that go into an APEX.
def is_apex_direct_dep(target, ctx):
apex_direct_deps = ctx.attr._apex_direct_deps[BuildSettingInfo].value
return str(target.label) in apex_direct_deps
def _apex_cc_aspect_impl(target, ctx):
# Whether this dep is a direct dep of an APEX or makes a difference in dependency
# traversal, and aggregation of libs that are required from the platform/other APEXes,
# and libs that this APEX will provide to others.
is_direct_dep = is_apex_direct_dep(target, ctx)
if has_cc_stubs(target, ctx):
if is_direct_dep:
# TODO(b/215500321): Mark these libraries as "stub-providing" exports
# of this APEX, which the system and other APEXes can depend on,
# and propagate this list.
pass
else:
# If this is not a direct dep, and stubs are available, don't propagate
# the libraries.
#
# TODO(b/215500321): In a bundled build, ensure that these libraries are
# available on the system either via the system partition, or another APEX
# and propagate this list.
return [ApexCcInfo(transitive_shared_libs = depset())]
shared_object_files = []
# Transitive deps containing shared libraries to be propagated the apex.
transitive_deps = []
rules_propagate_src = ["_bssl_hash_injection", "stripped_shared_library", "versioned_shared_library"]
# Exclude the stripped and unstripped so files
if ctx.rule.kind == "_cc_library_shared_proxy":
for output_file in target[DefaultInfo].files.to_list():
if output_file.extension == "so":
shared_object_files.append(output_file)
if hasattr(ctx.rule.attr, "shared"):
transitive_deps.append(ctx.rule.attr.shared)
elif ctx.rule.kind == "cc_shared_library" and hasattr(ctx.rule.attr, "dynamic_deps"):
# Propagate along the dynamic_deps edge
for dep in ctx.rule.attr.dynamic_deps:
transitive_deps.append(dep)
elif ctx.rule.kind in rules_propagate_src and hasattr(ctx.rule.attr, "src"):
# Propagate along the src edge
transitive_deps.append(ctx.rule.attr.src)
return [
ApexCcInfo(
# TODO: Rely on a split transition across arches to happen earlier
transitive_shared_libs = depset(
shared_object_files,
transitive = [dep[ApexCcInfo].transitive_shared_libs for dep in transitive_deps],
),
),
]
# This aspect is intended to be applied on a apex.native_shared_libs attribute
apex_cc_aspect = aspect(
implementation = _apex_cc_aspect_impl,
attrs = {
"_min_sdk_version": attr.label(default = "//build/bazel/rules/apex:min_sdk_version"),
"_apex_name": attr.label(default = "//build/bazel/rules/apex:apex_name"),
"_apex_direct_deps": attr.label(default = "//build/bazel/rules/apex:apex_direct_deps"),
},
attr_aspects = ["dynamic_deps", "shared", "src"],
# TODO: Have this aspect also propagate along attributes of native_shared_libs?
)

View File

@ -0,0 +1,260 @@
"""
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.
"""
load("//build/bazel/rules:apex.bzl", "ApexInfo")
def _arch_transition_impl(settings, attr):
"""Implementation of arch_transition.
Four archs are included for mainline modules: x86, x86_64, arm and arm64.
"""
return {
"x86": {
"//command_line_option:platforms": "//build/bazel/platforms:android_x86",
},
"x86_64": {
"//command_line_option:platforms": "//build/bazel/platforms:android_x86_64",
},
"arm": {
"//command_line_option:platforms": "//build/bazel/platforms:android_arm",
},
"arm64": {
"//command_line_option:platforms": "//build/bazel/platforms:android_arm64",
},
}
# Multi-arch transition.
arch_transition = transition(
implementation = _arch_transition_impl,
inputs = [],
outputs = [
"//command_line_option:platforms",
],
)
# Arch to ABI map
_arch_abi_map = {
"arm64": "arm64-v8a",
"arm": "armeabi-v7a",
"x86_64": "x86_64",
"x86": "x86",
}
def _apex_proto_convert(ctx, arch, module_name, apex_file):
"""Run 'aapt2 convert' to convert resource files to protobuf format."""
# Inputs
inputs = [
apex_file,
ctx.executable._aapt2,
]
# Outputs
filename = apex_file.basename
pos_dot = filename.rindex(".")
proto_convert_file = ctx.actions.declare_file("/".join([
module_name,
arch,
filename[:pos_dot] + ".pb" + filename[pos_dot:]]))
outputs = [proto_convert_file]
# Arguments
args = ctx.actions.args()
args.add_all(["convert"])
args.add_all(["--output-format", "proto"])
args.add_all([apex_file])
args.add_all(["-o", proto_convert_file.path])
ctx.actions.run(
inputs = inputs,
outputs = outputs,
executable = ctx.executable._aapt2,
arguments = [args],
mnemonic = "ApexProtoConvert",
)
return proto_convert_file
def _apex_base_file(ctx, arch, module_name, apex_proto_file):
"""Run zip2zip to transform the apex file the expected directory structure
with all files that will be included in the base module of aab file."""
# Inputs
inputs = [
apex_proto_file,
ctx.executable._zip2zip,
]
# Outputs
base_file = ctx.actions.declare_file("/".join([module_name, arch, module_name + ".base"]))
outputs = [base_file]
# Arguments
args = ctx.actions.args()
args.add_all(["-i", apex_proto_file])
args.add_all(["-o", base_file])
abi = _arch_abi_map[arch]
args.add_all([
"apex_payload.img:apex/%s.img" % abi,
"apex_build_info.pb:apex/%s.build_info.pb" % abi,
"apex_manifest.json:root/apex_manifest.json",
"apex_manifest.pb:root/apex_manifest.pb",
"AndroidManifest.xml:manifest/AndroidManifest.xml",
"assets/NOTICE.html.gz:assets/NOTICE.html.gz",
])
ctx.actions.run(
inputs = inputs,
outputs = outputs,
executable = ctx.executable._zip2zip,
arguments = [args],
mnemonic = "ApexBaseFile",
)
return base_file
def _build_bundle_config(ctx, arch, module_name):
"""Create bundle_config.json as configuration for running bundletool."""
file_content = {
"compression": {
"uncompressed_glob": [
"apex_payload.img",
"apex_manifest.*",
],
},
"apex_config": {},
}
bundle_config_file = ctx.actions.declare_file("/".join([module_name, "bundle_config.json"]))
ctx.actions.write(bundle_config_file, json.encode(file_content))
return bundle_config_file
def _merge_base_files(ctx, module_name, base_files):
"""Run merge_zips to merge all files created for each arch by _apex_base_file."""
# Inputs
inputs = base_files + [ctx.executable._merge_zips]
# Outputs
merged_base_file = ctx.actions.declare_file(module_name + "/" + module_name + ".zip")
outputs = [merged_base_file]
# Arguments
args = ctx.actions.args()
args.add_all(["--ignore-duplicates"])
args.add_all([merged_base_file])
args.add_all(base_files)
ctx.actions.run(
inputs = inputs,
outputs = outputs,
executable = ctx.executable._merge_zips,
arguments = [args],
mnemonic = "ApexMergeBaseFiles",
)
return merged_base_file
def _apex_bundle(ctx, module_name, merged_base_file, bundle_config_file):
"""Run bundletool to create the aab file."""
# Inputs
inputs = [
bundle_config_file,
merged_base_file,
ctx.executable._bundletool,
]
# Outputs
bundle_file = ctx.actions.declare_file(module_name + "/" + module_name + ".aab")
outputs = [bundle_file]
# Arguments
args = ctx.actions.args()
args.add_all(["build-bundle"])
args.add_all(["--config", bundle_config_file])
args.add_all(["--modules", merged_base_file])
args.add_all(["--output", bundle_file])
ctx.actions.run(
inputs = inputs,
outputs = outputs,
executable = ctx.executable._bundletool,
arguments = [args],
mnemonic = "ApexBundleFile",
)
return bundle_file
def _apex_aab_impl(ctx):
"""Implementation of apex_aab rule, which drives the process of creating aab
file from apex files created for each arch."""
apex_base_files = []
bundle_config_file = None
module_name = ctx.attr.mainline_module[0].label.name
for arch in ctx.split_attr.mainline_module:
apex_file = ctx.split_attr.mainline_module[arch].files.to_list()[0]
proto_convert_file = _apex_proto_convert(ctx, arch, module_name, apex_file)
base_file = _apex_base_file(ctx, arch, module_name, proto_convert_file)
apex_base_files.append(base_file)
# It is assumed that the bundle config is the same for all products.
if bundle_config_file == None:
bundle_config_file = _build_bundle_config(ctx, arch, module_name)
merged_base_file = _merge_base_files(ctx, module_name, apex_base_files)
bundle_file = _apex_bundle(ctx, module_name, merged_base_file, bundle_config_file)
return [DefaultInfo(files = depset([bundle_file]))]
# apex_aab rule creates Android Apk Bundle (.aab) file of the APEX specified in mainline_module.
# There is no equivalent Soong module, and it is currently done in shell script by
# invoking Soong multiple times.
apex_aab = rule(
implementation = _apex_aab_impl,
attrs = {
"mainline_module": attr.label(
mandatory = True,
cfg = arch_transition,
providers = [ApexInfo],
doc = "The label of a mainline module target",
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
doc = "Allow transition.",
),
"_zipper": attr.label(
cfg = "host",
executable = True,
default = "@bazel_tools//tools/zip:zipper",
),
"_aapt2": attr.label(
allow_single_file = True,
cfg = "host",
executable = True,
default = "//prebuilts/sdk/tools:linux/bin/aapt2",
),
"_merge_zips": attr.label(
allow_single_file = True,
cfg = "host",
executable = True,
default = "//prebuilts/build-tools:linux-x86/bin/merge_zips",
),
"_zip2zip": attr.label(
allow_single_file = True,
cfg = "host",
executable = True,
default = "//prebuilts/build-tools:linux-x86/bin/zip2zip",
),
"_bundletool": attr.label(
cfg = "host",
executable = True,
default = "//prebuilts/bundletool",
),
},
)

51
bazel/rules/apex/test.pem Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAt4iSfTF+e2khGQf0bUzTMwWFsgaiQbwQB3cvyBlE9XekFXUt
GdOEhC2J0p+930UoF6gjjRRrgGF+8K5iV1m3oEbB3qGz6UUOurvVkt4tq96e/Q5a
ogCOZEuWHjZfs2tQUVNJJtptIp9+0cM768vdf+qnK2JNFIhBqSY0FhjVljKevMcM
w2tWFRZnKPQ3JoRnWqi5CIauQtBcWRFKIApyf41uHGMjpQRd8aTGeLXBRTi/yD73
HltuKwSF2SXpj1F+9j4stqskQvipjQnid/Wb+nN3CNgyrGuRrtGvz71WWYcK3DLM
jvGLOl06QrN6a7ZfLUN4qQjJ6Is5SLTSw/sfFE7Fpcbg6/Geh+jSvChuo6EUtzoX
Qu42HsVXhrJLQ9/AVTWNmGc9IDr4PMtDiQc4FN8MOpUtR6V/zwrZFoeR3PHl9Z7v
uTxLIcQLIott0mAjPhbNgbFBs5HP1Z8TfFcyZWpShlx+aM1V2mzYQ7sgsWjFKMSQ
wIUk/YZ9QK/H5WKjC5M0yxueCU0ocvWFaAZ4RyS/r/SUyQpvyNXNwUsdp1a8sNxp
LP9U7FG64C+T791yoQJ0sKVbts5SEu/Tojw6miYbH6Fspdo2xxfCbrv6SAbkjlct
afOnEepgTlHet0G+y0N7OZRJ9WRGyLJNgGjmmDy9XSYGAykwwe4Fv348D0cCAwEA
AQKCAgBuFra/78NNpXbb++CK+20oCqTyb3Y+dd8rizuXDElH8Fb1JA9EkZLIckRc
mcMbvPDal9mTU29UV6b8Ga4VdVRnCGpb76TqRKkcK3Vlnm3IzUWSx1xoFmtTD9/h
CX6IMdPApHOZoaWbAg7hJfm4a9XWV9ukc1eG/GBeZPMTWhwr9vsugztNsQG2rnR8
pVi7eupAADrVOWwn2bG7H1rWM04Q4rXswy7rWd48BzmhyGxA6FRpehNjGzbPCOx8
n3gkpp7Ad/T8MVYT8fJKDmbQy/ue1EnPfVeQAwok0dRiiNDV7OH/yVzYVVzNSoSa
4+uH1qHqlbE3u3TZT0GyMfzG38f4scsbvG/AhH1fuPsy4QcWyLlMV6KUnk3KPc3Q
yOeRR82qndQMTYQ5/PFiilk7cNbTU0OBjuNpu/t1LIE2J2gGZ5Jw+g2NGtM/xsgC
jOahpRYvZB8fZ/bSjirwwmSSU+v0ZoPDHtt75R/QxqwPG2jai8kaGr7GEXWJfrfv
CktMnb6LoCyNiiiZSMUgdDHOQEkVNmt9fxiVaxsaIL4BygropwlD4WbuyRMevfYz
EffvvmaqC24zJi8WzDszCNLgP/piNhXDyxZX+KaQXj0Do/tzWBBkO0OO6mVGOkX2
6dadXfhOIggWO8K2lKCUKwWMO9LaKwSwZ4gzcc1a+U9rpE8kUQKCAQEA8lBGLzOL
Ht8+d13SY+NdPbL6qGvoqsKd5BfIhaNbH04Cp2zQs2TWySxmV47df03pGUpQOCKn
tFRxoczUrf1gfFDCCC95+A/crls8QJHG+MScTBH5U8Q0s9ReUo/0xaa55u77x5uS
0fAtdnOdqP8/pf1fSXUJvyLW85LWdkge1c7jk7I5MnWVO2Ak9/GkuRgITSSgVdBa
kr8nU1BCzDY0gOTWo5J1+NqqVH2eYfEI621iD4SAE3n2JrCC4K/Nt2enEJwup2TR
ym15g9nClicUQP5Y67eDfqTZu1d0I0Ezl1tL8UPxcLI+ucN4V6KL8RvqTVMnGX/R
s1FwkPVMQ6dKaQKCAQEAweZeggcSFukr+tTbnzDAHxg4YqiR+30wo7i8NadGu6W/
EiAdcCdmZYMI9KKc+B/N3cuFqBnaSd7VM7XvINdwZRanRj56Ya8LvQMi0S9YPiRn
T4TXC3EeewN5+SSO0Dkw83tW1PLqgSINy5ijBs5lGoIYMCC+GSA2DuRBiPpcfhqJ
kmC9uFQvrsge8CC8Sb1wHCr0Wz34qhPoTff6ZV8wm11Jkb5+tT7PMS5Ft0sEBsxV
R1JFtLNs0k/YpMb4/OrZFZZSIFCTUVPvHQ1/5BwumVnolBC4LORCaSk1xUOydU9h
bZd4qzIpFteGLGGRT6nEWC1YejLAvcFHVJiKs1F2LwKCAQEAzgnwA8bCLvgIt5rx
gLod2I7NkFRhPIHLm92VRf0HSHEe1Jo0Q7Yk5F56j00NjmgDItwLpg/hpfZ/wOLY
nTFrz4kj0636+jESprcxXn4WQAV+GTjXVqDpZ1fW9EEwEriYLoNbV/kzOIwPPD9G
+iJATrZJRb7dEMdhGy/qaB0fCxKmdDoBZKSSxjAUfzfbpv+GX4IbS5ykx07+81q1
0crtjgQHdoLdCUN1ve4qtIEt4nHaBfPWq7jy0ycXwlH6jE74wajsCq4xrPy1bKXH
TcHg+PrNRXF/wDoQYboVKL0ST0r0IixxqjAGIhLRy0KN1/CypBlmj8od12oSW1AZ
DxW6sQKCAQEAtIMW8M5MVO/2dam8XFMySMBvncl5PjuqEIFnFjwIaaFAZEtpnIPR
nCeFKtpIb+aL7TQP1hNbWPIOYfm6CUUH6dRRHeAEZvRjZS+KNlxxNkkFtM3itVA2
JCd0YjFakxbrL4FfsRgEoPtnBGexPiDflvIOOqAA2btXGD3/lNofSXbDJHbTqMsX
KQw9YSfYon2t5UtH+bmTyiKGXi/B+KXJxpnuZ7SEmY9DrHF7jcxUj0+jBKbfJf70
DEcxVRW3rx2jw6kSA+t/enM9ZDqxGVfzOeit0UpPa9uEyAoJeQAxH20rMq+VMyub
fRxgWOjsMtHFbKGqgPjG3uEU2vi4B4CLGQKCAQEA2Mr5f2AXPR8jca1+Id+CxZpU
bgMML7gW31L4lGX9Teo9z+zSdN7sIwqe42Zla1N9wda8p5ribnJxwRdxcPL8bid5
LLlls4xXD/jQCQCFL90X59Tm6VD6tm1VyCjL44nRwAqP4vJObSB5rTqJYtkfVmnp
KERF5P0i5yv4Oox0ZOsThou9jtyl1dS50Td0Urhp4LhPdmpDPUq25K1sDDfnGFm6
IcMPkVznRPUoKQCG9DSQcQqttkSV9Po+qfLa3aHtdndfe88Gd9uom8bsAMTZAfSZ
D4YhqBHSLWrxvtQ8GxkaPITJv7hocwssdFRUj5/UJKJBgUXPBXEXh+fxlDaGQQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,70 @@
"""
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.
"""
ApexToolchainInfo = provider(
doc = "APEX toolchain",
fields = [
"aapt2",
"avbtool",
"apexer",
"mke2fs",
"resize2fs",
"e2fsdroid",
"sefcontext_compile",
"conv_apex_manifest",
"android_jar",
"apex_compression_tool",
"soong_zip",
],
)
def _apex_toolchain_impl(ctx):
toolchain_info = platform_common.ToolchainInfo(
toolchain_info = ApexToolchainInfo(
aapt2 = ctx.file.aapt2,
avbtool = ctx.attr.avbtool,
apexer = ctx.file.apexer,
mke2fs = ctx.attr.mke2fs,
resize2fs = ctx.attr.resize2fs,
e2fsdroid = ctx.attr.e2fsdroid,
sefcontext_compile = ctx.file.sefcontext_compile,
conv_apex_manifest = ctx.file.conv_apex_manifest,
android_jar = ctx.file.android_jar,
apex_compression_tool = ctx.file.apex_compression_tool,
soong_zip = ctx.file.soong_zip,
),
)
return [toolchain_info]
apex_toolchain = rule(
implementation = _apex_toolchain_impl,
attrs = {
"aapt2": attr.label(allow_single_file = True, cfg = "host", executable = True),
"avbtool": attr.label(cfg = "host", executable = True),
"apexer": attr.label(allow_single_file = True, cfg = "host", executable = True),
"mke2fs": attr.label(cfg = "host", executable = True),
"resize2fs": attr.label(cfg = "host", executable = True),
"e2fsdroid": attr.label(cfg = "host", executable = True),
"sefcontext_compile": attr.label(allow_single_file = True, cfg = "host", executable = True),
"conv_apex_manifest": attr.label(allow_single_file = True, cfg = "host", executable = True),
"android_jar": attr.label(allow_single_file = True, cfg = "host"),
"apex_compression_tool": attr.label(allow_single_file = True, cfg = "host", executable = True),
# soong_zip is added as a dependency of apex_compression_tool which uses
# soong_zip to compress APEX files. avbtool is also used in apex_compression tool
# and has been added to apex toolchain previously.
"soong_zip": attr.label(allow_single_file = True, cfg = "host", executable = True),
},
)

Some files were not shown because too many files have changed in this diff Show More