Merge "Revert "Disable BTI for now.""
diff --git a/.gitignore b/.gitignore
index a09c56d..45884c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/.idea
+*.iml
diff --git a/Android.bp b/Android.bp
index 7c50047..63de015 100644
--- a/Android.bp
+++ b/Android.bp
@@ -50,6 +50,7 @@
name: "device_kernel_headers",
vendor: true,
recovery_available: true,
+ min_sdk_version: "apex_inherit",
}
cc_genrule {
@@ -60,6 +61,9 @@
linux_bionic: {
enabled: true,
},
+ linux_musl: {
+ enabled: false,
+ },
linux_glibc: {
enabled: false,
},
@@ -81,6 +85,9 @@
linux_bionic: {
enabled: true,
},
+ linux_musl: {
+ enabled: false,
+ },
linux_glibc: {
enabled: false,
},
@@ -112,3 +119,14 @@
dexpreopt_systemserver_check {
name: "dexpreopt_systemserver_check",
}
+
+// buildinfo.prop contains common properties for system/build.prop, like ro.build.version.*
+buildinfo_prop {
+ name: "buildinfo.prop",
+
+ // not installable because this will be included to system/build.prop
+ installable: false,
+
+ // Currently, only microdroid can refer to buildinfo.prop
+ visibility: ["//packages/modules/Virtualization/microdroid"],
+}
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..caa2a95
--- /dev/null
+++ b/METADATA
@@ -0,0 +1 @@
+name: "Android"
diff --git a/OWNERS b/OWNERS
index 937a243..0662016 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,11 +2,13 @@
# approving build related projects.
# AMER
-ahumesky@google.com
+agespino@google.com
alexmarquez@google.com
asmundak@google.com
ccross@android.com
+colefaust@google.com
cparsons@google.com
+dacek@google.com
delmerico@google.com
dwillemsen@google.com
eakammer@google.com
@@ -16,13 +18,12 @@
spandandas@google.com
tradical@google.com
usta@google.com
+vinhdaitran@google.com
weiwli@google.com
yudiliu@google.com
-yuntaoxu@google.com
# APAC
jingwen@google.com
-ruperts@google.com
# EMEA
hansson@google.com
diff --git a/README.md b/README.md
index 18c6604..caffd3d 100644
--- a/README.md
+++ b/README.md
@@ -550,6 +550,26 @@
and produces build rules. The build rules are collected by blueprint and
written to a [ninja](http://ninja-build.org) build file.
+## Environment Variables Config File
+
+Soong can optionally load environment variables from a pre-specified
+configuration file during startup. These environment variables can be used
+to control the behavior of the build. For example, these variables can determine
+whether remote-execution should be used for the build or not.
+
+The `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment variable specifies the
+directory in which the config file should be searched for. The
+`ANDROID_BUILD_ENVIRONMENT_CONFIG` variable determines the name of the config
+file to be searched for within the config directory. For example, the following
+build comand will load `ENV_VAR_1` and `ENV_VAR_2` environment variables from
+the `example_config.json` file inside the `build/soong` directory.
+
+```
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong \
+ ANDROID_BUILD_ENVIRONMENT_CONFIG=example_config \
+ build/soong/soong_ui.bash
+```
+
## Other documentation
* [Best Practices](docs/best_practices.md)
diff --git a/android/Android.bp b/android/Android.bp
index da36959..65332b2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -8,6 +8,7 @@
deps: [
"blueprint",
"blueprint-bootstrap",
+ "blueprint-metrics",
"sbox_proto",
"soong",
"soong-android-soongconfig",
@@ -16,7 +17,10 @@
"soong-remoteexec",
"soong-response",
"soong-shared",
+ "soong-starlark-format",
"soong-ui-metrics_proto",
+ "soong-android-allowlists",
+
"golang-protobuf-proto",
"golang-protobuf-encoding-prototext",
@@ -32,7 +36,9 @@
"bazel.go",
"bazel_handler.go",
"bazel_paths.go",
+ "buildinfo_prop.go",
"config.go",
+ "config_bp2build.go",
"csuite_config.go",
"deapexer.go",
"defaults.go",
@@ -43,6 +49,7 @@
"expand.go",
"filegroup.go",
"fixture.go",
+ "gen_notice.go",
"hooks.go",
"image.go",
"license.go",
@@ -93,12 +100,14 @@
"bazel_handler_test.go",
"bazel_test.go",
"config_test.go",
+ "config_bp2build_test.go",
"csuite_config_test.go",
"defaults_test.go",
"depset_test.go",
"deptag_test.go",
"expand_test.go",
"fixture_test.go",
+ "gen_notice_test.go",
"license_kind_test.go",
"license_test.go",
"licenses_test.go",
diff --git a/android/allowlists/Android.bp b/android/allowlists/Android.bp
new file mode 100644
index 0000000..05cffc1
--- /dev/null
+++ b/android/allowlists/Android.bp
@@ -0,0 +1,25 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-android-allowlists",
+ pkgPath: "android/soong/android/allowlists",
+ srcs: [
+ "allowlists.go",
+ ],
+}
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
new file mode 100644
index 0000000..a5e7cd6
--- /dev/null
+++ b/android/allowlists/allowlists.go
@@ -0,0 +1,471 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package allowlists
+
+// Configuration to decide if modules in a directory should default to true/false for bp2build_available
+type Bp2BuildConfig map[string]BazelConversionConfigEntry
+type BazelConversionConfigEntry int
+
+const (
+ // iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
+ // which can also mean that the key doesn't exist in a lookup.
+
+ // all modules in this package and subpackages default to bp2build_available: true.
+ // allows modules to opt-out.
+ Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
+
+ // all modules in this package (not recursively) default to bp2build_available: true.
+ // allows modules to opt-out.
+ Bp2BuildDefaultTrue
+
+ // all modules in this package (not recursively) default to bp2build_available: false.
+ // allows modules to opt-in.
+ Bp2BuildDefaultFalse
+)
+
+var (
+ Bp2buildDefaultConfig = Bp2BuildConfig{
+ "art/libartpalette": Bp2BuildDefaultTrueRecursively,
+ "art/libdexfile": Bp2BuildDefaultTrueRecursively,
+ "art/libnativebridge": Bp2BuildDefaultTrueRecursively,
+ "art/runtime": Bp2BuildDefaultTrueRecursively,
+ "art/tools": Bp2BuildDefaultTrue,
+ "bionic": Bp2BuildDefaultTrueRecursively,
+ "bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
+ "build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
+ "build/bazel/examples/soong_config_variables": Bp2BuildDefaultTrueRecursively,
+ "build/bazel/examples/python": Bp2BuildDefaultTrueRecursively,
+ "build/bazel/examples/gensrcs": Bp2BuildDefaultTrueRecursively,
+ "build/make/target/product/security": Bp2BuildDefaultTrue,
+ "build/make/tools/signapk": Bp2BuildDefaultTrue,
+ "build/make/tools/zipalign": Bp2BuildDefaultTrueRecursively,
+ "build/soong": Bp2BuildDefaultTrue,
+ "build/soong/cc/libbuildversion": Bp2BuildDefaultTrue, // Skip tests subdir
+ "build/soong/cc/ndkstubgen": Bp2BuildDefaultTrue,
+ "build/soong/cc/symbolfile": Bp2BuildDefaultTrue,
+ "build/soong/linkerconfig": Bp2BuildDefaultTrueRecursively,
+ "build/soong/scripts": Bp2BuildDefaultTrueRecursively,
+ "cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
+ "development/apps/DevelopmentSettings": Bp2BuildDefaultTrue,
+ "development/apps/Fallback": Bp2BuildDefaultTrue,
+ "development/apps/WidgetPreview": Bp2BuildDefaultTrue,
+ "development/samples/BasicGLSurfaceView": Bp2BuildDefaultTrue,
+ "development/samples/BluetoothChat": Bp2BuildDefaultTrue,
+ "development/samples/BrokenKeyDerivation": Bp2BuildDefaultTrue,
+ "development/samples/Compass": Bp2BuildDefaultTrue,
+ "development/samples/ContactManager": Bp2BuildDefaultTrue,
+ "development/samples/FixedGridLayout": Bp2BuildDefaultTrue,
+ "development/samples/HelloEffects": Bp2BuildDefaultTrue,
+ "development/samples/Home": Bp2BuildDefaultTrue,
+ "development/samples/HoneycombGallery": Bp2BuildDefaultTrue,
+ "development/samples/JetBoy": Bp2BuildDefaultTrue,
+ "development/samples/KeyChainDemo": Bp2BuildDefaultTrue,
+ "development/samples/LceDemo": Bp2BuildDefaultTrue,
+ "development/samples/LunarLander": Bp2BuildDefaultTrue,
+ "development/samples/MultiResolution": Bp2BuildDefaultTrue,
+ "development/samples/MultiWindow": Bp2BuildDefaultTrue,
+ "development/samples/NotePad": Bp2BuildDefaultTrue,
+ "development/samples/Obb": Bp2BuildDefaultTrue,
+ "development/samples/RSSReader": Bp2BuildDefaultTrue,
+ "development/samples/ReceiveShareDemo": Bp2BuildDefaultTrue,
+ "development/samples/SearchableDictionary": Bp2BuildDefaultTrue,
+ "development/samples/SipDemo": Bp2BuildDefaultTrue,
+ "development/samples/SkeletonApp": Bp2BuildDefaultTrue,
+ "development/samples/Snake": Bp2BuildDefaultTrue,
+ "development/samples/SpellChecker/": Bp2BuildDefaultTrueRecursively,
+ "development/samples/ThemedNavBarKeyboard": Bp2BuildDefaultTrue,
+ "development/samples/ToyVpn": Bp2BuildDefaultTrue,
+ "development/samples/TtsEngine": Bp2BuildDefaultTrue,
+ "development/samples/USB/AdbTest": Bp2BuildDefaultTrue,
+ "development/samples/USB/MissileLauncher": Bp2BuildDefaultTrue,
+ "development/samples/VoiceRecognitionService": Bp2BuildDefaultTrue,
+ "development/samples/VoicemailProviderDemo": Bp2BuildDefaultTrue,
+ "development/samples/WiFiDirectDemo": Bp2BuildDefaultTrue,
+ "development/sdk": Bp2BuildDefaultTrueRecursively,
+ "external/aac": Bp2BuildDefaultTrueRecursively,
+ "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
+ "external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
+ "external/auto/common": Bp2BuildDefaultTrueRecursively,
+ "external/auto/service": Bp2BuildDefaultTrueRecursively,
+ "external/boringssl": Bp2BuildDefaultTrueRecursively,
+ "external/bouncycastle": Bp2BuildDefaultTrue,
+ "external/brotli": Bp2BuildDefaultTrue,
+ "external/conscrypt": Bp2BuildDefaultTrue,
+ "external/e2fsprogs": Bp2BuildDefaultTrueRecursively,
+ "external/eigen": Bp2BuildDefaultTrueRecursively,
+ "external/erofs-utils": Bp2BuildDefaultTrueRecursively,
+ "external/error_prone": Bp2BuildDefaultTrueRecursively,
+ "external/f2fs-tools": Bp2BuildDefaultTrue,
+ "external/flac": Bp2BuildDefaultTrueRecursively,
+ "external/fmtlib": Bp2BuildDefaultTrueRecursively,
+ "external/google-benchmark": Bp2BuildDefaultTrueRecursively,
+ "external/googletest": Bp2BuildDefaultTrueRecursively,
+ "external/gwp_asan": Bp2BuildDefaultTrueRecursively,
+ "external/hamcrest": Bp2BuildDefaultTrueRecursively,
+ "external/icu": Bp2BuildDefaultTrueRecursively,
+ "external/icu/android_icu4j": Bp2BuildDefaultFalse, // java rules incomplete
+ "external/icu/icu4j": Bp2BuildDefaultFalse, // java rules incomplete
+ "external/jarjar": Bp2BuildDefaultTrueRecursively,
+ "external/javapoet": Bp2BuildDefaultTrueRecursively,
+ "external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
+ "external/jsoncpp": Bp2BuildDefaultTrueRecursively,
+ "external/junit": Bp2BuildDefaultTrueRecursively,
+ "external/libavc": Bp2BuildDefaultTrueRecursively,
+ "external/libcap": Bp2BuildDefaultTrueRecursively,
+ "external/libcxx": Bp2BuildDefaultTrueRecursively,
+ "external/libcxxabi": Bp2BuildDefaultTrueRecursively,
+ "external/libevent": Bp2BuildDefaultTrueRecursively,
+ "external/libgav1": Bp2BuildDefaultTrueRecursively,
+ "external/libhevc": Bp2BuildDefaultTrueRecursively,
+ "external/libmpeg2": Bp2BuildDefaultTrueRecursively,
+ "external/libpng": Bp2BuildDefaultTrueRecursively,
+ "external/lz4/lib": Bp2BuildDefaultTrue,
+ "external/lzma/C": Bp2BuildDefaultTrueRecursively,
+ "external/mdnsresponder": Bp2BuildDefaultTrueRecursively,
+ "external/minijail": Bp2BuildDefaultTrueRecursively,
+ "external/pcre": Bp2BuildDefaultTrueRecursively,
+ "external/protobuf": Bp2BuildDefaultTrueRecursively,
+ "external/python/six": Bp2BuildDefaultTrueRecursively,
+ "external/rappor": Bp2BuildDefaultTrueRecursively,
+ "external/scudo": Bp2BuildDefaultTrueRecursively,
+ "external/selinux/libselinux": Bp2BuildDefaultTrueRecursively,
+ "external/selinux/libsepol": Bp2BuildDefaultTrueRecursively,
+ "external/zlib": Bp2BuildDefaultTrueRecursively,
+ "external/zopfli": Bp2BuildDefaultTrueRecursively,
+ "external/zstd": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/media/codecs/g711/decoder": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/services/minijail": Bp2BuildDefaultTrueRecursively,
+ "frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue,
+ "frameworks/base/startop/apps/test": Bp2BuildDefaultTrue,
+ "frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/arect": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/math": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/nativebase": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/opengl/tests/gl2_cameraeye": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/gl2_java": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/testLatency": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/testPauseResume": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/testViewport": Bp2BuildDefaultTrue,
+ "frameworks/proto_logging/stats/stats_log_api_gen": Bp2BuildDefaultTrueRecursively,
+ "libnativehelper": Bp2BuildDefaultTrueRecursively,
+ "packages/apps/DevCamera": Bp2BuildDefaultTrue,
+ "packages/apps/HTMLViewer": Bp2BuildDefaultTrue,
+ "packages/apps/Protips": Bp2BuildDefaultTrue,
+ "packages/modules/StatsD/lib/libstatssocket": Bp2BuildDefaultTrueRecursively,
+ "packages/modules/adb": Bp2BuildDefaultTrue,
+ "packages/modules/adb/apex": Bp2BuildDefaultTrue,
+ "packages/modules/adb/crypto": Bp2BuildDefaultTrueRecursively,
+ "packages/modules/adb/libs": Bp2BuildDefaultTrueRecursively,
+ "packages/modules/adb/pairing_auth": Bp2BuildDefaultTrueRecursively,
+ "packages/modules/adb/pairing_connection": Bp2BuildDefaultTrueRecursively,
+ "packages/modules/adb/proto": Bp2BuildDefaultTrueRecursively,
+ "packages/modules/adb/tls": Bp2BuildDefaultTrueRecursively,
+ "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultTrue,
+ "packages/screensavers/Basic": Bp2BuildDefaultTrue,
+ "packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultTrue,
+ "prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
+ "prebuilts/tools/common/m2": Bp2BuildDefaultTrue,
+ "system/apex": Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
+ "system/apex/apexer": Bp2BuildDefaultTrue,
+ "system/apex/libs": Bp2BuildDefaultTrueRecursively,
+ "system/apex/proto": Bp2BuildDefaultTrueRecursively,
+ "system/apex/tools": Bp2BuildDefaultTrueRecursively,
+ "system/core/debuggerd": Bp2BuildDefaultTrueRecursively,
+ "system/core/diagnose_usb": Bp2BuildDefaultTrueRecursively,
+ "system/core/libasyncio": Bp2BuildDefaultTrue,
+ "system/core/libcrypto_utils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libpackagelistparser": Bp2BuildDefaultTrueRecursively,
+ "system/core/libprocessgroup": Bp2BuildDefaultTrue,
+ "system/core/libprocessgroup/cgrouprc": Bp2BuildDefaultTrue,
+ "system/core/libprocessgroup/cgrouprc_format": Bp2BuildDefaultTrue,
+ "system/core/libsystem": Bp2BuildDefaultTrueRecursively,
+ "system/core/libutils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libvndksupport": Bp2BuildDefaultTrueRecursively,
+ "system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
+ "system/libartpalette": Bp2BuildDefaultTrueRecursively,
+ "system/libbase": Bp2BuildDefaultTrueRecursively,
+ "system/libfmq": Bp2BuildDefaultTrue,
+ "system/libhwbinder": Bp2BuildDefaultTrueRecursively,
+ "system/libprocinfo": Bp2BuildDefaultTrue,
+ "system/libziparchive": Bp2BuildDefaultTrueRecursively,
+ "system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+ "system/media/audio": Bp2BuildDefaultTrueRecursively,
+ "system/memory/libion": Bp2BuildDefaultTrueRecursively,
+ "system/memory/libmemunreachable": Bp2BuildDefaultTrueRecursively,
+ "system/sepolicy/apex": Bp2BuildDefaultTrueRecursively,
+ "system/timezone/apex": Bp2BuildDefaultTrueRecursively,
+ "system/timezone/output_data": Bp2BuildDefaultTrueRecursively,
+ "system/unwinding/libbacktrace": Bp2BuildDefaultTrueRecursively,
+ "system/unwinding/libunwindstack": Bp2BuildDefaultTrueRecursively,
+ "tools/apksig": Bp2BuildDefaultTrue,
+ "tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively,
+ }
+
+ Bp2buildKeepExistingBuildFile = map[string]bool{
+ // This is actually build/bazel/build.BAZEL symlinked to ./BUILD
+ ".":/*recursive = */ false,
+
+ // build/bazel/examples/apex/... BUILD files should be generated, so
+ // build/bazel is not recursive. Instead list each subdirectory under
+ // build/bazel explicitly.
+ "build/bazel":/* recursive = */ false,
+ "build/bazel/ci/dist":/* recursive = */ false,
+ "build/bazel/examples/android_app":/* recursive = */ true,
+ "build/bazel/examples/java":/* recursive = */ true,
+ "build/bazel/bazel_skylib":/* recursive = */ true,
+ "build/bazel/rules":/* recursive = */ true,
+ "build/bazel/rules_cc":/* recursive = */ true,
+ "build/bazel/scripts":/* recursive = */ true,
+ "build/bazel/tests":/* recursive = */ true,
+ "build/bazel/platforms":/* recursive = */ true,
+ "build/bazel/product_variables":/* recursive = */ true,
+ "build/bazel/vendor/google":/* recursive = */ true,
+ "build/bazel_common_rules":/* recursive = */ true,
+ // build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
+ "build/make/tools":/* recursive = */ false,
+ "build/pesto":/* recursive = */ true,
+
+ // external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
+ // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
+ "external/bazelbuild-rules_android":/* recursive = */ true,
+ "external/bazel-skylib":/* recursive = */ true,
+ "external/guava":/* recursive = */ true,
+ "external/jsr305":/* recursive = */ true,
+ "frameworks/ex/common":/* recursive = */ true,
+
+ "packages/apps/Music":/* recursive = */ true,
+ "packages/apps/QuickSearchBox":/* recursive = */ true,
+ "packages/apps/WallpaperPicker":/* recursive = */ false,
+
+ "prebuilts/bundletool":/* recursive = */ true,
+ "prebuilts/gcc":/* recursive = */ true,
+ "prebuilts/build-tools":/* recursive = */ true,
+ "prebuilts/jdk/jdk11":/* recursive = */ false,
+ "prebuilts/sdk":/* recursive = */ false,
+ "prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
+ "prebuilts/sdk/current/support":/* recursive = */ false,
+ "prebuilts/sdk/tools":/* recursive = */ false,
+ "prebuilts/r8":/* recursive = */ false,
+ }
+
+ Bp2buildModuleAlwaysConvertList = []string{
+ // cc mainline modules
+ "code_coverage.policy",
+ "code_coverage.policy.other",
+ "codec2_soft_exports",
+ "com.android.media.swcodec-ld.config.txt",
+ "com.android.media.swcodec-mediaswcodec.rc",
+ "flatbuffer_headers",
+ "gemmlowp_headers",
+ "gl_headers",
+ "libaudioclient_aidl_conversion_util",
+ "libaudioutils_fixedfft",
+ "libbluetooth-types-header",
+ "libcodec2_headers",
+ "libcodec2_internal",
+ "libdmabufheap",
+ "libgui_bufferqueue_sources",
+ "libnativeloader-headers",
+ "libsync",
+
+ //external/avb
+ "avbtool",
+ "libavb",
+ "avb_headers",
+
+ //external/fec
+ "libfec_rs",
+
+ //system/core/libsparse
+ "libsparse",
+
+ //system/extras/ext4_utils
+ "libext4_utils",
+
+ //system/extras/libfec
+ "libfec",
+
+ //system/extras/squashfs_utils
+ "libsquashfs_utils",
+
+ //system/extras/verity/fec
+ "fec",
+
+ //packages/apps/Car/libs/car-ui-lib/car-ui-androidx
+ // genrule dependencies for java_imports
+ "car-ui-androidx-annotation-nodeps",
+ "car-ui-androidx-collection-nodeps",
+ "car-ui-androidx-core-common-nodeps",
+ "car-ui-androidx-lifecycle-common-nodeps",
+ "car-ui-androidx-constraintlayout-solver-nodeps",
+ }
+
+ Bp2buildModuleTypeAlwaysConvertList = []string{
+ "java_import",
+ "java_import_host",
+ }
+
+ Bp2buildModuleDoNotConvertList = []string{
+ // cc bugs
+ "libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
+ "gen-kotlin-build-file.py", // TODO(b/198619163) module has same name as source
+ "libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
+ "linkerconfig", "mdnsd", // TODO(b/202876379): has arch-variant static_executable
+ "linker", // TODO(b/228316882): cc_binary uses link_crt
+ "libdebuggerd", // TODO(b/228314770): support product variable-specific header_libs
+ "versioner", // TODO(b/228313961): depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
+ "f2fs.fibmap", // ld.lld: error: undefined symbol: _IO
+ "f2fscrypt", // TODO(b/234340806): error: incompatible integer to pointer conversion initializing 'FILE *' (aka 'struct _IO_FILE *') with an expression of type 'int', and error: incomplete definition of type 'struct mntent'
+ "apexer", "apexer_test", // Requires aapt2
+ "apexer_test_host_tools",
+ "host_apex_verifier",
+
+ // java bugs
+ "libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
+
+ // python protos
+ "libprotobuf-python", // Has a handcrafted alternative
+
+ // genrule incompatibilities
+ "brotli-fuzzer-corpus", // TODO(b/202015218): outputs are in location incompatible with bazel genrule handling.
+ "platform_tools_properties", "build_tools_source_properties", // TODO(b/203369847): multiple genrules in the same package creating the same file
+
+ // aar support
+ "prebuilt_car-ui-androidx-core-common", // TODO(b/224773339), genrule dependency creates an .aar, not a .jar
+ "prebuilt_platform-robolectric-4.4-prebuilt", // aosp/1999250, needs .aar support in Jars
+ "prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
+
+ // path property for filegroups
+ "conscrypt", // TODO(b/210751803), we don't handle path property for filegroups
+ "conscrypt-for-host", // TODO(b/210751803), we don't handle path property for filegroups
+ "host-libprotobuf-java-full", // TODO(b/210751803), we don't handle path property for filegroups
+ "libprotobuf-internal-protos", // TODO(b/210751803), we don't handle path property for filegroups
+ "libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
+ "libprotobuf-java-full", // TODO(b/210751803), we don't handle path property for filegroups
+ "libprotobuf-java-util-full", // TODO(b/210751803), we don't handle path property for filegroups
+ "auto_value_plugin_resources", // TODO(b/210751803), we don't handle path property for filegroups
+
+ // go deps:
+ "analyze_bcpf", // depends on bpmodify a blueprint_go_binary.
+ "apex-protos", // depends on soong_zip, a go binary
+ "generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // depends on unconverted modules: soong_zip
+ "host_bionic_linker_asm", // depends on extract_linker, a go binary.
+ "host_bionic_linker_script", // depends on extract_linker, a go binary.
+ "libc_musl_sysroot_bionic_arch_headers", // depends on soong_zip
+ "libc_musl_sysroot_zlib_headers", // depends on soong_zip and zip2zip
+ "libc_musl_sysroot_bionic_headers", // 218405924, depends on soong_zip and generates duplicate srcs
+ "libc_musl_sysroot_libc++_headers", "libc_musl_sysroot_libc++abi_headers", // depends on soong_zip, zip2zip
+ "robolectric-sqlite4java-native", // depends on soong_zip, a go binary
+ "robolectric_tzdata", // depends on soong_zip, a go binary
+
+ // rust support
+ "libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
+
+ // unconverted deps
+ "CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib
+ "abb", // depends on unconverted modules: libcmd, libbinder
+ "adb", // depends on unconverted modules: AdbWinApi, libandroidfw, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
+ "android_icu4j_srcgen", // depends on unconverted modules: currysrc
+ "android_icu4j_srcgen_binary", // depends on unconverted modules: android_icu4j_srcgen, currysrc
+ "apex_manifest_proto_java", // b/210751803, depends on libprotobuf-java-full
+ "art-script", // depends on unconverted modules: dalvikvm, dex2oat
+ "bin2c_fastdeployagent", // depends on unconverted modules: deployagent
+ "com.android.runtime", // depends on unconverted modules: bionic-linker-config, linkerconfig
+ "conv_linker_config", // depends on unconverted modules: linker_config_proto
+ "currysrc", // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
+ "dex2oat-script", // depends on unconverted modules: dex2oat
+ "generated_android_icu4j_resources", // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+ "generated_android_icu4j_test_resources", // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+ "host-libprotobuf-java-nano", // b/220869005, depends on libprotobuf-java-nano
+ "libadb_host", // depends on unconverted modules: AdbWinApi, libopenscreen-discovery, libopenscreen-platform-impl, libusb
+ "libart", // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
+ "libart-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libartbase-art-gtest
+ "libart_headers", // depends on unconverted modules: art_libartbase_headers
+ "libartd", // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
+ "libartd-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest
+ "libdebuggerd_handler", // depends on unconverted module libdebuggerd_handler_core
+ "libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
+ "libdexfile", // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
+ "libdexfile_static", // depends on unconverted modules: libartbase, libdexfile
+ "libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
+ "libfastdeploy_host", // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
+ "libgmock_main_ndk", // depends on unconverted modules: libgtest_ndk_c++
+ "libgmock_ndk", // depends on unconverted modules: libgtest_ndk_c++
+ "libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libnativetesthelper_jni, libgmock_ndk
+ "libnativetesthelper_jni", // depends on unconverted modules: libgtest_ndk_c++
+ "libprotobuf-java-nano", // b/220869005, depends on non-public_current SDK
+ "libstatslog", // depends on unconverted modules: libstatspull, statsd-aidl-ndk, libbinder_ndk
+ "libstatslog_art", // depends on unconverted modules: statslog_art.cpp, statslog_art.h
+ "linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
+ "pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
+ "robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
+ "static_crasher", // depends on unconverted modules: libdebuggerd_handler
+ "stats-log-api-gen", // depends on unconverted modules: libstats_proto_host
+ "statslog.cpp", "statslog.h", "statslog.rs", // depends on unconverted modules: stats-log-api-gen
+ "statslog_art.cpp", "statslog_art.h", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
+ "timezone-host", // depends on unconverted modules: art.module.api.annotations
+ "truth-host-prebuilt", // depends on unconverted modules: truth-prebuilt
+ "truth-prebuilt", // depends on unconverted modules: asm-7.0, guava
+
+ // b/215723302; awaiting tz{data,_version} to then rename targets conflicting with srcs
+ "tzdata",
+ "tz_version",
+ }
+
+ Bp2buildCcLibraryStaticOnlyList = []string{}
+
+ MixedBuildsDisabledList = []string{
+ "art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+
+ "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
+ "minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
+
+ "cap_names.h", // TODO(b/204913827) runfiles need to be handled in mixed builds
+ "libcap", // TODO(b/204913827) runfiles need to be handled in mixed builds
+ "libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
+
+ // Depends on libprotobuf-cpp-*
+ "libadb_pairing_connection",
+ "libadb_pairing_connection_static",
+ "libadb_pairing_server", "libadb_pairing_server_static",
+
+ // TODO(b/204811222) support suffix in cc_binary
+ "acvp_modulewrapper",
+ "android.hardware.media.c2@1.0-service-v4l2",
+ "app_process",
+ "bar_test",
+ "bench_cxa_atexit",
+ "bench_noop",
+ "bench_noop_nostl",
+ "bench_noop_static",
+ "boringssl_self_test",
+ "boringssl_self_test_vendor",
+ "bssl",
+ "cavp",
+ "crash_dump",
+ "crasher",
+ "libcxx_test_template",
+ "linker",
+ "memory_replay",
+ "native_bridge_guest_linker",
+ "native_bridge_stub_library_defaults",
+ "noop",
+ "simpleperf_ndk",
+ "toybox-static",
+ "zlib_bench",
+ }
+)
diff --git a/android/androidmk.go b/android/androidmk.go
index 72b6584..6b675a6 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -148,6 +148,14 @@
// without worrying about the variables being mixed up in the actual mk file.
// 3. Makes troubleshooting and spotting errors easier.
entryOrder []string
+
+ // Provides data typically stored by Context objects that are commonly needed by
+ //AndroidMkEntries objects.
+ entryContext AndroidMkEntriesContext
+}
+
+type AndroidMkEntriesContext interface {
+ Config() Config
}
type AndroidMkExtraEntriesContext interface {
@@ -280,6 +288,8 @@
// The contributions to the dist.
type distContributions struct {
+ // Path to license metadata file.
+ licenseMetadataFile Path
// List of goals and the dist copy instructions.
copiesForGoals []*copiesForGoals
}
@@ -356,6 +366,8 @@
// Collate the contributions this module makes to the dist.
distContributions := &distContributions{}
+ distContributions.licenseMetadataFile = amod.licenseMetadataFile
+
// Iterate over this module's dist structs, merged from the dist and dists properties.
for _, dist := range amod.Dists() {
// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
@@ -408,10 +420,19 @@
}
}
+ ext := filepath.Ext(dest)
+ suffix := ""
if dist.Suffix != nil {
- ext := filepath.Ext(dest)
- suffix := *dist.Suffix
- dest = strings.TrimSuffix(dest, ext) + suffix + ext
+ suffix = *dist.Suffix
+ }
+
+ productString := ""
+ if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
+ productString = fmt.Sprintf("_%s", a.entryContext.Config().DeviceProduct())
+ }
+
+ if suffix != "" || productString != "" {
+ dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
}
if dist.Dir != nil {
@@ -439,6 +460,10 @@
for _, c := range d.copies {
ret = append(
ret,
+ fmt.Sprintf("$(if $(strip $(ALL_TARGETS.%s.META_LIC)),,$(eval ALL_TARGETS.%s.META_LIC := %s))\n",
+ c.from.String(), c.from.String(), distContributions.licenseMetadataFile.String()))
+ ret = append(
+ ret,
fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", d.goals, c.from.String(), c.dest))
}
}
@@ -478,6 +503,7 @@
}
func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+ a.entryContext = ctx
a.EntryMap = make(map[string][]string)
amod := mod.(Module)
base := amod.base()
@@ -560,7 +586,7 @@
}
}
- if !base.InRamdisk() && !base.InVendorRamdisk() {
+ if !base.InVendorRamdisk() {
a.AddPaths("LOCAL_FULL_INIT_RC", base.initRcPaths)
}
if len(base.vintfFragmentsPaths) > 0 {
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index ecfb008..ae2187f 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -50,6 +50,8 @@
func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ m.base().licenseMetadataFile = PathForOutput(ctx, "meta_lic")
+
// If the dist_output_file: true then create an output file that is stored in
// the OutputFile property of the AndroidMkEntry.
if proptools.BoolDefault(m.properties.Dist_output_file, true) {
@@ -148,6 +150,9 @@
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("custom", customModuleFactory)
}),
+ FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.DeviceProduct = proptools.StringPtr("bar")
+ }),
FixtureWithRootAndroidBp(bp),
).RunTest(t)
@@ -195,10 +200,13 @@
},
}
+ dc.licenseMetadataFile = PathForTesting("meta_lic")
makeOutput := generateDistContributionsForMake(dc)
assertStringEquals(t, `.PHONY: my_goal
+$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))
$(call dist-for-goals,my_goal,one.out:one.out)
+$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))
$(call dist-for-goals,my_goal,two.out:other.out)
`, strings.Join(makeOutput, ""))
}
@@ -240,18 +248,26 @@
expectedAndroidMkLines := []string{
".PHONY: my_second_goal\n",
+ "$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
+ "$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
".PHONY: my_third_goal\n",
+ "$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
".PHONY: my_fourth_goal\n",
+ "$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
".PHONY: my_fifth_goal\n",
+ "$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
".PHONY: my_sixth_goal\n",
+ "$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
".PHONY: my_goal my_other_goal\n",
+ "$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
+ "$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))\n",
"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
}
@@ -271,7 +287,7 @@
)
}
for idx, line := range androidMkLines {
- expectedLine := expectedAndroidMkLines[idx]
+ expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic", module.base().licenseMetadataFile.String())
if line != expectedLine {
t.Errorf(
"Expected AndroidMk line to be '%s', got '%s'",
@@ -400,6 +416,25 @@
},
})
+ testHelper(t, "append-artifact-with-product", `
+ custom {
+ name: "foo",
+ dist: {
+ targets: ["my_goal"],
+ append_artifact_with_product: true,
+ }
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one_bar.out"),
+ },
+ },
+ },
+ })
+
testHelper(t, "dists-with-tag", `
custom {
name: "foo",
diff --git a/android/apex.go b/android/apex.go
index cf1bcfe..019efdd 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -58,9 +58,6 @@
// to true.
UsePlatformApis bool
- // The list of SDK modules that the containing apexBundle depends on.
- RequiredSdks SdkRefs
-
// List of Apex variant names that this module is associated with. This initially is the
// same as the `ApexVariationName` field. Then when multiple apex variants are merged in
// mergeApexVariations, ApexInfo struct of the merged variant holds the list of apexBundles
@@ -110,12 +107,6 @@
// thus wouldn't be merged.
func (i ApexInfo) mergedName(ctx PathContext) string {
name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
- for _, sdk := range i.RequiredSdks {
- name += "_" + sdk.Name + "_" + sdk.Version
- }
- if i.UsePlatformApis {
- name += "_private"
- }
return name
}
@@ -546,10 +537,9 @@
merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...)
merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
- if merged[index].UsePlatformApis != apexInfo.UsePlatformApis {
- panic(fmt.Errorf("variants having different UsePlatformApis can't be merged"))
- }
- merged[index].UsePlatformApis = apexInfo.UsePlatformApis
+ // Platform APIs is allowed for this module only when all APEXes containing
+ // the module are with `use_platform_apis: true`.
+ merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis
} else {
seen[mergedName] = len(merged)
apexInfo.ApexVariationName = mergedName
@@ -854,52 +844,28 @@
}
return list
}(map[string]int{
- "android.net.ipsec.ike": 30,
- "androidx.annotation_annotation-nodeps": 29,
- "androidx.arch.core_core-common-nodeps": 29,
- "androidx.collection_collection-nodeps": 29,
- "androidx.collection_collection-ktx-nodeps": 30,
- "androidx.concurrent_concurrent-futures-nodeps": 30,
- "androidx.lifecycle_lifecycle-common-java8-nodeps": 30,
- "androidx.lifecycle_lifecycle-common-nodeps": 29,
- "androidx.room_room-common-nodeps": 30,
"androidx-constraintlayout_constraintlayout-solver-nodeps": 29,
"apache-commons-compress": 29,
"bouncycastle_ike_digests": 30,
"brotli-java": 29,
- "captiveportal-lib": 28,
- "error_prone_annotations": 30,
"flatbuffer_headers": 30,
- "framework-permission": 30,
"gemmlowp_headers": 30,
- "guava-listenablefuture-prebuilt-jar": 30,
"ike-internals": 30,
- "kotlinx-coroutines-android": 28,
- "kotlinx-coroutines-android-nodeps": 30,
- "kotlinx-coroutines-core": 28,
- "kotlinx-coroutines-core-nodeps": 30,
"libbrotli": 30,
"libcrypto_static": 30,
"libeigen": 30,
"liblz4": 30,
"libmdnssd": 30,
- "libneuralnetworks_common": 30,
- "libneuralnetworks_headers": 30,
- "libneuralnetworks": 30,
"libprocpartition": 30,
"libprotobuf-java-lite": 30,
"libprotoutil": 30,
"libtextclassifier_hash_headers": 30,
"libtextclassifier_hash_static": 30,
"libtflite_kernel_utils": 30,
- "libwatchdog": 29,
"libzstd": 30,
- "metrics-constants-protos": 28,
"net-utils-framework-common": 29,
- "permissioncontroller-statsd": 28,
"philox_random_headers": 30,
"philox_random": 30,
- "service-permission": 30,
"tensorflow_headers": 30,
"xz-java": 29,
})
diff --git a/android/apex_test.go b/android/apex_test.go
index 60a639b..0bf4c9c 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,10 @@
{
name: "single",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"foo", "apex10000"},
@@ -45,25 +45,25 @@
{
name: "merge",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
+ {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
wantAliases: [][2]string{
- {"bar", "apex10000_baz_1"},
- {"foo", "apex10000_baz_1"},
+ {"bar", "apex10000"},
+ {"foo", "apex10000"},
},
},
{
name: "don't merge version",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex30", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
- {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex30"},
@@ -73,11 +73,11 @@
{
name: "merge updatable",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -85,32 +85,17 @@
},
},
{
- name: "don't merge sdks",
- in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
- },
- wantMerged: []ApexInfo{
- {"apex10000_baz_2", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
- {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- },
- wantAliases: [][2]string{
- {"bar", "apex10000_baz_2"},
- {"foo", "apex10000_baz_1"},
- },
- },
- {
name: "don't merge when for prebuilt_apex",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
// This one should not be merged in with the others because it is for
// a prebuilt_apex.
- {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+ {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
- {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+ {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -118,17 +103,30 @@
},
},
{
- name: "don't merge different UsePlatformApis",
+ name: "merge different UsePlatformApis but don't allow using platform api",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_private", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
- {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
- {"bar", "apex10000_private"},
+ {"bar", "apex10000"},
+ {"foo", "apex10000"},
+ },
+ },
+ {
+ name: "merge same UsePlatformApis and allow using platform api",
+ in: []ApexInfo{
+ {"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ },
+ wantMerged: []ApexInfo{
+ {"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ },
+ wantAliases: [][2]string{
+ {"bar", "apex10000"},
{"foo", "apex10000"},
},
},
diff --git a/android/api_levels.go b/android/api_levels.go
index 926d297..8163894 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -18,6 +18,9 @@
"encoding/json"
"fmt"
"strconv"
+
+ "android/soong/bazel"
+ "android/soong/starlark_fmt"
)
func init() {
@@ -321,6 +324,7 @@
"Q": 29,
"R": 30,
"S": 31,
+ "S-V2": 32,
}
// TODO: Differentiate "current" and "future".
@@ -364,6 +368,7 @@
"Q": 29,
"R": 30,
"S": 31,
+ "S-V2": 32,
}
for i, codename := range config.PlatformVersionActiveCodenames() {
apiLevelsMap[codename] = previewAPILevelBase + i
@@ -378,3 +383,21 @@
apiLevelsJson := GetApiLevelsJson(ctx)
createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
}
+
+func printApiLevelsStarlarkDict(config Config) string {
+ apiLevelsMap := GetApiLevelsMap(config)
+ valDict := make(map[string]string, len(apiLevelsMap))
+ for k, v := range apiLevelsMap {
+ valDict[k] = strconv.Itoa(v)
+ }
+ return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func StarlarkApiLevelConfigs(config Config) string {
+ return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
+_api_levels = %s
+
+api_levels = _api_levels
+`, printApiLevelsStarlarkDict(config),
+ )
+}
diff --git a/android/arch.go b/android/arch.go
index 96a4cbf..f732a7d 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -22,6 +22,7 @@
"strings"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
@@ -829,7 +830,7 @@
const maxArchTypeNameSize = 500
// Convert the type to a new set of types that contains only the arch-specific properties
- // (those that are tagged with `android:"arch_specific"`), and sharded into multiple types
+ // (those that are tagged with `android:"arch_variant"`), and sharded into multiple types
// to keep the runtime-generated names under the limit.
propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
@@ -864,6 +865,10 @@
archVariant := variantReplacer.Replace(archVariant)
variants = append(variants, proptools.FieldNameForProperty(archVariant))
}
+ for _, cpuVariant := range cpuVariants[arch] {
+ cpuVariant := variantReplacer.Replace(cpuVariant)
+ variants = append(variants, proptools.FieldNameForProperty(cpuVariant))
+ }
for _, feature := range archFeatures[arch] {
feature := variantReplacer.Replace(feature)
variants = append(variants, proptools.FieldNameForProperty(feature))
@@ -904,6 +909,7 @@
"Glibc",
"Musl",
"Linux",
+ "Host_linux",
"Not_windows",
"Arm_on_x86",
"Arm_on_x86_64",
@@ -917,19 +923,38 @@
for _, archType := range osArchTypeMap[os] {
targets = append(targets, GetCompoundTargetField(os, archType))
- // Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
+ // Also add the special "linux_<arch>", "bionic_<arch>" , "glibc_<arch>", and
+ // "musl_<arch>" property structs.
if os.Linux() {
target := "Linux_" + archType.Name
if !InList(target, targets) {
targets = append(targets, target)
}
}
+ if os.Linux() && os.Class == Host {
+ target := "Host_linux_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
if os.Bionic() {
target := "Bionic_" + archType.Name
if !InList(target, targets) {
targets = append(targets, target)
}
}
+ if os == Linux {
+ target := "Glibc_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
+ if os == LinuxMusl {
+ target := "Musl_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
}
}
@@ -1144,6 +1169,14 @@
}
}
+ if os.Linux() && os.Class == Host {
+ field := "Host_linux"
+ prefix := "target.host_linux"
+ if linuxProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+ mergePropertyStruct(ctx, genProps, linuxProperties)
+ }
+ }
+
if os.Bionic() {
field := "Bionic"
prefix := "target.bionic"
@@ -1166,14 +1199,6 @@
if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
mergePropertyStruct(ctx, genProps, bionicProperties)
}
-
- // Special case: to ease the transition from glibc to musl, apply linux_glibc
- // properties (which has historically mean host linux) to musl variants.
- field = "Linux_glibc"
- prefix = "target.linux_glibc"
- if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
- mergePropertyStruct(ctx, genProps, bionicProperties)
- }
}
// Handle target OS properties in the form:
@@ -1379,11 +1404,17 @@
result = append(result, osArchProperties)
}
+ if os == Linux {
+ field := "Glibc_" + archType.Name
+ userFriendlyField := "target.glibc_" + "_" + archType.Name
+ if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+ result = append(result, osArchProperties)
+ }
+ }
+
if os == LinuxMusl {
- // Special case: to ease the transition from glibc to musl, apply linux_glibc
- // properties (which has historically mean host linux) to musl variants.
- field := "Linux_glibc_" + archType.Name
- userFriendlyField := "target.linux_glibc_" + archType.Name
+ field := "Musl_" + archType.Name
+ userFriendlyField := "target.musl_" + "_" + archType.Name
if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
result = append(result, osArchProperties)
}
@@ -1487,23 +1518,32 @@
targets := make(map[OsType][]Target)
var targetErr error
- addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string,
- nativeBridgeEnabled NativeBridgeSupport, nativeBridgeHostArchName *string,
- nativeBridgeRelativePath *string) {
+ type targetConfig struct {
+ os OsType
+ archName string
+ archVariant *string
+ cpuVariant *string
+ abi []string
+ nativeBridgeEnabled NativeBridgeSupport
+ nativeBridgeHostArchName *string
+ nativeBridgeRelativePath *string
+ }
+
+ addTarget := func(target targetConfig) {
if targetErr != nil {
return
}
- arch, err := decodeArch(os, archName, archVariant, cpuVariant, abi)
+ arch, err := decodeArch(target.os, target.archName, target.archVariant, target.cpuVariant, target.abi)
if err != nil {
targetErr = err
return
}
- nativeBridgeRelativePathStr := String(nativeBridgeRelativePath)
- nativeBridgeHostArchNameStr := String(nativeBridgeHostArchName)
+ nativeBridgeRelativePathStr := String(target.nativeBridgeRelativePath)
+ nativeBridgeHostArchNameStr := String(target.nativeBridgeHostArchName)
// Use guest arch as relative install path by default
- if nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
+ if target.nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
nativeBridgeRelativePathStr = arch.ArchType.String()
}
@@ -1511,11 +1551,11 @@
// the currently configured build machine (either because the OS is different or because of
// the unsupported arch)
hostCross := false
- if os.Class == Host {
+ if target.os.Class == Host {
var osSupported bool
- if os == config.BuildOS {
+ if target.os == config.BuildOS {
osSupported = true
- } else if config.BuildOS.Linux() && os.Linux() {
+ } else if config.BuildOS.Linux() && target.os.Linux() {
// LinuxBionic and Linux are compatible
osSupported = true
} else {
@@ -1537,11 +1577,11 @@
}
}
- targets[os] = append(targets[os],
+ targets[target.os] = append(targets[target.os],
Target{
- Os: os,
+ Os: target.os,
Arch: arch,
- NativeBridge: nativeBridgeEnabled,
+ NativeBridge: target.nativeBridgeEnabled,
NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
NativeBridgeRelativePath: nativeBridgeRelativePathStr,
HostCross: hostCross,
@@ -1553,11 +1593,11 @@
}
// The primary host target, which must always exist.
- addTarget(config.BuildOS, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: config.BuildOS, archName: *variables.HostArch, nativeBridgeEnabled: NativeBridgeDisabled})
// An optional secondary host target.
if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
- addTarget(config.BuildOS, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: config.BuildOS, archName: *variables.HostSecondaryArch, nativeBridgeEnabled: NativeBridgeDisabled})
}
// Optional cross-compiled host targets, generally Windows.
@@ -1572,45 +1612,65 @@
}
// The primary cross-compiled host target.
- addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: crossHostOs, archName: *variables.CrossHostArch, nativeBridgeEnabled: NativeBridgeDisabled})
// An optional secondary cross-compiled host target.
if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
- addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: crossHostOs, archName: *variables.CrossHostSecondaryArch, nativeBridgeEnabled: NativeBridgeDisabled})
}
}
// Optional device targets
if variables.DeviceArch != nil && *variables.DeviceArch != "" {
// The primary device target.
- addTarget(Android, *variables.DeviceArch, variables.DeviceArchVariant,
- variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.DeviceArch,
+ archVariant: variables.DeviceArchVariant,
+ cpuVariant: variables.DeviceCpuVariant,
+ abi: variables.DeviceAbi,
+ nativeBridgeEnabled: NativeBridgeDisabled,
+ })
// An optional secondary device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
- addTarget(Android, *variables.DeviceSecondaryArch,
- variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
- variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.DeviceSecondaryArch,
+ archVariant: variables.DeviceSecondaryArchVariant,
+ cpuVariant: variables.DeviceSecondaryCpuVariant,
+ abi: variables.DeviceSecondaryAbi,
+ nativeBridgeEnabled: NativeBridgeDisabled,
+ })
}
// An optional NativeBridge device target.
if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
- addTarget(Android, *variables.NativeBridgeArch,
- variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
- variables.NativeBridgeAbi, NativeBridgeEnabled, variables.DeviceArch,
- variables.NativeBridgeRelativePath)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.NativeBridgeArch,
+ archVariant: variables.NativeBridgeArchVariant,
+ cpuVariant: variables.NativeBridgeCpuVariant,
+ abi: variables.NativeBridgeAbi,
+ nativeBridgeEnabled: NativeBridgeEnabled,
+ nativeBridgeHostArchName: variables.DeviceArch,
+ nativeBridgeRelativePath: variables.NativeBridgeRelativePath,
+ })
}
// An optional secondary NativeBridge device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
variables.NativeBridgeSecondaryArch != nil && *variables.NativeBridgeSecondaryArch != "" {
- addTarget(Android, *variables.NativeBridgeSecondaryArch,
- variables.NativeBridgeSecondaryArchVariant,
- variables.NativeBridgeSecondaryCpuVariant,
- variables.NativeBridgeSecondaryAbi,
- NativeBridgeEnabled,
- variables.DeviceSecondaryArch,
- variables.NativeBridgeSecondaryRelativePath)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.NativeBridgeSecondaryArch,
+ archVariant: variables.NativeBridgeSecondaryArchVariant,
+ cpuVariant: variables.NativeBridgeSecondaryCpuVariant,
+ abi: variables.NativeBridgeSecondaryAbi,
+ nativeBridgeEnabled: NativeBridgeEnabled,
+ nativeBridgeHostArchName: variables.DeviceSecondaryArch,
+ nativeBridgeRelativePath: variables.NativeBridgeSecondaryRelativePath,
+ })
}
}
@@ -1670,11 +1730,11 @@
}
// decodeArchSettings converts a list of archConfigs into a list of Targets for the given OsType.
-func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
+func decodeAndroidArchSettings(archConfigs []archConfig) ([]Target, error) {
var ret []Target
for _, config := range archConfigs {
- arch, err := decodeArch(os, config.arch, &config.archVariant,
+ arch, err := decodeArch(Android, config.arch, &config.archVariant,
&config.cpuVariant, config.abi)
if err != nil {
return nil, err
@@ -1714,6 +1774,18 @@
a.CpuVariant = ""
}
+ if a.ArchVariant != "" {
+ if validArchVariants := archVariants[archType]; !InList(a.ArchVariant, validArchVariants) {
+ return Arch{}, fmt.Errorf("[%q] unknown arch variant %q, support variants: %q", archType, a.ArchVariant, validArchVariants)
+ }
+ }
+
+ if a.CpuVariant != "" {
+ if validCpuVariants := cpuVariants[archType]; !InList(a.CpuVariant, validCpuVariants) {
+ return Arch{}, fmt.Errorf("[%q] unknown cpu variant %q, support variants: %q", archType, a.CpuVariant, validCpuVariants)
+ }
+ }
+
// Filter empty ABIs out of the list.
for i := 0; i < len(a.Abi); i++ {
if a.Abi[i] == "" {
@@ -1722,14 +1794,9 @@
}
}
- if a.ArchVariant == "" {
- // Set ArchFeatures from the default arch features.
- if featureMap, ok := defaultArchFeatureMap[os]; ok {
- a.ArchFeatures = featureMap[archType]
- }
- } else {
- // Set ArchFeatures from the arch type.
- if featureMap, ok := archFeatureMap[archType]; ok {
+ // Set ArchFeatures from the arch type. for Android OS, other os-es do not specify features
+ if os == Android {
+ if featureMap, ok := androidArchFeatureMap[archType]; ok {
a.ArchFeatures = featureMap[a.ArchVariant]
}
}
@@ -2059,6 +2126,7 @@
linuxStructs := getTargetStructs(ctx, archProperties, "Linux")
bionicStructs := getTargetStructs(ctx, archProperties, "Bionic")
hostStructs := getTargetStructs(ctx, archProperties, "Host")
+ hostLinuxStructs := getTargetStructs(ctx, archProperties, "Host_linux")
hostNotWindowsStructs := getTargetStructs(ctx, archProperties, "Not_windows")
// For android, linux, ...
@@ -2079,6 +2147,9 @@
if os.Bionic() {
osStructs = append(osStructs, bionicStructs...)
}
+ if os.Linux() && os.Class == Host {
+ osStructs = append(osStructs, hostLinuxStructs...)
+ }
if os == LinuxMusl {
osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Musl")...)
@@ -2111,6 +2182,16 @@
targetStructs := getTargetStructs(ctx, archProperties, targetField)
osArchStructs = append(osArchStructs, targetStructs...)
}
+ if os == LinuxMusl {
+ targetField := "Musl_" + arch.Name
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
+ }
+ if os == Linux {
+ targetField := "Glibc_" + arch.Name
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
+ }
targetField := GetCompoundTargetField(os, arch)
targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
@@ -2167,3 +2248,40 @@
return value
}
+
+func printArchTypeStarlarkDict(dict map[ArchType][]string) string {
+ valDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ valDict[k.String()] = starlark_fmt.PrintStringList(v, 1)
+ }
+ return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func printArchTypeNestedStarlarkDict(dict map[ArchType]map[string][]string) string {
+ valDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ valDict[k.String()] = starlark_fmt.PrintStringListDict(v, 1)
+ }
+ return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func StarlarkArchConfigurations() string {
+ return fmt.Sprintf(`
+_arch_to_variants = %s
+
+_arch_to_cpu_variants = %s
+
+_arch_to_features = %s
+
+_android_arch_feature_for_arch_variant = %s
+
+arch_to_variants = _arch_to_variants
+arch_to_cpu_variants = _arch_to_cpu_variants
+arch_to_features = _arch_to_features
+android_arch_feature_for_arch_variants = _android_arch_feature_for_arch_variant
+`, printArchTypeStarlarkDict(archVariants),
+ printArchTypeStarlarkDict(cpuVariants),
+ printArchTypeStarlarkDict(archFeatures),
+ printArchTypeNestedStarlarkDict(androidArchFeatureMap),
+ )
+}
diff --git a/android/arch_list.go b/android/arch_list.go
index d68a0d1..cbf8e7a 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -14,46 +14,18 @@
package android
-import "fmt"
-
var archVariants = map[ArchType][]string{
Arm: {
"armv7-a",
"armv7-a-neon",
"armv8-a",
"armv8-2a",
- "cortex-a7",
- "cortex-a8",
- "cortex-a9",
- "cortex-a15",
- "cortex-a53",
- "cortex-a53-a57",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "krait",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
},
Arm64: {
- "armv8_a",
- "armv8_a_branchprot",
- "armv8_2a",
+ "armv8-a",
+ "armv8-a-branchprot",
+ "armv8-2a",
"armv8-2a-dotprod",
- "cortex-a53",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
},
X86: {
"amberlake",
@@ -87,6 +59,41 @@
},
}
+var cpuVariants = map[ArchType][]string{
+ Arm: {
+ "cortex-a7",
+ "cortex-a8",
+ "cortex-a9",
+ "cortex-a15",
+ "cortex-a53",
+ "cortex-a53.a57",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "krait",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ Arm64: {
+ "cortex-a53",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ X86: {},
+ X86_64: {},
+}
+
var archFeatures = map[ArchType][]string{
Arm: {
"neon",
@@ -119,7 +126,7 @@
},
}
-var archFeatureMap = map[ArchType]map[string][]string{
+var androidArchFeatureMap = map[ArchType]map[string][]string{
Arm: {
"armv7-a-neon": {
"neon",
@@ -270,6 +277,13 @@
},
},
X86_64: {
+ "" /*default */ : {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
"amberlake": {
"ssse3",
"sse4",
@@ -389,23 +403,3 @@
},
},
}
-
-var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
-
-// RegisterDefaultArchVariantFeatures is called by files that define Toolchains to specify the
-// arch features that are available for the default arch variant. It must be called from an
-// init() function.
-func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
- checkCalledFromInit()
-
- for _, feature := range features {
- if !InList(feature, archFeatures[arch]) {
- panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
- }
- }
-
- if defaultArchFeatureMap[os] == nil {
- defaultArchFeatureMap[os] = make(map[ArchType][]string)
- }
- defaultArchFeatureMap[os][arch] = features
-}
diff --git a/android/arch_test.go b/android/arch_test.go
index a828321..dd0b115 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -491,11 +491,9 @@
arch: {
arm: {
a: ["arm"],
- armv7_a_neon: { a: ["armv7_a_neon"] },
},
arm64: {
a: ["arm64"],
- armv8_a: { a: ["armv8_a"] },
},
x86: { a: ["x86"] },
x86_64: { a: ["x86_64"] },
@@ -512,6 +510,7 @@
musl: { a: ["musl"] },
linux_bionic: { a: ["linux_bionic"] },
linux: { a: ["linux"] },
+ host_linux: { a: ["host_linux"] },
linux_glibc: { a: ["linux_glibc"] },
linux_musl: { a: ["linux_musl"] },
windows: { a: ["windows"], enabled: true },
@@ -552,12 +551,12 @@
{
module: "foo",
variant: "android_arm64_armv8-a",
- property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "armv8_a", "lib64", "android_arm64"},
+ property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "lib64", "android_arm64"},
},
{
module: "foo",
variant: "android_arm_armv7-a-neon",
- property: []string{"root", "linux", "bionic", "android", "android64", "arm", "armv7_a_neon", "lib32", "android_arm"},
+ property: []string{"root", "linux", "bionic", "android", "android64", "arm", "lib32", "android_arm"},
},
},
},
@@ -568,12 +567,12 @@
{
module: "foo",
variant: "linux_glibc_x86_64",
- property: []string{"root", "host", "linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"},
+ property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"},
},
{
module: "foo",
variant: "linux_glibc_x86",
- property: []string{"root", "host", "linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"},
+ property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"},
},
},
},
@@ -607,12 +606,12 @@
{
module: "foo",
variant: "linux_musl_x86_64",
- property: []string{"root", "host", "linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64", "linux_glibc_x86_64"},
+ property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64"},
},
{
module: "foo",
variant: "linux_musl_x86",
- property: []string{"root", "host", "linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86", "linux_glibc_x86"},
+ property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86"},
},
},
},
diff --git a/android/bazel.go b/android/bazel.go
index 0940205..40f2917 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -24,6 +24,15 @@
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
+
+ "android/soong/android/allowlists"
+)
+
+const (
+ // A sentinel value to be used as a key in Bp2BuildConfig for modules with
+ // no package path. This is also the module dir for top level Android.bp
+ // modules.
+ Bp2BuildTopLevel = "."
)
type bazelModuleProperties struct {
@@ -87,7 +96,7 @@
HandcraftedLabel() string
GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
ShouldConvertWithBp2build(ctx BazelConversionContext) bool
- shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
+ shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool
GetBazelBuildFileContents(c Config, path, name string) (string, error)
ConvertWithBp2build(ctx TopDownMutatorContext)
@@ -106,6 +115,27 @@
SetBaseModuleType(baseModuleType string)
}
+// MixedBuildBuildable is an interface that module types should implement in order
+// to be "handled by Bazel" in a mixed build.
+type MixedBuildBuildable interface {
+ // IsMixedBuildSupported returns true if and only if this module should be
+ // "handled by Bazel" in a mixed build.
+ // This "escape hatch" allows modules with corner-case scenarios to opt out
+ // of being built with Bazel.
+ IsMixedBuildSupported(ctx BaseModuleContext) bool
+
+ // QueueBazelCall invokes request-queueing functions on the BazelContext
+ // so that these requests are handled when Bazel's cquery is invoked.
+ QueueBazelCall(ctx BaseModuleContext)
+
+ // ProcessBazelQueryResponse uses Bazel information (obtained from the BazelContext)
+ // to set module fields and providers to propagate this module's metadata upstream.
+ // This effectively "bridges the gap" between Bazel and Soong in a mixed build.
+ // Soong modules depending on this module should be oblivious to the fact that
+ // this module was handled by Bazel.
+ ProcessBazelQueryResponse(ctx ModuleContext)
+}
+
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
type BazelModule interface {
Module
@@ -161,378 +191,170 @@
return "" // no label for unconverted module
}
-// Configuration to decide if modules in a directory should default to true/false for bp2build_available
-type Bp2BuildConfig map[string]BazelConversionConfigEntry
-type BazelConversionConfigEntry int
+type bp2BuildConversionAllowlist struct {
+ // Configure modules in these directories to enable bp2build_available: true or false by default.
+ defaultConfig allowlists.Bp2BuildConfig
-const (
- // A sentinel value to be used as a key in Bp2BuildConfig for modules with
- // no package path. This is also the module dir for top level Android.bp
- // modules.
- BP2BUILD_TOPLEVEL = "."
-
- // iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
- // which can also mean that the key doesn't exist in a lookup.
-
- // all modules in this package and subpackages default to bp2build_available: true.
- // allows modules to opt-out.
- Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
-
- // all modules in this package (not recursively) default to bp2build_available: true.
- // allows modules to opt-out.
- Bp2BuildDefaultTrue
-
- // all modules in this package (not recursively) default to bp2build_available: false.
- // allows modules to opt-in.
- Bp2BuildDefaultFalse
-)
-
-var (
// Keep any existing BUILD files (and do not generate new BUILD files) for these directories
// in the synthetic Bazel workspace.
- bp2buildKeepExistingBuildFile = map[string]bool{
- // This is actually build/bazel/build.BAZEL symlinked to ./BUILD
- ".":/*recursive = */ false,
+ keepExistingBuildFile map[string]bool
- // build/bazel/examples/apex/... BUILD files should be generated, so
- // build/bazel is not recursive. Instead list each subdirectory under
- // build/bazel explicitly.
- "build/bazel":/* recursive = */ false,
- "build/bazel/examples/android_app":/* recursive = */ true,
- "build/bazel/examples/java":/* recursive = */ true,
- "build/bazel/bazel_skylib":/* recursive = */ true,
- "build/bazel/rules":/* recursive = */ true,
- "build/bazel/rules_cc":/* recursive = */ true,
- "build/bazel/scripts":/* recursive = */ true,
- "build/bazel/tests":/* recursive = */ true,
- "build/bazel/platforms":/* recursive = */ true,
- "build/bazel/product_variables":/* recursive = */ true,
- "build/bazel_common_rules":/* recursive = */ true,
- // build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
- "build/make/tools":/* recursive = */ false,
- "build/pesto":/* recursive = */ true,
+ // Per-module allowlist to always opt modules in of both bp2build and mixed builds.
+ // These modules are usually in directories with many other modules that are not ready for
+ // conversion.
+ //
+ // A module can either be in this list or its directory allowlisted entirely
+ // in bp2buildDefaultConfig, but not both at the same time.
+ moduleAlwaysConvert map[string]bool
- // external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
- // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
- "external/bazelbuild-rules_android":/* recursive = */ true,
- "external/bazel-skylib":/* recursive = */ true,
- "external/guava":/* recursive = */ true,
- "external/jsr305":/* recursive = */ true,
- "frameworks/ex/common":/* recursive = */ true,
-
- "packages/apps/Music":/* recursive = */ true,
- "packages/apps/QuickSearchBox":/* recursive = */ true,
- "packages/apps/WallpaperPicker":/* recursive = */ false,
-
- "prebuilts/gcc":/* recursive = */ true,
- "prebuilts/build-tools":/* recursive = */ false,
- "prebuilts/sdk":/* recursive = */ false,
- "prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
- "prebuilts/sdk/current/support":/* recursive = */ false,
- "prebuilts/sdk/tools":/* recursive = */ false,
- "prebuilts/r8":/* recursive = */ false,
- }
-
- // Configure modules in these directories to enable bp2build_available: true or false by default.
- bp2buildDefaultConfig = Bp2BuildConfig{
- "art/libdexfile": Bp2BuildDefaultTrueRecursively,
- "art/runtime": Bp2BuildDefaultTrueRecursively,
- "art/tools": Bp2BuildDefaultTrue,
- "bionic": Bp2BuildDefaultTrueRecursively,
- "bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
- "build/bazel/examples/soong_config_variables": Bp2BuildDefaultTrueRecursively,
- "build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
- "build/make/tools/signapk": Bp2BuildDefaultTrue,
- "build/soong": Bp2BuildDefaultTrue,
- "build/soong/cc/libbuildversion": Bp2BuildDefaultTrue, // Skip tests subdir
- "build/soong/cc/ndkstubgen": Bp2BuildDefaultTrue,
- "build/soong/cc/symbolfile": Bp2BuildDefaultTrue,
- "build/soong/linkerconfig": Bp2BuildDefaultTrueRecursively,
- "build/soong/scripts": Bp2BuildDefaultTrueRecursively,
- "cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
- "development/apps/DevelopmentSettings": Bp2BuildDefaultTrue,
- "development/apps/Fallback": Bp2BuildDefaultTrue,
- "development/apps/WidgetPreview": Bp2BuildDefaultTrue,
- "development/samples/BasicGLSurfaceView": Bp2BuildDefaultTrue,
- "development/samples/BluetoothChat": Bp2BuildDefaultTrue,
- "development/samples/BrokenKeyDerivation": Bp2BuildDefaultTrue,
- "development/samples/Compass": Bp2BuildDefaultTrue,
- "development/samples/ContactManager": Bp2BuildDefaultTrue,
- "development/samples/FixedGridLayout": Bp2BuildDefaultTrue,
- "development/samples/HelloEffects": Bp2BuildDefaultTrue,
- "development/samples/Home": Bp2BuildDefaultTrue,
- "development/samples/HoneycombGallery": Bp2BuildDefaultTrue,
- "development/samples/JetBoy": Bp2BuildDefaultTrue,
- "development/samples/KeyChainDemo": Bp2BuildDefaultTrue,
- "development/samples/LceDemo": Bp2BuildDefaultTrue,
- "development/samples/LunarLander": Bp2BuildDefaultTrue,
- "development/samples/MultiResolution": Bp2BuildDefaultTrue,
- "development/samples/MultiWindow": Bp2BuildDefaultTrue,
- "development/samples/NotePad": Bp2BuildDefaultTrue,
- "development/samples/Obb": Bp2BuildDefaultTrue,
- "development/samples/RSSReader": Bp2BuildDefaultTrue,
- "development/samples/ReceiveShareDemo": Bp2BuildDefaultTrue,
- "development/samples/SearchableDictionary": Bp2BuildDefaultTrue,
- "development/samples/SipDemo": Bp2BuildDefaultTrue,
- "development/samples/SkeletonApp": Bp2BuildDefaultTrue,
- "development/samples/Snake": Bp2BuildDefaultTrue,
- "development/samples/SpellChecker/": Bp2BuildDefaultTrueRecursively,
- "development/samples/ThemedNavBarKeyboard": Bp2BuildDefaultTrue,
- "development/samples/ToyVpn": Bp2BuildDefaultTrue,
- "development/samples/TtsEngine": Bp2BuildDefaultTrue,
- "development/samples/USB/AdbTest": Bp2BuildDefaultTrue,
- "development/samples/USB/MissileLauncher": Bp2BuildDefaultTrue,
- "development/samples/VoiceRecognitionService": Bp2BuildDefaultTrue,
- "development/samples/VoicemailProviderDemo": Bp2BuildDefaultTrue,
- "development/samples/WiFiDirectDemo": Bp2BuildDefaultTrue,
- "development/sdk": Bp2BuildDefaultTrueRecursively,
- "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
- "external/boringssl": Bp2BuildDefaultTrueRecursively,
- "external/bouncycastle": Bp2BuildDefaultTrue,
- "external/brotli": Bp2BuildDefaultTrue,
- "external/conscrypt": Bp2BuildDefaultTrue,
- "external/error_prone": Bp2BuildDefaultTrue,
- "external/fmtlib": Bp2BuildDefaultTrueRecursively,
- "external/google-benchmark": Bp2BuildDefaultTrueRecursively,
- "external/googletest": Bp2BuildDefaultTrueRecursively,
- "external/gwp_asan": Bp2BuildDefaultTrueRecursively,
- "external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
- "external/jsoncpp": Bp2BuildDefaultTrueRecursively,
- "external/libcap": Bp2BuildDefaultTrueRecursively,
- "external/libcxx": Bp2BuildDefaultTrueRecursively,
- "external/libcxxabi": Bp2BuildDefaultTrueRecursively,
- "external/libevent": Bp2BuildDefaultTrueRecursively,
- "external/libpng": Bp2BuildDefaultTrueRecursively,
- "external/lz4/lib": Bp2BuildDefaultTrue,
- "external/lzma/C": Bp2BuildDefaultTrueRecursively,
- "external/mdnsresponder": Bp2BuildDefaultTrueRecursively,
- "external/minijail": Bp2BuildDefaultTrueRecursively,
- "external/pcre": Bp2BuildDefaultTrueRecursively,
- "external/protobuf": Bp2BuildDefaultTrueRecursively,
- "external/python/six": Bp2BuildDefaultTrueRecursively,
- "external/scudo": Bp2BuildDefaultTrueRecursively,
- "external/selinux/libselinux": Bp2BuildDefaultTrueRecursively,
- "external/selinux/libsepol": Bp2BuildDefaultTrueRecursively,
- "external/zlib": Bp2BuildDefaultTrueRecursively,
- "external/zstd": Bp2BuildDefaultTrueRecursively,
- "frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue,
- "frameworks/base/startop/apps/test": Bp2BuildDefaultTrue,
- "frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively,
- "frameworks/native/opengl/tests/gl2_cameraeye": Bp2BuildDefaultTrue,
- "frameworks/native/opengl/tests/gl2_java": Bp2BuildDefaultTrue,
- "frameworks/native/opengl/tests/testLatency": Bp2BuildDefaultTrue,
- "frameworks/native/opengl/tests/testPauseResume": Bp2BuildDefaultTrue,
- "frameworks/native/opengl/tests/testViewport": Bp2BuildDefaultTrue,
- "frameworks/proto_logging/stats/stats_log_api_gen": Bp2BuildDefaultTrueRecursively,
- "libnativehelper": Bp2BuildDefaultTrueRecursively,
- "packages/apps/DevCamera": Bp2BuildDefaultTrue,
- "packages/apps/HTMLViewer": Bp2BuildDefaultTrue,
- "packages/apps/Protips": Bp2BuildDefaultTrue,
- "packages/modules/StatsD/lib/libstatssocket": Bp2BuildDefaultTrueRecursively,
- "packages/modules/adb": Bp2BuildDefaultTrue,
- "packages/modules/adb/apex": Bp2BuildDefaultTrue,
- "packages/modules/adb/crypto": Bp2BuildDefaultTrueRecursively,
- "packages/modules/adb/libs": Bp2BuildDefaultTrueRecursively,
- "packages/modules/adb/pairing_auth": Bp2BuildDefaultTrueRecursively,
- "packages/modules/adb/pairing_connection": Bp2BuildDefaultTrueRecursively,
- "packages/modules/adb/proto": Bp2BuildDefaultTrueRecursively,
- "packages/modules/adb/tls": Bp2BuildDefaultTrueRecursively,
- "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultTrue,
- "packages/screensavers/Basic": Bp2BuildDefaultTrue,
- "packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultTrue,
- "prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
- "prebuilts/tools/common/m2": Bp2BuildDefaultTrue,
- "system/apex": Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
- "system/apex/proto": Bp2BuildDefaultTrueRecursively,
- "system/apex/libs": Bp2BuildDefaultTrueRecursively,
- "system/core/debuggerd": Bp2BuildDefaultTrueRecursively,
- "system/core/diagnose_usb": Bp2BuildDefaultTrueRecursively,
- "system/core/libasyncio": Bp2BuildDefaultTrue,
- "system/core/libcrypto_utils": Bp2BuildDefaultTrueRecursively,
- "system/core/libcutils": Bp2BuildDefaultTrueRecursively,
- "system/core/libpackagelistparser": Bp2BuildDefaultTrueRecursively,
- "system/core/libprocessgroup": Bp2BuildDefaultTrue,
- "system/core/libprocessgroup/cgrouprc": Bp2BuildDefaultTrue,
- "system/core/libprocessgroup/cgrouprc_format": Bp2BuildDefaultTrue,
- "system/core/libsystem": Bp2BuildDefaultTrueRecursively,
- "system/core/libutils": Bp2BuildDefaultTrueRecursively,
- "system/core/libvndksupport": Bp2BuildDefaultTrueRecursively,
- "system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
- "system/libbase": Bp2BuildDefaultTrueRecursively,
- "system/libprocinfo": Bp2BuildDefaultTrue,
- "system/libziparchive": Bp2BuildDefaultTrueRecursively,
- "system/logging/liblog": Bp2BuildDefaultTrueRecursively,
- "system/sepolicy/apex": Bp2BuildDefaultTrueRecursively,
- "system/timezone/apex": Bp2BuildDefaultTrueRecursively,
- "system/timezone/output_data": Bp2BuildDefaultTrueRecursively,
- "system/unwinding/libbacktrace": Bp2BuildDefaultTrueRecursively,
- "system/unwinding/libunwindstack": Bp2BuildDefaultTrueRecursively,
- "tools/apksig": Bp2BuildDefaultTrue,
- "tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively,
- }
+ // Per-module-type allowlist to always opt modules in to both bp2build and mixed builds
+ // when they have the same type as one listed.
+ moduleTypeAlwaysConvert map[string]bool
// Per-module denylist to always opt modules out of both bp2build and mixed builds.
- bp2buildModuleDoNotConvertList = []string{
- "libnativehelper_compat_libc", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
-
- "libart", // depends on unconverted modules: art_operator_srcs, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfile, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbase, libprofile, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api
- "libart-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libbacktrace, libartbase-art-gtest
- "libart_headers", // depends on unconverted modules: art_libartbase_headers
- "libartd", // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api, art_operator_srcs, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfiled, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbased, libprofiled, cpp-define-generator-asm-support
- "libartd-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libbacktrace, libartbased-art-gtest
- "libstatslog_art", // depends on unconverted modules: statslog_art.cpp, statslog_art.h
- "statslog_art.h", "statslog_art.cpp", // depends on unconverted modules: stats-log-api-gen
-
- "libandroid_runtime_lazy", // depends on unconverted modules: libbinder_headers
- "libcmd", // depends on unconverted modules: libbinder
-
- "libdexfile_support_static", // Depends on unconverted module: libdexfile_external_headers
- "libunwindstack_local", "libunwindstack_utils", "libc_malloc_debug", "libfdtrack", // Depends on unconverted module: libunwindstack
-
- "libdexfile_support", // TODO(b/210546943): Enabled based on product variables.
- "libdexfile_external_headers", // TODO(b/210546943): Enabled based on product variables.
-
- "libunwindstack", // Depends on unconverted module libdexfile_support.
- "libnativehelper_compat_libc++", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
-
- "chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
-
- "libsepol", // TODO(b/207408632): Unsupported case of .l sources in cc library rules
-
- "gen-kotlin-build-file.py", // module has same name as source
-
- "libactivitymanager_aidl", // TODO(b/207426160): Depends on activity_manager_procstate_aidl, which is an aidl filegroup.
-
- "libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libgmock_ndk
- "libnativetesthelper_jni", "libgmock_main_ndk", "libgmock_ndk", // depends on unconverted module: libgtest_ndk_c++
-
- "statslog-framework-java-gen", "statslog.cpp", "statslog.h", "statslog.rs", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
-
- "stats-log-api-gen", // depends on unconverted modules: libstats_proto_host, libprotobuf-cpp-full
-
- "libstatslog", // depends on unconverted modules: statslog.cpp, statslog.h, ...
-
- "cmd", // depends on unconverted module packagemanager_aidl-cpp, of unsupported type aidl_interface
- "servicedispatcher", // depends on unconverted module android.debug_aidl, of unsupported type aidl_interface
- "libutilscallstack", // depends on unconverted module libbacktrace
- "libbacktrace", // depends on unconverted module libunwindstack
- "libdebuggerd_handler", // depends on unconverted module libdebuggerd_handler_core
- "libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
- "unwind_for_offline", // depends on unconverted module libunwindstack_utils
- "libdebuggerd", // depends on unconverted modules libdexfile_support, libunwindstack, gwp_asan_crash_handler, libtombstone_proto, libprotobuf-cpp-lite
- "libdexfile_static", // depends on libartpalette, libartbase, libdexfile, which are of unsupported type: art_cc_library.
-
- "static_crasher", // depends on unconverted modules: libdebuggerd_handler
-
- "pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
-
- "libbase_ndk", // http://b/186826477, fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
-
- "libprotobuf-internal-protos", // b/210751803, we don't handle path property for filegroups
- "libprotobuf-internal-python-srcs", // b/210751803, we don't handle path property for filegroups
- "libprotobuf-java-full", // b/210751803, we don't handle path property for filegroups
- "libprotobuf-java-util-full", // b/210751803, we don't handle path property for filegroups
- "conscrypt", // b/210751803, we don't handle path property for filegroups
-
- // python protos
- "libprotobuf-python", // contains .proto sources
- "conv_linker_config", // depends on linker_config_proto, a python lib with proto sources
- "apex_build_info_proto", "apex_manifest_proto", // a python lib with proto sources
- "linker_config_proto", // contains .proto sources
-
- "brotli-fuzzer-corpus", // b/202015218: outputs are in location incompatible with bazel genrule handling.
-
- // b/203369847: multiple genrules in the same package creating the same file
- // //development/sdk/...
- "platform_tools_properties",
- "build_tools_source_properties",
-
- // APEX support
- "com.android.runtime", // depends on unconverted modules: bionic-linker-config, linkerconfig
-
- "libgtest_ndk_c++", // b/201816222: Requires sdk_version support.
- "libgtest_main_ndk_c++", // b/201816222: Requires sdk_version support.
-
- "abb", // depends on unconverted modules: libcmd, libbinder
- "adb", // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
- "libadb_host", // depends on unconverted modules: libopenscreen-discovery, libopenscreen-platform-impl, libusb, AdbWinApi
- "libfastdeploy_host", // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
- "linker", // depends on unconverted modules: libdebuggerd_handler_fallback
- "linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
- "versioner", // depends on unconverted modules: libclang_cxx_host, libLLVM_host, of unsupported type llvm_host_prebuilt_library_shared
-
- "linkerconfig", // http://b/202876379 has arch-variant static_executable
- "mdnsd", // http://b/202876379 has arch-variant static_executable
-
- "CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib
-
- "libdexfile", // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
- "libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
-
- // go deps:
- "apex-protos", // depends on soong_zip, a go binary
- "robolectric_tzdata", // depends on soong_zip, a go binary
- "robolectric-sqlite4java-native", // depends on soong_zip, a go binary
- "host_bionic_linker_asm", // depends on extract_linker, a go binary.
- "host_bionic_linker_script", // depends on extract_linker, a go binary.
-
- // java deps
- "bin2c_fastdeployagent", // depends on deployagent, a java binary
- }
+ moduleDoNotConvert map[string]bool
// Per-module denylist of cc_library modules to only generate the static
// variant if their shared variant isn't ready or buildable by Bazel.
- bp2buildCcLibraryStaticOnlyList = []string{}
+ ccLibraryStaticOnly map[string]bool
// Per-module denylist to opt modules out of mixed builds. Such modules will
// still be generated via bp2build.
- mixedBuildsDisabledList = []string{
- "art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+ mixedBuildsDisabled map[string]bool
+}
- "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
- "minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
-
- "cap_names.h", // TODO(b/204913827) runfiles need to be handled in mixed builds
- "libcap", // TODO(b/204913827) runfiles need to be handled in mixed builds
- "libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
-
- // Depends on libprotobuf-cpp-*
- "libadb_pairing_connection",
- "libadb_pairing_connection_static",
- "libadb_pairing_server", "libadb_pairing_server_static",
- }
-
- // Used for quicker lookups
- bp2buildModuleDoNotConvert = map[string]bool{}
- bp2buildCcLibraryStaticOnly = map[string]bool{}
- mixedBuildsDisabled = map[string]bool{}
-)
-
-func init() {
- for _, moduleName := range bp2buildModuleDoNotConvertList {
- bp2buildModuleDoNotConvert[moduleName] = true
- }
-
- for _, moduleName := range bp2buildCcLibraryStaticOnlyList {
- bp2buildCcLibraryStaticOnly[moduleName] = true
- }
-
- for _, moduleName := range mixedBuildsDisabledList {
- mixedBuildsDisabled[moduleName] = true
+// NewBp2BuildAllowlist creates a new, empty bp2BuildConversionAllowlist
+// which can be populated using builder pattern Set* methods
+func NewBp2BuildAllowlist() bp2BuildConversionAllowlist {
+ return bp2BuildConversionAllowlist{
+ allowlists.Bp2BuildConfig{},
+ map[string]bool{},
+ map[string]bool{},
+ map[string]bool{},
+ map[string]bool{},
+ map[string]bool{},
+ map[string]bool{},
}
}
+// SetDefaultConfig copies the entries from defaultConfig into the allowlist
+func (a bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) bp2BuildConversionAllowlist {
+ if a.defaultConfig == nil {
+ a.defaultConfig = allowlists.Bp2BuildConfig{}
+ }
+ for k, v := range defaultConfig {
+ a.defaultConfig[k] = v
+ }
+
+ return a
+}
+
+// SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
+func (a bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) bp2BuildConversionAllowlist {
+ if a.keepExistingBuildFile == nil {
+ a.keepExistingBuildFile = map[string]bool{}
+ }
+ for k, v := range keepExistingBuildFile {
+ a.keepExistingBuildFile[k] = v
+ }
+
+ return a
+}
+
+// SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
+func (a bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) bp2BuildConversionAllowlist {
+ if a.moduleAlwaysConvert == nil {
+ a.moduleAlwaysConvert = map[string]bool{}
+ }
+ for _, m := range moduleAlwaysConvert {
+ a.moduleAlwaysConvert[m] = true
+ }
+
+ return a
+}
+
+// SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
+func (a bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) bp2BuildConversionAllowlist {
+ if a.moduleTypeAlwaysConvert == nil {
+ a.moduleTypeAlwaysConvert = map[string]bool{}
+ }
+ for _, m := range moduleTypeAlwaysConvert {
+ a.moduleTypeAlwaysConvert[m] = true
+ }
+
+ return a
+}
+
+// SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
+func (a bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) bp2BuildConversionAllowlist {
+ if a.moduleDoNotConvert == nil {
+ a.moduleDoNotConvert = map[string]bool{}
+ }
+ for _, m := range moduleDoNotConvert {
+ a.moduleDoNotConvert[m] = true
+ }
+
+ return a
+}
+
+// SetCcLibraryStaticOnlyList copies the entries from ccLibraryStaticOnly into the allowlist
+func (a bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) bp2BuildConversionAllowlist {
+ if a.ccLibraryStaticOnly == nil {
+ a.ccLibraryStaticOnly = map[string]bool{}
+ }
+ for _, m := range ccLibraryStaticOnly {
+ a.ccLibraryStaticOnly[m] = true
+ }
+
+ return a
+}
+
+// SetMixedBuildsDisabledList copies the entries from mixedBuildsDisabled into the allowlist
+func (a bp2BuildConversionAllowlist) SetMixedBuildsDisabledList(mixedBuildsDisabled []string) bp2BuildConversionAllowlist {
+ if a.mixedBuildsDisabled == nil {
+ a.mixedBuildsDisabled = map[string]bool{}
+ }
+ for _, m := range mixedBuildsDisabled {
+ a.mixedBuildsDisabled[m] = true
+ }
+
+ return a
+}
+
+var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
+var bp2buildAllowlist OncePer
+
+func getBp2BuildAllowList() bp2BuildConversionAllowlist {
+ return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
+ return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
+ SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
+ SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
+ SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
+ SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
+ SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList).
+ SetMixedBuildsDisabledList(allowlists.MixedBuildsDisabledList)
+ }).(bp2BuildConversionAllowlist)
+}
+
+// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
+// generate a static version of itself based on the current global configuration.
func GenerateCcLibraryStaticOnly(moduleName string) bool {
- return bp2buildCcLibraryStaticOnly[moduleName]
+ return getBp2BuildAllowList().ccLibraryStaticOnly[moduleName]
}
+// ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
+// added to the build symlink forest based on the current global configuration.
func ShouldKeepExistingBuildFileForDir(dir string) bool {
- if _, ok := bp2buildKeepExistingBuildFile[dir]; ok {
+ return shouldKeepExistingBuildFileForDir(getBp2BuildAllowList(), dir)
+}
+
+func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, dir string) bool {
+ if _, ok := allowlist.keepExistingBuildFile[dir]; ok {
// Exact dir match
return true
}
// Check if subtree match
- for prefix, recursive := range bp2buildKeepExistingBuildFile {
+ for prefix, recursive := range allowlist.keepExistingBuildFile {
if recursive {
if strings.HasPrefix(dir, prefix+"/") {
return true
@@ -543,9 +365,19 @@
return false
}
-// MixedBuildsEnabled checks that a module is ready to be replaced by a
+// MixedBuildsEnabled returns true if a module is ready to be replaced by a
+// converted or handcrafted Bazel target. As a side effect, calling this
+// method will also log whether this module is mixed build enabled for
+// metrics reporting.
+func MixedBuildsEnabled(ctx BaseModuleContext) bool {
+ mixedBuildEnabled := mixedBuildPossible(ctx)
+ ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
+ return mixedBuildEnabled
+}
+
+// mixedBuildPossible returns true if a module is ready to be replaced by a
// converted or handcrafted Bazel target.
-func (b *BazelModuleBase) MixedBuildsEnabled(ctx ModuleContext) bool {
+func mixedBuildPossible(ctx BaseModuleContext) bool {
if ctx.Os() == Windows {
// Windows toolchains are not currently supported.
return false
@@ -566,7 +398,7 @@
// variants of a cc_library.
return false
}
- return !mixedBuildsDisabled[ctx.Module().Name()]
+ return !getBp2BuildAllowList().mixedBuildsDisabled[ctx.Module().Name()]
}
// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -578,32 +410,74 @@
return b.shouldConvertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
}
-// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
+// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build
func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx BazelConversionContext) bool {
return b.shouldConvertWithBp2build(ctx, ctx.Module())
}
-func (b *BazelModuleBase) shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool {
- if bp2buildModuleDoNotConvert[module.Name()] {
- return false
- }
+type bazelOtherModuleContext interface {
+ ModuleErrorf(format string, args ...interface{})
+ Config() Config
+ OtherModuleType(m blueprint.Module) string
+ OtherModuleName(m blueprint.Module) string
+ OtherModuleDir(m blueprint.Module) string
+}
+func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool {
if !b.bazelProps().Bazel_module.CanConvertToBazel {
return false
}
+ propValue := b.bazelProperties.Bazel_module.Bp2build_available
packagePath := ctx.OtherModuleDir(module)
- config := ctx.Config().bp2buildPackageConfig
+
+ // Modules in unit tests which are enabled in the allowlist by type or name
+ // trigger this conditional because unit tests run under the "." package path
+ isTestModule := packagePath == Bp2BuildTopLevel && proptools.BoolDefault(propValue, false)
+ if isTestModule {
+ return true
+ }
+
+ moduleName := module.Name()
+ allowlist := ctx.Config().bp2buildPackageConfig
+ moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
+ moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
+ allowlistConvert := moduleNameAllowed || moduleTypeAllowed
+ if moduleNameAllowed && moduleTypeAllowed {
+ ctx.ModuleErrorf("A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert")
+ return false
+ }
+
+ if allowlist.moduleDoNotConvert[moduleName] {
+ if moduleNameAllowed {
+ ctx.ModuleErrorf("a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert")
+ }
+ return false
+ }
+
+ if allowlistConvert && shouldKeepExistingBuildFileForDir(allowlist, packagePath) {
+ if moduleNameAllowed {
+ ctx.ModuleErrorf("A module cannot be in a directory listed in keepExistingBuildFile"+
+ " and also be in moduleAlwaysConvert. Directory: '%s'", packagePath)
+ return false
+ }
+ }
// This is a tristate value: true, false, or unset.
- propValue := b.bazelProperties.Bazel_module.Bp2build_available
- if bp2buildDefaultTrueRecursively(packagePath, config) {
+ if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
+ if moduleNameAllowed {
+ ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
+ " or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'",
+ directoryPath)
+ return false
+ }
+
// Allow modules to explicitly opt-out.
return proptools.BoolDefault(propValue, true)
}
// Allow modules to explicitly opt-in.
- return proptools.BoolDefault(propValue, false)
+ return proptools.BoolDefault(propValue, allowlistConvert)
}
// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
@@ -616,14 +490,16 @@
//
// This function will also return false if the package doesn't match anything in
// the config.
-func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
- ret := false
-
+//
+// This function will also return the allowlist entry which caused a particular
+// package to be enabled. Since packages can be enabled via a recursive declaration,
+// the path returned will not always be the same as the one provided.
+func bp2buildDefaultTrueRecursively(packagePath string, config allowlists.Bp2BuildConfig) (bool, string) {
// Check if the package path has an exact match in the config.
- if config[packagePath] == Bp2BuildDefaultTrue || config[packagePath] == Bp2BuildDefaultTrueRecursively {
- return true
- } else if config[packagePath] == Bp2BuildDefaultFalse {
- return false
+ if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively {
+ return true, packagePath
+ } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse {
+ return false, packagePath
}
// If not, check for the config recursively.
@@ -631,15 +507,15 @@
// e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
for _, part := range strings.Split(packagePath, "/") {
packagePrefix += part
- if config[packagePrefix] == Bp2BuildDefaultTrueRecursively {
+ if config[packagePrefix] == allowlists.Bp2BuildDefaultTrueRecursively {
// package contains this prefix and this prefix should convert all modules
- return true
+ return true, packagePrefix
}
// Continue to the next part of the package dir.
packagePrefix += "/"
}
- return ret
+ return false, packagePath
}
// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
@@ -685,6 +561,7 @@
if err != nil {
return "", err
}
+ defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 0052551..8ab003c 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -28,10 +28,31 @@
"android/soong/bazel/cquery"
"android/soong/shared"
+ "github.com/google/blueprint"
"android/soong/bazel"
)
+func init() {
+ RegisterMixedBuildsMutator(InitRegistrationContext)
+}
+
+func RegisterMixedBuildsMutator(ctx RegistrationContext) {
+ ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
+ })
+}
+
+func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
+ if m := ctx.Module(); m.Enabled() {
+ if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
+ if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
+ mixedBuildMod.QueueBazelCall(ctx)
+ }
+ }
+ }
+}
+
type cqueryRequest interface {
// Name returns a string name for this request type. Such request type names must be unique,
// and must only consist of alphanumeric characters.
@@ -50,8 +71,8 @@
// Portion of cquery map key to describe target configuration.
type configKey struct {
- archType ArchType
- osType OsType
+ arch string
+ osType OsType
}
// Map key to describe bazel cquery requests.
@@ -61,33 +82,32 @@
configKey configKey
}
-// bazelHandler is the interface for a helper object related to deferring to Bazel for
-// processing a module (during Bazel mixed builds). Individual module types should define
-// their own bazel handler if they support deferring to Bazel.
-type BazelHandler interface {
- // Issue query to Bazel to retrieve information about Bazel's view of the current module.
- // If Bazel returns this information, set module properties on the current module to reflect
- // the returned information.
- // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
- GenerateBazelBuildActions(ctx ModuleContext, label string) bool
-}
-
+// BazelContext is a context object useful for interacting with Bazel during
+// the course of a build. Use of Bazel to evaluate part of the build graph
+// is referred to as a "mixed build". (Some modules are managed by Soong,
+// some are managed by Bazel). To facilitate interop between these build
+// subgraphs, Soong may make requests to Bazel and evaluate their responses
+// so that Soong modules may accurately depend on Bazel targets.
type BazelContext interface {
- // The below methods involve queuing cquery requests to be later invoked
- // by bazel. If any of these methods return (_, false), then the request
- // has been queued to be run later.
+ // Add a cquery request to the bazel request queue. All queued requests
+ // will be sent to Bazel on a subsequent invocation of InvokeBazel.
+ QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey)
+
+ // ** Cquery Results Retrieval Functions
+ // The below functions pertain to retrieving cquery results from a prior
+ // InvokeBazel function call and parsing the results.
// Returns result files built by building the given bazel target label.
- GetOutputFiles(label string, cfgKey configKey) ([]string, bool)
+ GetOutputFiles(label string, cfgKey configKey) ([]string, error)
- // TODO(cparsons): Other cquery-related methods should be added here.
// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
- GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error)
+ GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error)
// Returns the executable binary resultant from building together the python sources
- GetPythonBinary(label string, cfgKey configKey) (string, bool)
+ // TODO(b/232976601): Remove.
+ GetPythonBinary(label string, cfgKey configKey) (string, error)
- // ** End cquery methods
+ // ** end Cquery Results Retrieval Functions
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
@@ -101,6 +121,9 @@
// Returns build statements which should get registered to reflect Bazel's outputs.
BuildStatementsToRegister() []bazel.BuildStatement
+
+ // Returns the depsets defined in Bazel's aquery response.
+ AqueryDepsets() []bazel.AqueryDepset
}
type bazelRunner interface {
@@ -128,6 +151,9 @@
// Build statements which should get registered to reflect Bazel's outputs.
buildStatements []bazel.BuildStatement
+
+ // Depsets which should be used for Bazel's build statements.
+ depsets []bazel.AqueryDepset
}
var _ BazelContext = &bazelContext{}
@@ -146,19 +172,23 @@
LabelToPythonBinary map[string]string
}
-func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
- result, ok := m.LabelToOutputFiles[label]
- return result, ok
+func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+ panic("unimplemented")
}
-func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
- result, ok := m.LabelToCcInfo[label]
- return result, ok, nil
+func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+ result, _ := m.LabelToOutputFiles[label]
+ return result, nil
}
-func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
- result, ok := m.LabelToPythonBinary[label]
- return result, ok
+func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+ result, _ := m.LabelToCcInfo[label]
+ return result, nil
+}
+
+func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+ result, _ := m.LabelToPythonBinary[label]
+ return result, nil
}
func (m MockBazelContext) InvokeBazel() error {
@@ -175,48 +205,59 @@
return []bazel.BuildStatement{}
}
+func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
+ return []bazel.AqueryDepset{}
+}
+
var _ BazelContext = MockBazelContext{}
-func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
- rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, cfgKey)
- var ret []string
- if ok {
+func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+ key := cqueryKey{label, requestType, cfgKey}
+ bazelCtx.requestMutex.Lock()
+ defer bazelCtx.requestMutex.Unlock()
+ bazelCtx.requests[key] = true
+}
+
+func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+ key := cqueryKey{label, cquery.GetOutputFiles, cfgKey}
+ if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString)
- ret = cquery.GetOutputFiles.ParseResult(bazelOutput)
+ return cquery.GetOutputFiles.ParseResult(bazelOutput), nil
}
- return ret, ok
+ return nil, fmt.Errorf("no bazel response found for %v", key)
}
-func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
- result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, cfgKey)
- if !ok {
- return cquery.CcInfo{}, ok, nil
- }
-
- bazelOutput := strings.TrimSpace(result)
- ret, err := cquery.GetCcInfo.ParseResult(bazelOutput)
- return ret, ok, err
-}
-
-func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
- rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, cfgKey)
- var ret string
- if ok {
+func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+ key := cqueryKey{label, cquery.GetCcInfo, cfgKey}
+ if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString)
- ret = cquery.GetPythonBinary.ParseResult(bazelOutput)
+ return cquery.GetCcInfo.ParseResult(bazelOutput)
}
- return ret, ok
+ return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
}
-func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
+func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+ key := cqueryKey{label, cquery.GetPythonBinary, cfgKey}
+ if rawString, ok := bazelCtx.results[key]; ok {
+ bazelOutput := strings.TrimSpace(rawString)
+ return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
+ }
+ return "", fmt.Errorf("no bazel response found for %v", key)
+}
+
+func (n noopBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
panic("unimplemented")
}
-func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
+func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
panic("unimplemented")
}
-func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
+func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+ panic("unimplemented")
+}
+
+func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
panic("unimplemented")
}
@@ -236,6 +277,10 @@
return []bazel.BuildStatement{}
}
+func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
+ return []bazel.AqueryDepset{}
+}
+
func NewBazelContext(c *config) (BazelContext, error) {
// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
// are production ready.
@@ -299,24 +344,6 @@
return true
}
-// Adds a cquery request to the Bazel request queue, to be later invoked, or
-// returns the result of the given request if the request was already made.
-// If the given request was already made (and the results are available), then
-// returns (result, true). If the request is queued but no results are available,
-// then returns ("", false).
-func (context *bazelContext) cquery(label string, requestType cqueryRequest,
- cfgKey configKey) (string, bool) {
- key := cqueryKey{label, requestType, cfgKey}
- if result, ok := context.results[key]; ok {
- return result, true
- } else {
- context.requestMutex.Lock()
- defer context.requestMutex.Unlock()
- context.requests[key] = true
- return "", false
- }
-}
-
func pwdPrefix() string {
// Darwin doesn't have /proc
if runtime.GOOS != "darwin" {
@@ -561,7 +588,7 @@
return id_string + ">>" + %s(target)
`
- for requestType, _ := range requestTypeToCqueryIdEntries {
+ for requestType := range requestTypeToCqueryIdEntries {
labelMapName := requestType.Name() + "_Labels"
functionName := requestType.Name() + "_Fn"
labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
@@ -664,7 +691,12 @@
if err != nil {
return err
}
-
+ if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
+ err = os.MkdirAll(metricsDir, 0777)
+ if err != nil {
+ return err
+ }
+ }
err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
if err != nil {
return err
@@ -716,9 +748,9 @@
}
}
- for val, _ := range context.requests {
+ for val := range context.requests {
if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
- context.results[val] = string(cqueryResult)
+ context.results[val] = cqueryResult
} else {
return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
getCqueryId(val), cqueryOutput, cqueryErr)
@@ -741,7 +773,7 @@
return err
}
- context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+ context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
if err != nil {
return err
}
@@ -767,6 +799,10 @@
return context.buildStatements
}
+func (context *bazelContext) AqueryDepsets() []bazel.AqueryDepset {
+ return context.depsets
+}
+
func (context *bazelContext) OutputBase() string {
return context.paths.outputBase
}
@@ -799,6 +835,23 @@
ctx.AddNinjaFileDeps(file)
}
+ for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
+ var outputs []Path
+ for _, depsetDepHash := range depset.TransitiveDepSetHashes {
+ otherDepsetName := bazelDepsetName(depsetDepHash)
+ outputs = append(outputs, PathForPhony(ctx, otherDepsetName))
+ }
+ for _, artifactPath := range depset.DirectArtifacts {
+ outputs = append(outputs, PathForBazelOut(ctx, artifactPath))
+ }
+ thisDepsetName := bazelDepsetName(depset.ContentHash)
+ ctx.Build(pctx, BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []WritablePath{PathForPhony(ctx, thisDepsetName)},
+ Implicits: outputs,
+ })
+ }
+
// Register bazel-owned build statements (obtained from the aquery invocation).
for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
if len(buildStatement.Command) < 1 {
@@ -825,7 +878,7 @@
}
// The actual Bazel action.
- cmd.Text(" " + buildStatement.Command)
+ cmd.Text(buildStatement.Command)
for _, outputPath := range buildStatement.OutputPaths {
cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
@@ -833,6 +886,10 @@
for _, inputPath := range buildStatement.InputPaths {
cmd.Implicit(PathForBazelOut(ctx, inputPath))
}
+ for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
+ otherDepsetName := bazelDepsetName(inputDepsetHash)
+ cmd.Implicit(PathForPhony(ctx, otherDepsetName))
+ }
if depfile := buildStatement.Depfile; depfile != nil {
cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
@@ -848,7 +905,8 @@
// build statement have later timestamps than the outputs.
rule.Restat()
- rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
+ desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
+ rule.Build(fmt.Sprintf("bazel %d", index), desc)
}
}
@@ -857,7 +915,7 @@
}
func getConfigString(key cqueryKey) string {
- arch := key.configKey.archType.Name
+ arch := key.configKey.arch
if len(arch) == 0 || arch == "common" {
// Use host platform, which is currently hardcoded to be x86_64.
arch = "x86_64"
@@ -870,6 +928,14 @@
return arch + "|" + os
}
-func GetConfigKey(ctx ModuleContext) configKey {
- return configKey{archType: ctx.Arch().ArchType, osType: ctx.Os()}
+func GetConfigKey(ctx BaseModuleContext) configKey {
+ return configKey{
+ // use string because Arch is not a valid key in go
+ arch: ctx.Arch().String(),
+ osType: ctx.Os(),
+ }
+}
+
+func bazelDepsetName(contentHash string) string {
+ return fmt.Sprintf("bazel_depset_%s", contentHash)
}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index ad5b63b..cfdccd7 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -5,25 +5,24 @@
"path/filepath"
"reflect"
"testing"
+
+ "android/soong/bazel/cquery"
)
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
label := "//foo:bar"
- cfg := configKey{Arm64, Android}
+ cfg := configKey{"arm64_armv8-a", Android}
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
- bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64|android>>out/foo/bar.txt`,
+ bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
})
- g, ok := bazelContext.GetOutputFiles(label, cfg)
- if ok {
- t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
- }
+ bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
err := bazelContext.InvokeBazel()
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
- g, ok = bazelContext.GetOutputFiles(label, cfg)
- if !ok {
- t.Errorf("Expected cquery results after running InvokeBazel(), but got none")
+ g, err := bazelContext.GetOutputFiles(label, cfg)
+ if err != nil {
+ t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
t.Errorf("Expected output %s, got %s", w, g)
}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index f353a9d..fa10f62 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -79,6 +79,7 @@
OtherModuleType(m blueprint.Module) string
OtherModuleName(m blueprint.Module) string
OtherModuleDir(m blueprint.Module) string
+ ModuleErrorf(format string, args ...interface{})
}
// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
diff --git a/android/bazel_test.go b/android/bazel_test.go
index e5d8fbb..e14649e 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -13,59 +13,68 @@
// limitations under the License.
package android
-import "testing"
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/android/allowlists"
+ "android/soong/bazel"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
func TestConvertAllModulesInPackage(t *testing.T) {
testCases := []struct {
- prefixes Bp2BuildConfig
+ prefixes allowlists.Bp2BuildConfig
packageDir string
}{
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a",
},
{
- prefixes: Bp2BuildConfig{
- "a/b": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a/b": Bp2BuildDefaultTrueRecursively,
- "a/b/c": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
+ "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultTrueRecursively,
- "d/e/f": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultTrueRecursively,
+ "d/e/f": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultFalse,
- "a/b": Bp2BuildDefaultTrueRecursively,
- "a/b/c": Bp2BuildDefaultFalse,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultFalse,
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
+ "a/b/c": allowlists.Bp2BuildDefaultFalse,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultTrueRecursively,
- "a/b": Bp2BuildDefaultFalse,
- "a/b/c": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultTrueRecursively,
+ "a/b": allowlists.Bp2BuildDefaultFalse,
+ "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a",
},
}
for _, test := range testCases {
- if !bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+ if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); !ok {
t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes)
}
}
@@ -73,62 +82,342 @@
func TestModuleOptIn(t *testing.T) {
testCases := []struct {
- prefixes Bp2BuildConfig
+ prefixes allowlists.Bp2BuildConfig
packageDir string
}{
{
- prefixes: Bp2BuildConfig{
- "a/b": Bp2BuildDefaultFalse,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a/b": allowlists.Bp2BuildDefaultFalse,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultFalse,
- "a/b": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultFalse,
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a",
},
{
- prefixes: Bp2BuildConfig{
- "a/b": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a", // opt-in by default
},
{
- prefixes: Bp2BuildConfig{
- "a/b/c": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultTrueRecursively,
- "d/e/f": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultTrueRecursively,
+ "d/e/f": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "foo/bar",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultTrueRecursively,
- "a/b": Bp2BuildDefaultFalse,
- "a/b/c": Bp2BuildDefaultTrueRecursively,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultTrueRecursively,
+ "a/b": allowlists.Bp2BuildDefaultFalse,
+ "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
- prefixes: Bp2BuildConfig{
- "a": Bp2BuildDefaultFalse,
- "a/b": Bp2BuildDefaultTrueRecursively,
- "a/b/c": Bp2BuildDefaultFalse,
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultFalse,
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
+ "a/b/c": allowlists.Bp2BuildDefaultFalse,
},
packageDir: "a",
},
}
for _, test := range testCases {
- if bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+ if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); ok {
t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
}
}
}
+
+type TestBazelModule struct {
+ bazel.TestModuleInfo
+ BazelModuleBase
+}
+
+var _ blueprint.Module = TestBazelModule{}
+
+func (m TestBazelModule) Name() string {
+ return m.TestModuleInfo.ModuleName
+}
+
+func (m TestBazelModule) GenerateBuildActions(blueprint.ModuleContext) {
+}
+
+type TestBazelConversionContext struct {
+ omc bazel.OtherModuleTestContext
+ allowlist bp2BuildConversionAllowlist
+ errors []string
+}
+
+var _ bazelOtherModuleContext = &TestBazelConversionContext{}
+
+func (bcc *TestBazelConversionContext) OtherModuleType(m blueprint.Module) string {
+ return bcc.omc.OtherModuleType(m)
+}
+
+func (bcc *TestBazelConversionContext) OtherModuleName(m blueprint.Module) string {
+ return bcc.omc.OtherModuleName(m)
+}
+
+func (bcc *TestBazelConversionContext) OtherModuleDir(m blueprint.Module) string {
+ return bcc.omc.OtherModuleDir(m)
+}
+
+func (bcc *TestBazelConversionContext) ModuleErrorf(format string, args ...interface{}) {
+ bcc.errors = append(bcc.errors, fmt.Sprintf(format, args...))
+}
+
+func (bcc *TestBazelConversionContext) Config() Config {
+ return Config{
+ &config{
+ bp2buildPackageConfig: bcc.allowlist,
+ },
+ }
+}
+
+var bazelableBazelModuleBase = BazelModuleBase{
+ bazelProperties: properties{
+ Bazel_module: bazelModuleProperties{
+ CanConvertToBazel: true,
+ },
+ },
+}
+
+func TestBp2BuildAllowlist(t *testing.T) {
+ testCases := []struct {
+ description string
+ shouldConvert bool
+ expectedErrors []string
+ module TestBazelModule
+ allowlist bp2BuildConversionAllowlist
+ }{
+ {
+ description: "allowlist enables module",
+ shouldConvert: true,
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: "dir1",
+ },
+ BazelModuleBase: bazelableBazelModuleBase,
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ },
+ },
+ {
+ description: "module in name allowlist and type allowlist fails",
+ shouldConvert: false,
+ expectedErrors: []string{"A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert"},
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: "dir1",
+ },
+ BazelModuleBase: bazelableBazelModuleBase,
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ moduleTypeAlwaysConvert: map[string]bool{
+ "rule1": true,
+ },
+ },
+ },
+ {
+ description: "module in allowlist and denylist fails",
+ shouldConvert: false,
+ expectedErrors: []string{"a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert"},
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: "dir1",
+ },
+ BazelModuleBase: bazelableBazelModuleBase,
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ moduleDoNotConvert: map[string]bool{
+ "foo": true,
+ },
+ },
+ },
+ {
+ description: "module in allowlist and existing BUILD file",
+ shouldConvert: false,
+ expectedErrors: []string{"A module cannot be in a directory listed in keepExistingBuildFile and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: "existing/build/dir",
+ },
+ BazelModuleBase: bazelableBazelModuleBase,
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ keepExistingBuildFile: map[string]bool{
+ "existing/build/dir": true,
+ },
+ },
+ },
+ {
+ description: "module allowlist and enabled directory",
+ shouldConvert: false,
+ expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: "existing/build/dir",
+ },
+ BazelModuleBase: bazelableBazelModuleBase,
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ defaultConfig: allowlists.Bp2BuildConfig{
+ "existing/build/dir": allowlists.Bp2BuildDefaultTrue,
+ },
+ },
+ },
+ {
+ description: "module allowlist and enabled subdirectory",
+ shouldConvert: false,
+ expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: "existing/build/dir/subdir",
+ },
+ BazelModuleBase: bazelableBazelModuleBase,
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ defaultConfig: allowlists.Bp2BuildConfig{
+ "existing/build/dir": allowlists.Bp2BuildDefaultTrueRecursively,
+ },
+ },
+ },
+ {
+ description: "module enabled in unit test short-circuits other allowlists",
+ shouldConvert: true,
+ module: TestBazelModule{
+ TestModuleInfo: bazel.TestModuleInfo{
+ ModuleName: "foo",
+ Typ: "rule1",
+ Dir: ".",
+ },
+ BazelModuleBase: BazelModuleBase{
+ bazelProperties: properties{
+ Bazel_module: bazelModuleProperties{
+ CanConvertToBazel: true,
+ Bp2build_available: proptools.BoolPtr(true),
+ },
+ },
+ },
+ },
+ allowlist: bp2BuildConversionAllowlist{
+ moduleAlwaysConvert: map[string]bool{
+ "foo": true,
+ },
+ moduleDoNotConvert: map[string]bool{
+ "foo": true,
+ },
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.description, func(t *testing.T) {
+ bcc := &TestBazelConversionContext{
+ omc: bazel.OtherModuleTestContext{
+ Modules: []bazel.TestModuleInfo{
+ test.module.TestModuleInfo,
+ },
+ },
+ allowlist: test.allowlist,
+ }
+
+ shouldConvert := test.module.shouldConvertWithBp2build(bcc, test.module.TestModuleInfo)
+ if test.shouldConvert != shouldConvert {
+ t.Errorf("Module shouldConvert expected to be: %v, but was: %v", test.shouldConvert, shouldConvert)
+ }
+
+ errorsMatch := true
+ if len(test.expectedErrors) != len(bcc.errors) {
+ errorsMatch = false
+ } else {
+ for i, err := range test.expectedErrors {
+ if err != bcc.errors[i] {
+ errorsMatch = false
+ }
+ }
+ }
+ if !errorsMatch {
+ t.Errorf("Expected errors to be: %v, but were: %v", test.expectedErrors, bcc.errors)
+ }
+ })
+ }
+}
+
+func TestBp2buildAllowList(t *testing.T) {
+ allowlist := getBp2BuildAllowList()
+ for k, v := range allowlists.Bp2buildDefaultConfig {
+ if allowlist.defaultConfig[k] != v {
+ t.Errorf("bp2build default config of %s: expected: %v, got: %v", k, v, allowlist.defaultConfig[k])
+ }
+ }
+ for k, v := range allowlists.Bp2buildKeepExistingBuildFile {
+ if allowlist.keepExistingBuildFile[k] != v {
+ t.Errorf("bp2build keep existing build file of %s: expected: %v, got: %v", k, v, allowlist.keepExistingBuildFile[k])
+ }
+ }
+ for _, k := range allowlists.Bp2buildModuleTypeAlwaysConvertList {
+ if !allowlist.moduleTypeAlwaysConvert[k] {
+ t.Errorf("bp2build module type always convert of %s: expected: true, got: %v", k, allowlist.moduleTypeAlwaysConvert[k])
+ }
+ }
+ for _, k := range allowlists.Bp2buildModuleDoNotConvertList {
+ if !allowlist.moduleDoNotConvert[k] {
+ t.Errorf("bp2build module do not convert of %s: expected: true, got: %v", k, allowlist.moduleDoNotConvert[k])
+ }
+ }
+ for _, k := range allowlists.Bp2buildCcLibraryStaticOnlyList {
+ if !allowlist.ccLibraryStaticOnly[k] {
+ t.Errorf("bp2build cc library static only of %s: expected: true, got: %v", k, allowlist.ccLibraryStaticOnly[k])
+ }
+ }
+ for _, k := range allowlists.MixedBuildsDisabledList {
+ if !allowlist.mixedBuildsDisabled[k] {
+ t.Errorf("bp2build mix build disabled of %s: expected: true, got: %v", k, allowlist.mixedBuildsDisabled[k])
+ }
+ }
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
new file mode 100644
index 0000000..6339a71
--- /dev/null
+++ b/android/buildinfo_prop.go
@@ -0,0 +1,182 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ ctx := InitRegistrationContext
+ ctx.RegisterSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
+}
+
+type buildinfoPropProperties struct {
+ // Whether this module is directly installable to one of the partitions. Default: true.
+ Installable *bool
+}
+
+type buildinfoPropModule struct {
+ SingletonModuleBase
+
+ properties buildinfoPropProperties
+
+ outputFilePath OutputPath
+ installPath InstallPath
+}
+
+var _ OutputFileProducer = (*buildinfoPropModule)(nil)
+
+func (p *buildinfoPropModule) installable() bool {
+ return proptools.BoolDefault(p.properties.Installable, true)
+}
+
+// OutputFileProducer
+func (p *buildinfoPropModule) OutputFiles(tag string) (Paths, error) {
+ if tag != "" {
+ return nil, fmt.Errorf("unsupported tag %q", tag)
+ }
+ return Paths{p.outputFilePath}, nil
+}
+
+func (p *buildinfoPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ p.outputFilePath = PathForModuleOut(ctx, p.Name()).OutputPath
+ if !ctx.Config().KatiEnabled() {
+ WriteFileRule(ctx, p.outputFilePath, "# no buildinfo.prop if kati is disabled")
+ return
+ }
+
+ rule := NewRuleBuilder(pctx, ctx)
+ cmd := rule.Command().Text("(")
+
+ writeString := func(str string) {
+ cmd.Text(`echo "` + str + `" && `)
+ }
+
+ writeString("# begin build properties")
+ writeString("# autogenerated by build/soong/android/buildinfo_prop.go")
+
+ writeProp := func(key, value string) {
+ if strings.Contains(key, "=") {
+ panic(fmt.Errorf("wrong property key %q: key must not contain '='", key))
+ }
+ writeString(key + "=" + value)
+ }
+
+ config := ctx.Config()
+
+ writeProp("ro.build.version.sdk", config.PlatformSdkVersion().String())
+ writeProp("ro.build.version.preview_sdk", config.PlatformPreviewSdkVersion())
+ writeProp("ro.build.version.codename", config.PlatformSdkCodename())
+ writeProp("ro.build.version.all_codenames", strings.Join(config.PlatformVersionActiveCodenames(), ","))
+ writeProp("ro.build.version.release", config.PlatformVersionLastStable())
+ writeProp("ro.build.version.release_or_codename", config.PlatformVersionName())
+ writeProp("ro.build.version.security_patch", config.PlatformSecurityPatch())
+ writeProp("ro.build.version.base_os", config.PlatformBaseOS())
+ writeProp("ro.build.version.min_supported_target_sdk", config.PlatformMinSupportedTargetSdkVersion())
+
+ if config.Eng() {
+ writeProp("ro.build.type", "eng")
+ } else if config.Debuggable() {
+ writeProp("ro.build.type", "userdebug")
+ } else {
+ writeProp("ro.build.type", "user")
+ }
+
+ // Currently, only a few properties are implemented to unblock microdroid use case.
+ // TODO(b/189164487): support below properties as well and replace build/make/tools/buildinfo.sh
+ /*
+ if $BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT {
+ writeProp("ro.build.legacy.id", config.BuildID())
+ } else {
+ writeProp("ro.build.id", config.BuildId())
+ }
+ writeProp("ro.build.display.id", $BUILD_DISPLAY_ID)
+ writeProp("ro.build.version.incremental", $BUILD_NUMBER)
+ writeProp("ro.build.version.preview_sdk_fingerprint", $PLATFORM_PREVIEW_SDK_FINGERPRINT)
+ writeProp("ro.build.version.known_codenames", $PLATFORM_VERSION_KNOWN_CODENAMES)
+ writeProp("ro.build.version.release_or_preview_display", $PLATFORM_DISPLAY_VERSION)
+ writeProp("ro.build.date", `$DATE`)
+ writeProp("ro.build.date.utc", `$DATE +%s`)
+ writeProp("ro.build.user", $BUILD_USERNAME)
+ writeProp("ro.build.host", $BUILD_HOSTNAME)
+ writeProp("ro.build.tags", $BUILD_VERSION_TAGS)
+ writeProp("ro.build.flavor", $TARGET_BUILD_FLAVOR)
+ // These values are deprecated, use "ro.product.cpu.abilist"
+ // instead (see below).
+ writeString("# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,")
+ writeString("# use ro.product.cpu.abilist instead.")
+ writeProp("ro.product.cpu.abi", $TARGET_CPU_ABI)
+ if [ -n "$TARGET_CPU_ABI2" ] {
+ writeProp("ro.product.cpu.abi2", $TARGET_CPU_ABI2)
+ }
+
+ if [ -n "$PRODUCT_DEFAULT_LOCALE" ] {
+ writeProp("ro.product.locale", $PRODUCT_DEFAULT_LOCALE)
+ }
+ writeProp("ro.wifi.channels", $PRODUCT_DEFAULT_WIFI_CHANNELS)
+ writeString("# ro.build.product is obsolete; use ro.product.device")
+ writeProp("ro.build.product", $TARGET_DEVICE)
+
+ writeString("# Do not try to parse description or thumbprint")
+ writeProp("ro.build.description", $PRIVATE_BUILD_DESC)
+ if [ -n "$BUILD_THUMBPRINT" ] {
+ writeProp("ro.build.thumbprint", $BUILD_THUMBPRINT)
+ }
+ */
+
+ writeString("# end build properties")
+
+ cmd.Text("true) > ").Output(p.outputFilePath)
+ rule.Build("build.prop", "generating build.prop")
+
+ if !p.installable() {
+ p.SkipInstall()
+ }
+
+ p.installPath = PathForModuleInstall(ctx)
+ ctx.InstallFile(p.installPath, p.Name(), p.outputFilePath)
+}
+
+func (f *buildinfoPropModule) GenerateSingletonBuildActions(ctx SingletonContext) {
+ // does nothing; buildinfo_prop is a singeton because two buildinfo modules don't make sense.
+}
+
+func (p *buildinfoPropModule) AndroidMkEntries() []AndroidMkEntries {
+ return []AndroidMkEntries{AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: OptionalPathForPath(p.outputFilePath),
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installPath.String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
+ },
+ },
+ }}
+}
+
+// buildinfo_prop module generates a build.prop file, which contains a set of common
+// system/build.prop properties, such as ro.build.version.*. Not all properties are implemented;
+// currently this module is only for microdroid.
+func buildinfoPropFactory() SingletonModule {
+ module := &buildinfoPropModule{}
+ module.AddProperties(&module.properties)
+ InitAndroidModule(module)
+ return module
+}
diff --git a/android/config.go b/android/config.go
index 10e074c..eb01baa 100644
--- a/android/config.go
+++ b/android/config.go
@@ -38,6 +38,7 @@
"android/soong/android/soongconfig"
"android/soong/bazel"
"android/soong/remoteexec"
+ "android/soong/starlark_fmt"
)
// Bool re-exports proptools.Bool for the android package.
@@ -157,7 +158,7 @@
mockBpList string
runningAsBp2Build bool
- bp2buildPackageConfig Bp2BuildConfig
+ bp2buildPackageConfig bp2BuildConversionAllowlist
Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
@@ -169,6 +170,10 @@
ninjaFileDepsSet sync.Map
OncePer
+
+ mixedBuildsLock sync.Mutex
+ mixedBuildEnabledModules map[string]struct{}
+ mixedBuildDisabledModules map[string]struct{}
}
type deviceConfig struct {
@@ -286,14 +291,12 @@
}
}
- //TODO(b/216168792) should use common function to print Starlark code
- nonArchVariantProductVariablesJson, err := json.MarshalIndent(&nonArchVariantProductVariables, "", " ")
+ nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
if err != nil {
return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
}
- //TODO(b/216168792) should use common function to print Starlark code
- archVariantProductVariablesJson, err := json.MarshalIndent(&archVariantProductVariables, "", " ")
+ archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
if err != nil {
return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
}
@@ -351,18 +354,20 @@
config := &config{
productVariables: productVariables{
- DeviceName: stringPtr("test_device"),
- Platform_sdk_version: intPtr(30),
- Platform_sdk_codename: stringPtr("S"),
- Platform_version_active_codenames: []string{"S", "Tiramisu"},
- DeviceSystemSdkVersions: []string{"14", "15"},
- Platform_systemsdk_versions: []string{"29", "30"},
- AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
- AAPTPreferredConfig: stringPtr("xhdpi"),
- AAPTCharacteristics: stringPtr("nosdcard"),
- AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
- UncompressPrivAppDex: boolPtr(true),
- ShippingApiLevel: stringPtr("30"),
+ DeviceName: stringPtr("test_device"),
+ DeviceProduct: stringPtr("test_product"),
+ Platform_sdk_version: intPtr(30),
+ Platform_sdk_codename: stringPtr("S"),
+ Platform_base_sdk_extension_version: intPtr(1),
+ Platform_version_active_codenames: []string{"S", "Tiramisu"},
+ DeviceSystemSdkVersions: []string{"14", "15"},
+ Platform_systemsdk_versions: []string{"29", "30"},
+ AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+ AAPTPreferredConfig: stringPtr("xhdpi"),
+ AAPTCharacteristics: stringPtr("nosdcard"),
+ AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
+ UncompressPrivAppDex: boolPtr(true),
+ ShippingApiLevel: stringPtr("30"),
},
outDir: buildDir,
@@ -374,7 +379,9 @@
// passed to PathForSource or PathForModuleSrc.
TestAllowNonExistentPaths: true,
- BazelContext: noopBazelContext{},
+ BazelContext: noopBazelContext{},
+ mixedBuildDisabledModules: make(map[string]struct{}),
+ mixedBuildEnabledModules: make(map[string]struct{}),
}
config.deviceConfig = &deviceConfig{
config: config,
@@ -465,8 +472,10 @@
runGoTests: runGoTests,
multilibConflicts: make(map[ArchType]bool),
- moduleListFile: moduleListFile,
- fs: pathtools.NewOsFs(absSrcDir),
+ moduleListFile: moduleListFile,
+ fs: pathtools.NewOsFs(absSrcDir),
+ mixedBuildDisabledModules: make(map[string]struct{}),
+ mixedBuildEnabledModules: make(map[string]struct{}),
}
config.deviceConfig = &deviceConfig{
@@ -520,7 +529,7 @@
}
if archConfig != nil {
- androidTargets, err := decodeArchSettings(Android, archConfig)
+ androidTargets, err := decodeAndroidArchSettings(archConfig)
if err != nil {
return Config{}, err
}
@@ -549,7 +558,7 @@
}
config.BazelContext, err = NewBazelContext(config)
- config.bp2buildPackageConfig = bp2buildDefaultConfig
+ config.bp2buildPackageConfig = getBp2BuildAllowList()
return Config{config}, err
}
@@ -689,6 +698,10 @@
return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
}
+func (c *config) TargetsJava17() bool {
+ return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_17")
+}
+
// EnvDeps returns the environment variables this build depends on. The first
// call to this function blocks future reads from the environment.
func (c *config) EnvDeps() map[string]string {
@@ -723,6 +736,15 @@
return *c.productVariables.DeviceName
}
+// DeviceProduct returns the current product target. There could be multiple of
+// these per device type.
+//
+// NOTE: Do not base conditional logic on this value. It may break product
+// inheritance.
+func (c *config) DeviceProduct() string {
+ return *c.productVariables.DeviceProduct
+}
+
func (c *config) DeviceResourceOverlays() []string {
return c.productVariables.DeviceResourceOverlays
}
@@ -743,6 +765,14 @@
return String(c.productVariables.Platform_sdk_codename)
}
+func (c *config) PlatformSdkExtensionVersion() int {
+ return *c.productVariables.Platform_sdk_extension_version
+}
+
+func (c *config) PlatformBaseSdkExtensionVersion() int {
+ return *c.productVariables.Platform_base_sdk_extension_version
+}
+
func (c *config) PlatformSecurityPatch() string {
return String(c.productVariables.Platform_security_patch)
}
@@ -759,8 +789,12 @@
return String(c.productVariables.Platform_base_os)
}
+func (c *config) PlatformVersionLastStable() string {
+ return String(c.productVariables.Platform_version_last_stable)
+}
+
func (c *config) MinSupportedSdkVersion() ApiLevel {
- return uncheckedFinalApiLevel(16)
+ return uncheckedFinalApiLevel(19)
}
func (c *config) FinalApiLevels() []ApiLevel {
@@ -1248,6 +1282,10 @@
return Bool(c.config.productVariables.ClangCoverage)
}
+func (c *deviceConfig) ClangCoverageContinuousMode() bool {
+ return Bool(c.config.productVariables.ClangCoverageContinuousMode)
+}
+
func (c *deviceConfig) GcovCoverageEnabled() bool {
return Bool(c.config.productVariables.GcovCoverage)
}
@@ -1338,6 +1376,10 @@
return "", false
}
+func (c *deviceConfig) ApexGlobalMinSdkVersionOverride() string {
+ return String(c.config.productVariables.ApexGlobalMinSdkVersionOverride)
+}
+
func (c *config) IntegerOverflowDisabledForPath(path string) bool {
if len(c.productVariables.IntegerOverflowExcludePaths) == 0 {
return false
@@ -1456,6 +1498,10 @@
return c.productVariables.MissingUsesLibraries
}
+func (c *config) TargetMultitreeUpdateMeta() bool {
+ return c.productVariables.MultitreeUpdateMeta
+}
+
func (c *deviceConfig) DeviceArch() string {
return String(c.config.productVariables.DeviceArch)
}
@@ -1531,6 +1577,18 @@
return c.config.productVariables.BoardProductPrivatePrebuiltDirs
}
+func (c *deviceConfig) SystemExtSepolicyPrebuiltApiDir() string {
+ return String(c.config.productVariables.SystemExtSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) ProductSepolicyPrebuiltApiDir() string {
+ return String(c.config.productVariables.ProductSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool {
+ return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
+}
+
func (c *deviceConfig) DirectedVendorSnapshot() bool {
return c.config.productVariables.DirectedVendorSnapshot
}
@@ -1631,6 +1689,10 @@
return c.config.productVariables.BuildBrokenVendorPropertyNamespace
}
+func (c *deviceConfig) BuildBrokenInputDir(name string) bool {
+ return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
+}
+
func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
}
@@ -1983,3 +2045,19 @@
func (c *config) RBEWrapper() string {
return c.GetenvWithDefault("RBE_WRAPPER", remoteexec.DefaultWrapperPath)
}
+
+// UseHostMusl returns true if the host target has been configured to build against musl libc.
+func (c *config) UseHostMusl() bool {
+ return Bool(c.productVariables.HostMusl)
+}
+
+func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) {
+ moduleName := ctx.Module().Name()
+ c.mixedBuildsLock.Lock()
+ defer c.mixedBuildsLock.Unlock()
+ if useBazel {
+ c.mixedBuildEnabledModules[moduleName] = struct{}{}
+ } else {
+ c.mixedBuildDisabledModules[moduleName] = struct{}{}
+ }
+}
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
new file mode 100644
index 0000000..748be62
--- /dev/null
+++ b/android/config_bp2build.go
@@ -0,0 +1,487 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+ "sort"
+ "strings"
+
+ "android/soong/bazel"
+ "android/soong/starlark_fmt"
+
+ "github.com/google/blueprint"
+)
+
+// BazelVarExporter is a collection of configuration variables that can be exported for use in Bazel rules
+type BazelVarExporter interface {
+ // asBazel expands strings of configuration variables into their concrete values
+ asBazel(Config, ExportedStringVariables, ExportedStringListVariables, ExportedConfigDependingVariables) []bazelConstant
+}
+
+// ExportedVariables is a collection of interdependent configuration variables
+type ExportedVariables struct {
+ // Maps containing toolchain variables that are independent of the
+ // environment variables of the build.
+ exportedStringVars ExportedStringVariables
+ exportedStringListVars ExportedStringListVariables
+ exportedStringListDictVars ExportedStringListDictVariables
+
+ exportedVariableReferenceDictVars ExportedVariableReferenceDictVariables
+
+ /// Maps containing variables that are dependent on the build config.
+ exportedConfigDependingVars ExportedConfigDependingVariables
+
+ pctx PackageContext
+}
+
+// NewExportedVariables creats an empty ExportedVariables struct with non-nil maps
+func NewExportedVariables(pctx PackageContext) ExportedVariables {
+ return ExportedVariables{
+ exportedStringVars: ExportedStringVariables{},
+ exportedStringListVars: ExportedStringListVariables{},
+ exportedStringListDictVars: ExportedStringListDictVariables{},
+ exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{},
+ exportedConfigDependingVars: ExportedConfigDependingVariables{},
+ pctx: pctx,
+ }
+}
+
+func (ev ExportedVariables) asBazel(config Config,
+ stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
+ ret := []bazelConstant{}
+ ret = append(ret, ev.exportedStringVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+ ret = append(ret, ev.exportedStringListVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+ ret = append(ret, ev.exportedStringListDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+ // Note: ExportedVariableReferenceDictVars collections can only contain references to other variables and must be printed last
+ ret = append(ret, ev.exportedVariableReferenceDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+ return ret
+}
+
+// ExportStringStaticVariable declares a static string variable and exports it to
+// Bazel's toolchain.
+func (ev ExportedVariables) ExportStringStaticVariable(name string, value string) {
+ ev.pctx.StaticVariable(name, value)
+ ev.exportedStringVars.set(name, value)
+}
+
+// ExportStringListStaticVariable declares a static variable and exports it to
+// Bazel's toolchain.
+func (ev ExportedVariables) ExportStringListStaticVariable(name string, value []string) {
+ ev.pctx.StaticVariable(name, strings.Join(value, " "))
+ ev.exportedStringListVars.set(name, value)
+}
+
+// ExportVariableConfigMethod declares a variable whose value is evaluated at
+// runtime via a function with access to the Config and exports it to Bazel's
+// toolchain.
+func (ev ExportedVariables) ExportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
+ ev.exportedConfigDependingVars.set(name, method)
+ return ev.pctx.VariableConfigMethod(name, method)
+}
+
+// ExportSourcePathVariable declares a static "source path" variable and exports
+// it to Bazel's toolchain.
+func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) {
+ ev.pctx.SourcePathVariable(name, value)
+ ev.exportedStringVars.set(name, value)
+}
+
+// ExportVariableFuncVariable declares a variable whose value is evaluated at
+// runtime via a function and exports it to Bazel's toolchain.
+func (ev ExportedVariables) ExportVariableFuncVariable(name string, f func() string) {
+ ev.exportedConfigDependingVars.set(name, func(config Config) string {
+ return f()
+ })
+ ev.pctx.VariableFunc(name, func(PackageVarContext) string {
+ return f()
+ })
+}
+
+// ExportString only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportString(name string, value string) {
+ ev.exportedStringVars.set(name, value)
+}
+
+// ExportStringList only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportStringList(name string, value []string) {
+ ev.exportedStringListVars.set(name, value)
+}
+
+// ExportStringListDict only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportStringListDict(name string, value map[string][]string) {
+ ev.exportedStringListDictVars.set(name, value)
+}
+
+// ExportVariableReferenceDict only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportVariableReferenceDict(name string, value map[string]string) {
+ ev.exportedVariableReferenceDictVars.set(name, value)
+}
+
+// ExportedConfigDependingVariables is a mapping of variable names to functions
+// of type func(config Config) string which return the runtime-evaluated string
+// value of a particular variable
+type ExportedConfigDependingVariables map[string]interface{}
+
+func (m ExportedConfigDependingVariables) set(k string, v interface{}) {
+ m[k] = v
+}
+
+// Ensure that string s has no invalid characters to be generated into the bzl file.
+func validateCharacters(s string) string {
+ for _, c := range []string{`\n`, `"`, `\`} {
+ if strings.Contains(s, c) {
+ panic(fmt.Errorf("%s contains illegal character %s", s, c))
+ }
+ }
+ return s
+}
+
+type bazelConstant struct {
+ variableName string
+ internalDefinition string
+ sortLast bool
+}
+
+// ExportedStringVariables is a mapping of variable names to string values
+type ExportedStringVariables map[string]string
+
+func (m ExportedStringVariables) set(k string, v string) {
+ m[k] = v
+}
+
+func (m ExportedStringVariables) asBazel(config Config,
+ stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for k, variableValue := range m {
+ expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
+ if err != nil {
+ panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
+ }
+ if len(expandedVar) > 1 {
+ panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
+ }
+ ret = append(ret, bazelConstant{
+ variableName: k,
+ internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
+ })
+ }
+ return ret
+}
+
+// ExportedStringListVariables is a mapping of variable names to a list of strings
+type ExportedStringListVariables map[string][]string
+
+func (m ExportedStringListVariables) set(k string, v []string) {
+ m[k] = v
+}
+
+func (m ExportedStringListVariables) asBazel(config Config,
+ stringScope ExportedStringVariables, stringListScope ExportedStringListVariables,
+ exportedVars ExportedConfigDependingVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ // For each exported variable, recursively expand elements in the variableValue
+ // list to ensure that interpolated variables are expanded according to their values
+ // in the variable scope.
+ for k, variableValue := range m {
+ var expandedVars []string
+ for _, v := range variableValue {
+ expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
+ if err != nil {
+ panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
+ }
+ expandedVars = append(expandedVars, expandedVar...)
+ }
+ // Assign the list as a bzl-private variable; this variable will be exported
+ // out through a constants struct later.
+ ret = append(ret, bazelConstant{
+ variableName: k,
+ internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
+ })
+ }
+ return ret
+}
+
+// ExportedStringListDictVariables is a mapping from variable names to a
+// dictionary which maps keys to lists of strings
+type ExportedStringListDictVariables map[string]map[string][]string
+
+func (m ExportedStringListDictVariables) set(k string, v map[string][]string) {
+ m[k] = v
+}
+
+// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
+func (m ExportedStringListDictVariables) asBazel(_ Config, _ ExportedStringVariables,
+ _ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for k, dict := range m {
+ ret = append(ret, bazelConstant{
+ variableName: k,
+ internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
+ })
+ }
+ return ret
+}
+
+// ExportedVariableReferenceDictVariables is a mapping from variable names to a
+// dictionary which references previously defined variables. This is used to
+// create a Starlark output such as:
+// string_var1 = "string1
+// var_ref_dict_var1 = {
+// "key1": string_var1
+// }
+// This type of variable collection must be expanded last so that it recognizes
+// previously defined variables.
+type ExportedVariableReferenceDictVariables map[string]map[string]string
+
+func (m ExportedVariableReferenceDictVariables) set(k string, v map[string]string) {
+ m[k] = v
+}
+
+func (m ExportedVariableReferenceDictVariables) asBazel(_ Config, _ ExportedStringVariables,
+ _ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for n, dict := range m {
+ for k, v := range dict {
+ matches, err := variableReference(v)
+ if err != nil {
+ panic(err)
+ } else if !matches.matches {
+ panic(fmt.Errorf("Expected a variable reference, got %q", v))
+ } else if len(matches.fullVariableReference) != len(v) {
+ panic(fmt.Errorf("Expected only a variable reference, got %q", v))
+ }
+ dict[k] = "_" + matches.variable
+ }
+ ret = append(ret, bazelConstant{
+ variableName: n,
+ internalDefinition: starlark_fmt.PrintDict(dict, 0),
+ sortLast: true,
+ })
+ }
+ return ret
+}
+
+// BazelToolchainVars expands an ExportedVariables collection and returns a string
+// of formatted Starlark variable definitions
+func BazelToolchainVars(config Config, exportedVars ExportedVariables) string {
+ results := exportedVars.asBazel(
+ config,
+ exportedVars.exportedStringVars,
+ exportedVars.exportedStringListVars,
+ exportedVars.exportedConfigDependingVars,
+ )
+
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].sortLast != results[j].sortLast {
+ return !results[i].sortLast
+ }
+ return results[i].variableName < results[j].variableName
+ })
+
+ definitions := make([]string, 0, len(results))
+ constants := make([]string, 0, len(results))
+ for _, b := range results {
+ definitions = append(definitions,
+ fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
+ constants = append(constants,
+ fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
+ }
+
+ // Build the exported constants struct.
+ ret := bazel.GeneratedBazelFileWarning
+ ret += "\n\n"
+ ret += strings.Join(definitions, "\n\n")
+ ret += "\n\n"
+ ret += "constants = struct(\n"
+ ret += strings.Join(constants, "\n")
+ ret += "\n)"
+
+ return ret
+}
+
+type match struct {
+ matches bool
+ fullVariableReference string
+ variable string
+}
+
+func variableReference(input string) (match, error) {
+ // e.g. "${ExternalCflags}"
+ r := regexp.MustCompile(`\${(?:config\.)?([a-zA-Z0-9_]+)}`)
+
+ matches := r.FindStringSubmatch(input)
+ if len(matches) == 0 {
+ return match{}, nil
+ }
+ if len(matches) != 2 {
+ return match{}, fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", input, len(matches)-1)
+ }
+ return match{
+ matches: true,
+ fullVariableReference: matches[0],
+ // Index 1 of FindStringSubmatch contains the subexpression match
+ // (variable name) of the capture group.
+ variable: matches[1],
+ }, nil
+}
+
+// expandVar recursively expand interpolated variables in the exportedVars scope.
+//
+// We're using a string slice to track the seen variables to avoid
+// stackoverflow errors with infinite recursion. it's simpler to use a
+// string slice than to handle a pass-by-referenced map, which would make it
+// quite complex to track depth-first interpolations. It's also unlikely the
+// interpolation stacks are deep (n > 1).
+func expandVar(config Config, toExpand string, stringScope ExportedStringVariables,
+ stringListScope ExportedStringListVariables, exportedVars ExportedConfigDependingVariables) ([]string, error) {
+
+ // Internal recursive function.
+ var expandVarInternal func(string, map[string]bool) (string, error)
+ expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
+ var ret string
+ remainingString := toExpand
+ for len(remainingString) > 0 {
+ matches, err := variableReference(remainingString)
+ if err != nil {
+ panic(err)
+ }
+ if !matches.matches {
+ return ret + remainingString, nil
+ }
+ matchIndex := strings.Index(remainingString, matches.fullVariableReference)
+ ret += remainingString[:matchIndex]
+ remainingString = remainingString[matchIndex+len(matches.fullVariableReference):]
+
+ variable := matches.variable
+ // toExpand contains a variable.
+ if _, ok := seenVars[variable]; ok {
+ return ret, fmt.Errorf(
+ "Unbounded recursive interpolation of variable: %s", variable)
+ }
+ // A map is passed-by-reference. Create a new map for
+ // this scope to prevent variables seen in one depth-first expansion
+ // to be also treated as "seen" in other depth-first traversals.
+ newSeenVars := map[string]bool{}
+ for k := range seenVars {
+ newSeenVars[k] = true
+ }
+ newSeenVars[variable] = true
+ if unexpandedVars, ok := stringListScope[variable]; ok {
+ expandedVars := []string{}
+ for _, unexpandedVar := range unexpandedVars {
+ expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+ if err != nil {
+ return ret, err
+ }
+ expandedVars = append(expandedVars, expandedVar)
+ }
+ ret += strings.Join(expandedVars, " ")
+ } else if unexpandedVar, ok := stringScope[variable]; ok {
+ expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+ if err != nil {
+ return ret, err
+ }
+ ret += expandedVar
+ } else if unevaluatedVar, ok := exportedVars[variable]; ok {
+ evalFunc := reflect.ValueOf(unevaluatedVar)
+ validateVariableMethod(variable, evalFunc)
+ evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
+ evaluatedValue := evaluatedResult[0].Interface().(string)
+ expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
+ if err != nil {
+ return ret, err
+ }
+ ret += expandedVar
+ } else {
+ return "", fmt.Errorf("Unbound config variable %s", variable)
+ }
+ }
+ return ret, nil
+ }
+ var ret []string
+ stringFields := splitStringKeepingQuotedSubstring(toExpand, ' ')
+ for _, v := range stringFields {
+ val, err := expandVarInternal(v, map[string]bool{})
+ if err != nil {
+ return ret, err
+ }
+ ret = append(ret, val)
+ }
+
+ return ret, nil
+}
+
+// splitStringKeepingQuotedSubstring splits a string on a provided separator,
+// but it will not split substrings inside unescaped double quotes. If the double
+// quotes are escaped, then the returned string will only include the quote, and
+// not the escape.
+func splitStringKeepingQuotedSubstring(s string, delimiter byte) []string {
+ var ret []string
+ quote := byte('"')
+
+ var substring []byte
+ quoted := false
+ escaped := false
+
+ for i := range s {
+ if !quoted && s[i] == delimiter {
+ ret = append(ret, string(substring))
+ substring = []byte{}
+ continue
+ }
+
+ characterIsEscape := i < len(s)-1 && s[i] == '\\' && s[i+1] == quote
+ if characterIsEscape {
+ escaped = true
+ continue
+ }
+
+ if s[i] == quote {
+ if !escaped {
+ quoted = !quoted
+ }
+ escaped = false
+ }
+
+ substring = append(substring, s[i])
+ }
+
+ ret = append(ret, string(substring))
+
+ return ret
+}
+
+func validateVariableMethod(name string, methodValue reflect.Value) {
+ methodType := methodValue.Type()
+ if methodType.Kind() != reflect.Func {
+ panic(fmt.Errorf("method given for variable %s is not a function",
+ name))
+ }
+ if n := methodType.NumIn(); n != 1 {
+ panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
+ name, n))
+ }
+ if n := methodType.NumOut(); n != 1 {
+ panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
+ name, n))
+ }
+ if kind := methodType.Out(0).Kind(); kind != reflect.String {
+ panic(fmt.Errorf("method for variable %s does not return a string",
+ name))
+ }
+}
diff --git a/android/config_bp2build_test.go b/android/config_bp2build_test.go
new file mode 100644
index 0000000..1a0ba7b
--- /dev/null
+++ b/android/config_bp2build_test.go
@@ -0,0 +1,454 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "android/soong/bazel"
+ "testing"
+)
+
+func TestExpandVars(t *testing.T) {
+ android_arm64_config := TestConfig("out", nil, "", nil)
+ android_arm64_config.BuildOS = Android
+ android_arm64_config.BuildArch = Arm64
+
+ testCases := []struct {
+ description string
+ config Config
+ stringScope ExportedStringVariables
+ stringListScope ExportedStringListVariables
+ configVars ExportedConfigDependingVariables
+ toExpand string
+ expectedValues []string
+ }{
+ {
+ description: "no expansion for non-interpolated value",
+ toExpand: "foo",
+ expectedValues: []string{"foo"},
+ },
+ {
+ description: "single level expansion for string var",
+ stringScope: ExportedStringVariables{
+ "foo": "bar",
+ },
+ toExpand: "${foo}",
+ expectedValues: []string{"bar"},
+ },
+ {
+ description: "single level expansion with short-name for string var",
+ stringScope: ExportedStringVariables{
+ "foo": "bar",
+ },
+ toExpand: "${config.foo}",
+ expectedValues: []string{"bar"},
+ },
+ {
+ description: "single level expansion string list var",
+ stringListScope: ExportedStringListVariables{
+ "foo": []string{"bar"},
+ },
+ toExpand: "${foo}",
+ expectedValues: []string{"bar"},
+ },
+ {
+ description: "mixed level expansion for string list var",
+ stringScope: ExportedStringVariables{
+ "foo": "${bar}",
+ "qux": "hello",
+ },
+ stringListScope: ExportedStringListVariables{
+ "bar": []string{"baz", "${qux}"},
+ },
+ toExpand: "${foo}",
+ expectedValues: []string{"baz hello"},
+ },
+ {
+ description: "double level expansion",
+ stringListScope: ExportedStringListVariables{
+ "foo": []string{"${bar}"},
+ "bar": []string{"baz"},
+ },
+ toExpand: "${foo}",
+ expectedValues: []string{"baz"},
+ },
+ {
+ description: "double level expansion with a literal",
+ stringListScope: ExportedStringListVariables{
+ "a": []string{"${b}", "c"},
+ "b": []string{"d"},
+ },
+ toExpand: "${a}",
+ expectedValues: []string{"d c"},
+ },
+ {
+ description: "double level expansion, with two variables in a string",
+ stringListScope: ExportedStringListVariables{
+ "a": []string{"${b} ${c}"},
+ "b": []string{"d"},
+ "c": []string{"e"},
+ },
+ toExpand: "${a}",
+ expectedValues: []string{"d e"},
+ },
+ {
+ description: "triple level expansion with two variables in a string",
+ stringListScope: ExportedStringListVariables{
+ "a": []string{"${b} ${c}"},
+ "b": []string{"${c}", "${d}"},
+ "c": []string{"${d}"},
+ "d": []string{"foo"},
+ },
+ toExpand: "${a}",
+ expectedValues: []string{"foo foo foo"},
+ },
+ {
+ description: "expansion with config depending vars",
+ configVars: ExportedConfigDependingVariables{
+ "a": func(c Config) string { return c.BuildOS.String() },
+ "b": func(c Config) string { return c.BuildArch.String() },
+ },
+ config: android_arm64_config,
+ toExpand: "${a}-${b}",
+ expectedValues: []string{"android-arm64"},
+ },
+ {
+ description: "double level multi type expansion",
+ stringListScope: ExportedStringListVariables{
+ "platform": []string{"${os}-${arch}"},
+ "const": []string{"const"},
+ },
+ configVars: ExportedConfigDependingVariables{
+ "os": func(c Config) string { return c.BuildOS.String() },
+ "arch": func(c Config) string { return c.BuildArch.String() },
+ "foo": func(c Config) string { return "foo" },
+ },
+ config: android_arm64_config,
+ toExpand: "${const}/${platform}/${foo}",
+ expectedValues: []string{"const/android-arm64/foo"},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.description, func(t *testing.T) {
+ output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
+ if len(output) != len(testCase.expectedValues) {
+ t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
+ }
+ for i, actual := range output {
+ expectedValue := testCase.expectedValues[i]
+ if actual != expectedValue {
+ t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
+ }
+ }
+ })
+ }
+}
+
+func TestBazelToolchainVars(t *testing.T) {
+ testCases := []struct {
+ name string
+ config Config
+ vars ExportedVariables
+ expectedOut string
+ }{
+ {
+ name: "exports strings",
+ vars: ExportedVariables{
+ exportedStringVars: ExportedStringVariables{
+ "a": "b",
+ "c": "d",
+ },
+ },
+ expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = "b"
+
+_c = "d"
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "exports string lists",
+ vars: ExportedVariables{
+ exportedStringListVars: ExportedStringListVariables{
+ "a": []string{"b1", "b2"},
+ "c": []string{"d1", "d2"},
+ },
+ },
+ expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = [
+ "b1",
+ "b2",
+]
+
+_c = [
+ "d1",
+ "d2",
+]
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "exports string lists dicts",
+ vars: ExportedVariables{
+ exportedStringListDictVars: ExportedStringListDictVariables{
+ "a": map[string][]string{"b1": {"b2"}},
+ "c": map[string][]string{"d1": {"d2"}},
+ },
+ },
+ expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = {
+ "b1": ["b2"],
+}
+
+_c = {
+ "d1": ["d2"],
+}
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "exports dict with var refs",
+ vars: ExportedVariables{
+ exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
+ "a": map[string]string{"b1": "${b2}"},
+ "c": map[string]string{"d1": "${config.d2}"},
+ },
+ },
+ expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = {
+ "b1": _b2,
+}
+
+_c = {
+ "d1": _d2,
+}
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "sorts across types with variable references last",
+ vars: ExportedVariables{
+ exportedStringVars: ExportedStringVariables{
+ "b": "b-val",
+ "d": "d-val",
+ },
+ exportedStringListVars: ExportedStringListVariables{
+ "c": []string{"c-val"},
+ "e": []string{"e-val"},
+ },
+ exportedStringListDictVars: ExportedStringListDictVariables{
+ "a": map[string][]string{"a1": {"a2"}},
+ "f": map[string][]string{"f1": {"f2"}},
+ },
+ exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
+ "aa": map[string]string{"b1": "${b}"},
+ "cc": map[string]string{"d1": "${config.d}"},
+ },
+ },
+ expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = {
+ "a1": ["a2"],
+}
+
+_b = "b-val"
+
+_c = ["c-val"]
+
+_d = "d-val"
+
+_e = ["e-val"]
+
+_f = {
+ "f1": ["f2"],
+}
+
+_aa = {
+ "b1": _b,
+}
+
+_cc = {
+ "d1": _d,
+}
+
+constants = struct(
+ a = _a,
+ b = _b,
+ c = _c,
+ d = _d,
+ e = _e,
+ f = _f,
+ aa = _aa,
+ cc = _cc,
+)`,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ out := BazelToolchainVars(tc.config, tc.vars)
+ if out != tc.expectedOut {
+ t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
+ }
+ })
+ }
+}
+
+func TestSplitStringKeepingQuotedSubstring(t *testing.T) {
+ testCases := []struct {
+ description string
+ s string
+ delimiter byte
+ split []string
+ }{
+ {
+ description: "empty string returns single empty string",
+ s: "",
+ delimiter: ' ',
+ split: []string{
+ "",
+ },
+ },
+ {
+ description: "string with single space returns two empty strings",
+ s: " ",
+ delimiter: ' ',
+ split: []string{
+ "",
+ "",
+ },
+ },
+ {
+ description: "string with two spaces returns three empty strings",
+ s: " ",
+ delimiter: ' ',
+ split: []string{
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ description: "string with four words returns four word string",
+ s: "hello world with words",
+ delimiter: ' ',
+ split: []string{
+ "hello",
+ "world",
+ "with",
+ "words",
+ },
+ },
+ {
+ description: "string with words and nested quote returns word strings and quote string",
+ s: `hello "world with" words`,
+ delimiter: ' ',
+ split: []string{
+ "hello",
+ `"world with"`,
+ "words",
+ },
+ },
+ {
+ description: "string with escaped quote inside real quotes",
+ s: `hello \"world "with\" words"`,
+ delimiter: ' ',
+ split: []string{
+ "hello",
+ `"world`,
+ `"with" words"`,
+ },
+ },
+ {
+ description: "string with words and escaped quotes returns word strings",
+ s: `hello \"world with\" words`,
+ delimiter: ' ',
+ split: []string{
+ "hello",
+ `"world`,
+ `with"`,
+ "words",
+ },
+ },
+ {
+ description: "string which is single quoted substring returns only substring",
+ s: `"hello world with words"`,
+ delimiter: ' ',
+ split: []string{
+ `"hello world with words"`,
+ },
+ },
+ {
+ description: "string starting with quote returns quoted string",
+ s: `"hello world with" words`,
+ delimiter: ' ',
+ split: []string{
+ `"hello world with"`,
+ "words",
+ },
+ },
+ {
+ description: "string with starting quote and no ending quote returns quote to end of string",
+ s: `hello "world with words`,
+ delimiter: ' ',
+ split: []string{
+ "hello",
+ `"world with words`,
+ },
+ },
+ {
+ description: "quoted string is treated as a single \"word\" unless separated by delimiter",
+ s: `hello "world"with words`,
+ delimiter: ' ',
+ split: []string{
+ "hello",
+ `"world"with`,
+ "words",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ split := splitStringKeepingQuotedSubstring(tc.s, tc.delimiter)
+ if len(split) != len(tc.split) {
+ t.Fatalf("number of split string elements (%d) differs from expected (%d): split string (%v), expected (%v)",
+ len(split), len(tc.split), split, tc.split,
+ )
+ }
+ for i := range split {
+ if split[i] != tc.split[i] {
+ t.Errorf("split string element (%d), %v, differs from expected, %v", i, split[i], tc.split[i])
+ }
+ }
+ })
+ }
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index c932ffa..14ed783 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -18,6 +18,9 @@
"strings"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
+
+ "github.com/google/blueprint"
)
func init() {
@@ -28,6 +31,11 @@
ctx.RegisterModuleType("filegroup", FileGroupFactory)
})
+// IsFilegroup checks that a module is a filegroup type
+func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool {
+ return ctx.OtherModuleType(m) == "filegroup"
+}
+
// https://docs.bazel.build/versions/master/be/general.html#filegroup
type bazelFilegroupAttributes struct {
Srcs bazel.LabelListAttribute
@@ -94,6 +102,7 @@
srcs Paths
}
+var _ MixedBuildBuildable = (*fileGroup)(nil)
var _ SourceFileProducer = (*fileGroup)(nil)
// filegroup contains a list of files that are referenced by other modules
@@ -107,33 +116,21 @@
return module
}
-func (fg *fileGroup) maybeGenerateBazelBuildActions(ctx ModuleContext) {
- if !fg.MixedBuildsEnabled(ctx) {
- return
- }
+var _ blueprint.JSONActionSupplier = (*fileGroup)(nil)
- archVariant := ctx.Arch().ArchType
- osVariant := ctx.Os()
- if len(fg.Srcs()) == 1 && fg.Srcs()[0].Base() == fg.Name() {
- // This will be a regular file target, not filegroup, in Bazel.
- // See FilegroupBp2Build for more information.
- archVariant = Common
- osVariant = CommonOS
+func (fg *fileGroup) JSONActions() []blueprint.JSONAction {
+ ins := make([]string, 0, len(fg.srcs))
+ outs := make([]string, 0, len(fg.srcs))
+ for _, p := range fg.srcs {
+ ins = append(ins, p.String())
+ outs = append(outs, p.Rel())
}
-
- bazelCtx := ctx.Config().BazelContext
- filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{archVariant, osVariant})
- if !ok {
- return
+ return []blueprint.JSONAction{
+ blueprint.JSONAction{
+ Inputs: ins,
+ Outputs: outs,
+ },
}
-
- bazelOuts := make(Paths, 0, len(filePaths))
- for _, p := range filePaths {
- src := PathForBazelOut(ctx, p)
- bazelOuts = append(bazelOuts, src)
- }
-
- fg.srcs = bazelOuts
}
func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -141,8 +138,6 @@
if fg.properties.Path != nil {
fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
}
-
- fg.maybeGenerateBazelBuildActions(ctx)
}
func (fg *fileGroup) Srcs() Paths {
@@ -154,3 +149,38 @@
ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
}
}
+
+func (fg *fileGroup) QueueBazelCall(ctx BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+
+ bazelCtx.QueueBazelRequest(
+ fg.GetBazelLabel(ctx, fg),
+ cquery.GetOutputFiles,
+ configKey{Common.String(), CommonOS})
+}
+
+func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
+ return true
+}
+
+func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) {
+ fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
+ if fg.properties.Path != nil {
+ fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
+ }
+
+ bazelCtx := ctx.Config().BazelContext
+ filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{Common.String(), CommonOS})
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ bazelOuts := make(Paths, 0, len(filePaths))
+ for _, p := range filePaths {
+ src := PathForBazelOut(ctx, p)
+ bazelOuts = append(bazelOuts, src)
+ }
+
+ fg.srcs = bazelOuts
+}
diff --git a/android/fixture.go b/android/fixture.go
index 728f031..0690a5a 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -586,6 +586,18 @@
})
}
+// FixtureExpectsOneErrorPattern returns an error handler that will cause the test to fail
+// if there is more than one error or the error does not match the pattern.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+func FixtureExpectsOneErrorPattern(pattern string) FixtureErrorHandler {
+ return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
+ t.Helper()
+ CheckErrorsAgainstExpectations(t, result.Errs, []string{pattern})
+ })
+}
+
// FixtureCustomErrorHandler creates a custom error handler
func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler {
return simpleErrorHandler{
diff --git a/android/gen_notice.go b/android/gen_notice.go
new file mode 100644
index 0000000..fda91ac
--- /dev/null
+++ b/android/gen_notice.go
@@ -0,0 +1,207 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ RegisterGenNoticeBuildComponents(InitRegistrationContext)
+}
+
+// Register the gen_notice module type.
+func RegisterGenNoticeBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
+ ctx.RegisterModuleType("gen_notice", GenNoticeFactory)
+}
+
+type genNoticeBuildRules struct{}
+
+func (s *genNoticeBuildRules) GenerateBuildActions(ctx SingletonContext) {
+ ctx.VisitAllModules(func(m Module) {
+ gm, ok := m.(*genNoticeModule)
+ if !ok {
+ return
+ }
+ if len(gm.missing) > 0 {
+ missingReferencesRule(ctx, gm)
+ return
+ }
+ out := BuildNoticeTextOutputFromLicenseMetadata
+ if proptools.Bool(gm.properties.Xml) {
+ out = BuildNoticeXmlOutputFromLicenseMetadata
+ } else if proptools.Bool(gm.properties.Html) {
+ out = BuildNoticeHtmlOutputFromLicenseMetadata
+ }
+ defaultName := ""
+ if len(gm.properties.For) > 0 {
+ defaultName = gm.properties.For[0]
+ }
+
+ modules := make([]Module, 0)
+ for _, name := range gm.properties.For {
+ mods := ctx.ModuleVariantsFromName(gm, name)
+ for _, mod := range mods {
+ if mod == nil {
+ continue
+ }
+ modules = append(modules, mod)
+ }
+ }
+ if ctx.Failed() {
+ return
+ }
+ out(ctx, gm.output, ctx.ModuleName(gm), proptools.StringDefault(gm.properties.ArtifactName, defaultName), "", modules...)
+ })
+}
+
+func GenNoticeBuildRulesFactory() Singleton {
+ return &genNoticeBuildRules{}
+}
+
+type genNoticeProperties struct {
+ // For specifies the modules for which to generate a notice file.
+ For []string
+ // ArtifactName specifies the internal name to use for the notice file.
+ // It appears in the "used by:" list for targets whose entire name is stripped by --strip_prefix.
+ ArtifactName *string
+ // Stem specifies the base name of the output file.
+ Stem *string `android:"arch_variant"`
+ // Html indicates an html-format file is needed. The default is text. Can be Html or Xml but not both.
+ Html *bool
+ // Xml indicates an xml-format file is needed. The default is text. Can be Html or Xml but not both.
+ Xml *bool
+ // Gzipped indicates the output file must be compressed with gzip. Will append .gz to suffix if not there.
+ Gzipped *bool
+ // Suffix specifies the file extension to use. Defaults to .html for html, .xml for xml, or no extension for text.
+ Suffix *string
+ // Visibility specifies where this license can be used
+ Visibility []string
+}
+
+type genNoticeModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties genNoticeProperties
+
+ output OutputPath
+ missing []string
+}
+
+func (m *genNoticeModule) DepsMutator(ctx BottomUpMutatorContext) {
+ if proptools.Bool(m.properties.Html) && proptools.Bool(m.properties.Xml) {
+ ctx.ModuleErrorf("can be html or xml but not both")
+ }
+ if !ctx.Config().AllowMissingDependencies() {
+ var missing []string
+ // Verify the modules for which to generate notices exist.
+ for _, otherMod := range m.properties.For {
+ if !ctx.OtherModuleExists(otherMod) {
+ missing = append(missing, otherMod)
+ }
+ }
+ if len(missing) == 1 {
+ ctx.PropertyErrorf("for", "no %q module exists", missing[0])
+ } else if len(missing) > 1 {
+ ctx.PropertyErrorf("for", "modules \"%s\" do not exist", strings.Join(missing, "\", \""))
+ }
+ }
+}
+
+func (m *genNoticeModule) getStem() string {
+ stem := m.base().BaseModuleName()
+ if m.properties.Stem != nil {
+ stem = proptools.String(m.properties.Stem)
+ }
+ return stem
+}
+
+func (m *genNoticeModule) getSuffix() string {
+ suffix := ""
+ if m.properties.Suffix == nil {
+ if proptools.Bool(m.properties.Html) {
+ suffix = ".html"
+ } else if proptools.Bool(m.properties.Xml) {
+ suffix = ".xml"
+ }
+ } else {
+ suffix = proptools.String(m.properties.Suffix)
+ }
+ if proptools.Bool(m.properties.Gzipped) && !strings.HasSuffix(suffix, ".gz") {
+ suffix += ".gz"
+ }
+ return suffix
+}
+
+func (m *genNoticeModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ if ctx.Config().AllowMissingDependencies() {
+ // Verify the modules for which to generate notices exist.
+ for _, otherMod := range m.properties.For {
+ if !ctx.OtherModuleExists(otherMod) {
+ m.missing = append(m.missing, otherMod)
+ }
+ }
+ m.missing = append(m.missing, ctx.GetMissingDependencies()...)
+ m.missing = FirstUniqueStrings(m.missing)
+ }
+ out := m.getStem() + m.getSuffix()
+ m.output = PathForModuleOut(ctx, out).OutputPath
+}
+
+func GenNoticeFactory() Module {
+ module := &genNoticeModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
+
+var _ OutputFileProducer = (*genNoticeModule)(nil)
+
+// Implements OutputFileProducer
+func (m *genNoticeModule) OutputFiles(tag string) (Paths, error) {
+ if tag == "" {
+ return Paths{m.output}, nil
+ }
+ return nil, fmt.Errorf("unrecognized tag %q", tag)
+}
+
+// missingReferencesRule emits an ErrorRule for missing module references.
+func missingReferencesRule(ctx BuilderContext, m *genNoticeModule) {
+ if len(m.missing) < 1 {
+ panic(fmt.Errorf("missing references rule requested with no missing references"))
+ }
+
+ ctx.Build(pctx, BuildParams{
+ Rule: ErrorRule,
+ Output: m.output,
+ Description: "notice for " + proptools.StringDefault(m.properties.ArtifactName, "container"),
+ Args: map[string]string{
+ "error": m.Name() + " references missing module(s): " + strings.Join(m.missing, ", "),
+ },
+ })
+}
diff --git a/android/gen_notice_test.go b/android/gen_notice_test.go
new file mode 100644
index 0000000..4ad2ecf
--- /dev/null
+++ b/android/gen_notice_test.go
@@ -0,0 +1,164 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var genNoticeTests = []struct {
+ name string
+ fs MockFS
+ expectedErrors []string
+}{
+ {
+ name: "gen_notice must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ gen_notice {
+ name: "top_license",
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "bad gen_notice",
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ gen_notice {
+ name: "top_notice",
+ for: ["top_rule"],
+ }`),
+ "other/Android.bp": []byte(`
+ mock_genrule {
+ name: "other_rule",
+ dep: ["top_notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "top_notice": for: no "top_rule" module exists`,
+ },
+ },
+ {
+ name: "doubly bad gen_notice",
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ gen_notice {
+ name: "top_notice",
+ for: ["top_rule", "other_rule"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "top_notice": for: modules "top_rule", "other_rule" do not exist`,
+ },
+ },
+ {
+ name: "good gen_notice",
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ gen_notice {
+ name: "top_notice",
+ for: ["top_rule"],
+ }
+
+ mock_genrule {
+ name: "top_rule",
+ dep: ["top_notice"],
+ }`),
+ "other/Android.bp": []byte(`
+ mock_genrule {
+ name: "other_rule",
+ dep: ["top_notice"],
+ }`),
+ },
+ },
+ {
+ name: "multiple license kinds",
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ gen_notice {
+ name: "top_notice",
+ for: ["top_rule"],
+ }
+
+ gen_notice {
+ name: "top_html_notice",
+ html: true,
+ for: ["top_rule"],
+ }
+
+ gen_notice {
+ name: "top_xml_notice",
+ xml: true,
+ for: ["top_notice"],
+ }
+
+ mock_genrule {
+ name: "top_rule",
+ dep: [
+ "top_notice",
+ "top_html_notice",
+ "top_xml_notice",
+ ],
+ }`),
+ "other/Android.bp": []byte(`
+ mock_genrule {
+ name: "other_rule",
+ dep: ["top_xml_notice"],
+ }`),
+ },
+ },
+}
+
+func TestGenNotice(t *testing.T) {
+ for _, test := range genNoticeTests {
+ t.Run(test.name, func(t *testing.T) {
+ GroupFixturePreparers(
+ PrepareForTestWithGenNotice,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("mock_genrule", newMockGenruleModule)
+ }),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
+ })
+ }
+}
+
+type mockGenruleProperties struct {
+ Dep []string
+}
+
+type mockGenruleModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties mockGenruleProperties
+}
+
+func newMockGenruleModule() Module {
+ m := &mockGenruleModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type genruleDepTag struct {
+ blueprint.BaseDependencyTag
+}
+
+func (j *mockGenruleModule) DepsMutator(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+ ctx.AddDependency(m, genruleDepTag{}, j.properties.Dep...)
+}
+
+func (p *mockGenruleModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/hooks.go b/android/hooks.go
index bded764..2ad3b5f 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -15,7 +15,10 @@
package android
import (
+ "fmt"
+ "path"
"reflect"
+ "runtime"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -86,12 +89,33 @@
l.appendPrependHelper(props, proptools.PrependMatchingProperties)
}
-func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
- inherited := []interface{}{&l.Module().base().commonProperties}
- module := l.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
+func (l *loadHookContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
+ return l.bp.CreateModule(factory, name, props...)
+}
- if l.Module().base().variableProperties != nil && module.base().variableProperties != nil {
- src := l.Module().base().variableProperties
+type createModuleContext interface {
+ Module() Module
+ createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module
+}
+
+func createModule(ctx createModuleContext, factory ModuleFactory, ext string, props ...interface{}) Module {
+ inherited := []interface{}{&ctx.Module().base().commonProperties}
+
+ var typeName string
+ if typeNameLookup, ok := ModuleTypeByFactory()[reflect.ValueOf(factory)]; ok {
+ typeName = typeNameLookup
+ } else {
+ factoryPtr := reflect.ValueOf(factory).Pointer()
+ factoryFunc := runtime.FuncForPC(factoryPtr)
+ filePath, _ := factoryFunc.FileLine(factoryPtr)
+ typeName = fmt.Sprintf("%s_%s", path.Base(filePath), factoryFunc.Name())
+ }
+ typeName = typeName + "_" + ext
+
+ module := ctx.createModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module)
+
+ if ctx.Module().base().variableProperties != nil && module.base().variableProperties != nil {
+ src := ctx.Module().base().variableProperties
dst := []interface{}{
module.base().variableProperties,
// Put an empty copy of the src properties into dst so that properties in src that are not in dst
@@ -107,6 +131,10 @@
return module
}
+func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+ return createModule(l, factory, "_loadHookModule", props...)
+}
+
func (l *loadHookContext) registerScopedModuleType(name string, factory blueprint.ModuleFactory) {
l.bp.RegisterScopedModuleType(name, factory)
}
diff --git a/android/license.go b/android/license.go
index 587cb36..ebee055 100644
--- a/android/license.go
+++ b/android/license.go
@@ -63,7 +63,7 @@
func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
// license modules have no licenses, but license_kinds must refer to license_kind modules
mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
- mergePathProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, m.properties.License_text)...)
+ namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...)
for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
if lk, ok := module.(*licenseKindModule); ok {
mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index 2ce921b..b17defe 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -90,7 +90,10 @@
// Populate the properties from the variant.
l := variant.(*licenseModule)
p.License_kinds = l.properties.License_kinds
- p.License_text = l.base().commonProperties.Effective_license_text
+ p.License_text = make(Paths, 0, len(l.base().commonProperties.Effective_license_text))
+ for _, np := range l.base().commonProperties.Effective_license_text {
+ p.License_text = append(p.License_text, np.Path)
+ }
}
func (p *licenseSdkMemberProperties) AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet) {
diff --git a/android/licenses.go b/android/licenses.go
index e9e271b..c47b3e6 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -213,7 +213,7 @@
m.base().commonProperties.Effective_package_name = l.properties.Package_name
}
mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
- mergePathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+ mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
} else {
@@ -239,10 +239,24 @@
*prop = SortedUniqueStrings(*prop)
}
-// Update a property Path array with a distinct union of its values and a list of new values.
-func mergePathProps(prop *Paths, values ...Path) {
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func namePathProps(prop *NamedPaths, name *string, values ...Path) {
+ if name == nil {
+ for _, value := range values {
+ *prop = append(*prop, NamedPath{value, ""})
+ }
+ } else {
+ for _, value := range values {
+ *prop = append(*prop, NamedPath{value, *name})
+ }
+ }
+ *prop = SortedUniqueNamedPaths(*prop)
+}
+
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) {
*prop = append(*prop, values...)
- *prop = SortedUniquePaths(*prop)
+ *prop = SortedUniqueNamedPaths(*prop)
}
// Get the licenses property falling back to the package default.
@@ -289,6 +303,7 @@
switch reflect.TypeOf(module).String() {
case "*android.licenseModule": // is a license, doesn't need one
case "*android.licenseKindModule": // is a license, doesn't need one
+ case "*android.genNoticeModule": // contains license texts as data
case "*android.NamespaceModule": // just partitions things, doesn't add anything
case "*android.soongConfigModuleTypeModule": // creates aliases for modules with licenses
case "*android.soongConfigModuleTypeImport": // creates aliases for modules with licenses
@@ -316,4 +331,13 @@
func licensesMakeVarsProvider(ctx MakeVarsContext) {
ctx.Strict("BUILD_LICENSE_METADATA",
ctx.Config().HostToolPath(ctx, "build_license_metadata").String())
+ ctx.Strict("COPY_LICENSE_METADATA",
+ ctx.Config().HostToolPath(ctx, "copy_license_metadata").String())
+ ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
+ ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
+ ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
+ ctx.Strict("COMPLIANCENOTICE_BOM", ctx.Config().HostToolPath(ctx, "compliancenotice_bom").String())
+ ctx.Strict("COMPLIANCENOTICE_SHIPPEDLIBS", ctx.Config().HostToolPath(ctx, "compliancenotice_shippedlibs").String())
+ ctx.Strict("COMPLIANCE_LISTSHARE", ctx.Config().HostToolPath(ctx, "compliance_listshare").String())
+ ctx.Strict("COMPLIANCE_CHECKSHARE", ctx.Config().HostToolPath(ctx, "compliance_checkshare").String())
}
diff --git a/android/licenses_test.go b/android/licenses_test.go
index 70160fa..8a81e12 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -90,9 +90,9 @@
"libother": []string{"shownotice"},
},
effectiveNotices: map[string][]string{
- "libexample1": []string{"top/LICENSE", "top/NOTICE"},
- "libnested": []string{"top/LICENSE", "top/NOTICE"},
- "libother": []string{"top/LICENSE", "top/NOTICE"},
+ "libexample1": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+ "libnested": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+ "libother": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
},
},
diff --git a/android/metrics.go b/android/metrics.go
index 2cd5efa..1580f82 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -17,7 +17,9 @@
import (
"io/ioutil"
"runtime"
+ "sort"
+ "github.com/google/blueprint/metrics"
"google.golang.org/protobuf/proto"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -55,7 +57,7 @@
})
}
-func collectMetrics(config Config) *soong_metrics_proto.SoongBuildMetrics {
+func collectMetrics(config Config, eventHandler metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics {
metrics := &soong_metrics_proto.SoongBuildMetrics{}
soongMetrics := ReadSoongMetrics(config)
@@ -68,11 +70,38 @@
metrics.TotalAllocCount = proto.Uint64(memStats.Mallocs)
metrics.TotalAllocSize = proto.Uint64(memStats.TotalAlloc)
+ for _, event := range eventHandler.CompletedEvents() {
+ perfInfo := soong_metrics_proto.PerfInfo{
+ Description: proto.String(event.Id),
+ Name: proto.String("soong_build"),
+ StartTime: proto.Uint64(uint64(event.Start.UnixNano())),
+ RealTime: proto.Uint64(event.RuntimeNanoseconds()),
+ }
+ metrics.Events = append(metrics.Events, &perfInfo)
+ }
+ mixedBuildsInfo := soong_metrics_proto.MixedBuildsInfo{}
+ mixedBuildEnabledModules := make([]string, 0, len(config.mixedBuildEnabledModules))
+ for module, _ := range config.mixedBuildEnabledModules {
+ mixedBuildEnabledModules = append(mixedBuildEnabledModules, module)
+ }
+
+ mixedBuildDisabledModules := make([]string, 0, len(config.mixedBuildDisabledModules))
+ for module, _ := range config.mixedBuildDisabledModules {
+ mixedBuildDisabledModules = append(mixedBuildDisabledModules, module)
+ }
+ // Sorted for deterministic output.
+ sort.Strings(mixedBuildEnabledModules)
+ sort.Strings(mixedBuildDisabledModules)
+
+ mixedBuildsInfo.MixedBuildEnabledModules = mixedBuildEnabledModules
+ mixedBuildsInfo.MixedBuildDisabledModules = mixedBuildDisabledModules
+ metrics.MixedBuildsInfo = &mixedBuildsInfo
+
return metrics
}
-func WriteMetrics(config Config, metricsFile string) error {
- metrics := collectMetrics(config)
+func WriteMetrics(config Config, eventHandler metrics.EventHandler, metricsFile string) error {
+ metrics := collectMetrics(config, eventHandler)
buf, err := proto.Marshal(metrics)
if err != nil {
diff --git a/android/module.go b/android/module.go
index 00aed95..8bbfd8a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,11 +16,13 @@
import (
"fmt"
+ "net/url"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
+ "sort"
"strings"
"text/scanner"
@@ -454,6 +456,10 @@
// GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods,
// but do not exist.
GetMissingDependencies() []string
+
+ // LicenseMetadataFile returns the path where the license metadata for this module will be
+ // generated.
+ LicenseMetadataFile() Path
}
type Module interface {
@@ -607,6 +613,12 @@
// A suffix to add to the artifact file name (before any extension).
Suffix *string `android:"arch_variant"`
+ // If true, then the artifact file will be appended with _<product name>. For
+ // example, if the product is coral and the module is an android_app module
+ // of name foo, then the artifact would be foo_coral.apk. If false, there is
+ // no change to the artifact file name.
+ Append_artifact_with_product *bool `android:"arch_variant"`
+
// A string tag to select the OutputFiles associated with the tag.
//
// If no tag is specified then it will select the default dist paths provided
@@ -616,6 +628,53 @@
Tag *string `android:"arch_variant"`
}
+// NamedPath associates a path with a name. e.g. a license text path with a package name
+type NamedPath struct {
+ Path Path
+ Name string
+}
+
+// String returns an escaped string representing the `NamedPath`.
+func (p NamedPath) String() string {
+ if len(p.Name) > 0 {
+ return p.Path.String() + ":" + url.QueryEscape(p.Name)
+ }
+ return p.Path.String()
+}
+
+// NamedPaths describes a list of paths each associated with a name.
+type NamedPaths []NamedPath
+
+// Strings returns a list of escaped strings representing each `NamedPath` in the list.
+func (l NamedPaths) Strings() []string {
+ result := make([]string, 0, len(l))
+ for _, p := range l {
+ result = append(result, p.String())
+ }
+ return result
+}
+
+// SortedUniqueNamedPaths modifies `l` in place to return the sorted unique subset.
+func SortedUniqueNamedPaths(l NamedPaths) NamedPaths {
+ if len(l) == 0 {
+ return l
+ }
+ sort.Slice(l, func(i, j int) bool {
+ return l[i].String() < l[j].String()
+ })
+ k := 0
+ for i := 1; i < len(l); i++ {
+ if l[i].String() == l[k].String() {
+ continue
+ }
+ k++
+ if k < i {
+ l[k] = l[i]
+ }
+ }
+ return l[:k+1]
+}
+
type nameProperties struct {
// The name of the module. Must be unique across all modules.
Name *string
@@ -684,7 +743,7 @@
// Override of module name when reporting licenses
Effective_package_name *string `blueprint:"mutated"`
// Notice files
- Effective_license_text Paths `blueprint:"mutated"`
+ Effective_license_text NamedPaths `blueprint:"mutated"`
// License names
Effective_license_kinds []string `blueprint:"mutated"`
// License conditions
@@ -1122,33 +1181,89 @@
archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
var enabledProperty bazel.BoolAttribute
- if props.Enabled != nil {
- enabledProperty.Value = props.Enabled
+
+ onlyAndroid := false
+ neitherHostNorDevice := false
+
+ osSupport := map[string]bool{}
+
+ // if the target is enabled and supports arch variance, determine the defaults based on the module
+ // type's host or device property and host_supported/device_supported properties
+ if mod.commonProperties.ArchSpecific {
+ moduleSupportsDevice := mod.DeviceSupported()
+ moduleSupportsHost := mod.HostSupported()
+ if moduleSupportsHost && !moduleSupportsDevice {
+ // for host only, we specify as unsupported on android rather than listing all host osSupport
+ // TODO(b/220874839): consider replacing this with a constraint that covers all host osSupport
+ // instead
+ enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
+ } else if moduleSupportsDevice && !moduleSupportsHost {
+ enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(true))
+ // specify as a positive to ensure any target-specific enabled can be resolved
+ // also save that a target is only android, as if there is only the positive restriction on
+ // android, it'll be dropped, so we may need to add it back later
+ onlyAndroid = true
+ } else if !moduleSupportsHost && !moduleSupportsDevice {
+ neitherHostNorDevice = true
+ }
+
+ for _, os := range OsTypeList() {
+ if os.Class == Host {
+ osSupport[os.Name] = moduleSupportsHost
+ } else if os.Class == Device {
+ osSupport[os.Name] = moduleSupportsDevice
+ }
+ }
+ }
+
+ if neitherHostNorDevice {
+ // we can't build this, disable
+ enabledProperty.Value = proptools.BoolPtr(false)
+ } else if props.Enabled != nil {
+ enabledProperty.SetValue(props.Enabled)
+ if !*props.Enabled {
+ for os, enabled := range osSupport {
+ if val := enabledProperty.SelectValue(bazel.OsConfigurationAxis, os); enabled && val != nil && *val {
+ // if this should be disabled by default, clear out any enabling we've done
+ enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, os, nil)
+ }
+ }
+ }
}
for axis, configToProps := range archVariantProps {
for config, _props := range configToProps {
if archProps, ok := _props.(*commonProperties); ok {
required.SetSelectValue(axis, config, depsToLabelList(archProps.Required).Value)
- if archProps.Enabled != nil {
- enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+ if !neitherHostNorDevice {
+ if archProps.Enabled != nil {
+ if axis != bazel.OsConfigurationAxis || osSupport[config] {
+ enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+ }
+ }
}
}
}
}
- if enabledPropertyOverrides.Value != nil {
- enabledProperty.Value = enabledPropertyOverrides.Value
- }
- for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
- configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
- for cfg, val := range configToBools {
- enabledProperty.SetSelectValue(axis, cfg, &val)
+ if !neitherHostNorDevice {
+ if enabledPropertyOverrides.Value != nil {
+ enabledProperty.Value = enabledPropertyOverrides.Value
+ }
+ for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
+ configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
+ for cfg, val := range configToBools {
+ if axis != bazel.OsConfigurationAxis || osSupport[cfg] {
+ enabledProperty.SetSelectValue(axis, cfg, &val)
+ }
+ }
}
}
productConfigEnabledLabels := []bazel.Label{}
- if !proptools.BoolDefault(enabledProperty.Value, true) {
+ // TODO(b/234497586): Soong config variables and product variables have different overriding behavior, we
+ // should handle it correctly
+ if !proptools.BoolDefault(enabledProperty.Value, true) && !neitherHostNorDevice {
// If the module is not enabled by default, then we can check if a
// product variable enables it
productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
@@ -1172,6 +1287,13 @@
ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
}
+ // if android is the only arch/os enabled, then add a restriction to only be compatible with android
+ if platformEnabledAttribute.IsNil() && onlyAndroid {
+ l := bazel.LabelAttribute{}
+ l.SetValue(bazel.Label{Label: bazel.OsConfigurationAxis.SelectKey(Android.Name)})
+ platformEnabledAttribute.Add(&l)
+ }
+
data.Append(required)
constraints := constraintAttributes{}
@@ -1406,8 +1528,10 @@
}
type propInfo struct {
- Name string
- Type string
+ Name string
+ Type string
+ Value string
+ Values []string
}
func (m *ModuleBase) propertiesWithValues() []propInfo {
@@ -1447,18 +1571,60 @@
return
}
elKind := v.Type().Elem().Kind()
- info = append(info, propInfo{name, elKind.String() + " " + kind.String()})
+ info = append(info, propInfo{Name: name, Type: elKind.String() + " " + kind.String(), Values: sliceReflectionValue(v)})
default:
- info = append(info, propInfo{name, kind.String()})
+ info = append(info, propInfo{Name: name, Type: kind.String(), Value: reflectionValue(v)})
}
}
for _, p := range props {
propsWithValues("", reflect.ValueOf(p).Elem())
}
+ sort.Slice(info, func(i, j int) bool {
+ return info[i].Name < info[j].Name
+ })
return info
}
+func reflectionValue(value reflect.Value) string {
+ switch value.Kind() {
+ case reflect.Bool:
+ return fmt.Sprintf("%t", value.Bool())
+ case reflect.Int64:
+ return fmt.Sprintf("%d", value.Int())
+ case reflect.String:
+ return fmt.Sprintf("%s", value.String())
+ case reflect.Struct:
+ if value.IsZero() {
+ return "{}"
+ }
+ length := value.NumField()
+ vals := make([]string, length, length)
+ for i := 0; i < length; i++ {
+ sTyp := value.Type().Field(i)
+ if proptools.ShouldSkipProperty(sTyp) {
+ continue
+ }
+ name := sTyp.Name
+ vals[i] = fmt.Sprintf("%s: %s", name, reflectionValue(value.Field(i)))
+ }
+ return fmt.Sprintf("%s{%s}", value.Type(), strings.Join(vals, ", "))
+ case reflect.Array, reflect.Slice:
+ vals := sliceReflectionValue(value)
+ return fmt.Sprintf("[%s]", strings.Join(vals, ", "))
+ }
+ return ""
+}
+
+func sliceReflectionValue(value reflect.Value) []string {
+ length := value.Len()
+ vals := make([]string, length, length)
+ for i := 0; i < length; i++ {
+ vals[i] = reflectionValue(value.Index(i))
+ }
+ return vals
+}
+
func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
@@ -1796,7 +1962,11 @@
}
func (m *ModuleBase) EffectiveLicenseFiles() Paths {
- return m.commonProperties.Effective_license_text
+ result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
+ for _, p := range m.commonProperties.Effective_license_text {
+ result = append(result, p.Path)
+ }
+ return result
}
// computeInstallDeps finds the installed paths of all dependencies that have a dependency
@@ -2163,7 +2333,11 @@
return
}
- m.module.GenerateAndroidBuildActions(ctx)
+ if mixedBuildMod, handled := m.isHandledByBazel(ctx); handled {
+ mixedBuildMod.ProcessBazelQueryResponse(ctx)
+ } else {
+ m.module.GenerateAndroidBuildActions(ctx)
+ }
if ctx.Failed() {
return
}
@@ -2219,6 +2393,18 @@
m.variables = ctx.variables
}
+func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
+ if !ctx.Config().BazelContext.BazelEnabled() {
+ return nil, false
+ }
+ if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
+ if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
+ return mixedBuildMod, true
+ }
+ }
+ return nil, false
+}
+
// Check the supplied dist structure to make sure that it is valid.
//
// property - the base property, e.g. dist or dists[1], which is combined with the
@@ -2334,7 +2520,7 @@
bazelConversionMode bool
}
-func (b *baseModuleContext) BazelConversionMode() bool {
+func (b *baseModuleContext) isBazelConversionMode() bool {
return b.bazelConversionMode
}
func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
@@ -2723,7 +2909,7 @@
}
func (b *baseModuleContext) ModuleFromName(name string) (blueprint.Module, bool) {
- if !b.BazelConversionMode() {
+ if !b.isBazelConversionMode() {
panic("cannot call ModuleFromName if not in bazel conversion mode")
}
if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" {
@@ -3051,6 +3237,7 @@
symlinkTarget: "",
executable: executable,
effectiveLicenseFiles: &licenseFiles,
+ partition: fullInstallPath.partition,
}
m.packagingSpecs = append(m.packagingSpecs, spec)
return spec
@@ -3168,6 +3355,7 @@
srcPath: nil,
symlinkTarget: relPath,
executable: false,
+ partition: fullInstallPath.partition,
})
return fullInstallPath
@@ -3208,6 +3396,7 @@
srcPath: nil,
symlinkTarget: absPath,
executable: false,
+ partition: fullInstallPath.partition,
})
return fullInstallPath
@@ -3221,6 +3410,10 @@
return m.bp
}
+func (m *moduleContext) LicenseMetadataFile() Path {
+ return m.module.base().licenseMetadataFile
+}
+
// SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name"
// into the module name, or empty string if the input was not a module reference.
func SrcIsModule(s string) (module string) {
@@ -3615,6 +3808,8 @@
Installed_paths []string `json:"installed,omitempty"`
SrcJars []string `json:"srcjars,omitempty"`
Paths []string `json:"path,omitempty"`
+ Static_libs []string `json:"static_libs,omitempty"`
+ Libs []string `json:"libs,omitempty"`
}
func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents string) []error {
diff --git a/android/module_test.go b/android/module_test.go
index c35e66e..77ef146 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,12 +15,9 @@
package android
import (
- "bytes"
"path/filepath"
"runtime"
"testing"
-
- mkparser "android/soong/androidmk/parser"
)
func TestSrcIsModule(t *testing.T) {
@@ -475,21 +472,10 @@
prepareForModuleTests,
PrepareForTestWithArchMutator,
FixtureModifyConfig(SetKatiEnabledForTests),
- FixtureRegisterWithContext(func(ctx RegistrationContext) {
- ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
- }),
+ PrepareForTestWithMakevars,
).RunTestWithBp(t, bp)
- installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
- buf := bytes.NewBuffer(append([]byte(nil), installs...))
- parser := mkparser.NewParser("makevars", buf)
-
- nodes, errs := parser.Parse()
- if len(errs) > 0 {
- t.Fatalf("error parsing install rules: %s", errs[0])
- }
-
- rules := parseMkRules(t, result.Config, nodes)
+ rules := result.InstallMakeRulesForTesting(t)
module := func(name string, host bool) TestingModule {
variant := "android_common"
@@ -501,125 +487,88 @@
outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
- ruleForOutput := func(output string) installMakeRule {
+ ruleForOutput := func(output string) InstallMakeRule {
for _, rule := range rules {
- if rule.target == output {
+ if rule.Target == output {
return rule
}
}
t.Fatalf("no make install rule for %s", output)
- return installMakeRule{}
+ return InstallMakeRule{}
}
- installRule := func(name string) installMakeRule {
+ installRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
}
- symlinkRule := func(name string) installMakeRule {
+ symlinkRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
}
hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
- hostInstallRule := func(name string) installMakeRule {
+ hostInstallRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/host/linux-x86", name))
}
- hostSymlinkRule := func(name string) installMakeRule {
+ hostSymlinkRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
}
- assertDeps := func(rule installMakeRule, deps ...string) {
+ assertDeps := func(rule InstallMakeRule, deps ...string) {
t.Helper()
- AssertArrayString(t, "expected inputs", deps, rule.deps)
+ AssertArrayString(t, "expected inputs", deps, rule.Deps)
}
- assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
+ assertOrderOnlys := func(rule InstallMakeRule, orderonlys ...string) {
t.Helper()
- AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
+ AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.OrderOnlyDeps)
}
// Check host install rule dependencies
assertDeps(hostInstallRule("foo"),
hostOutputRule("foo").Output.String(),
- hostInstallRule("bar").target,
- hostSymlinkRule("bar").target,
- hostInstallRule("baz").target,
- hostSymlinkRule("baz").target,
- hostInstallRule("qux").target,
- hostSymlinkRule("qux").target,
+ hostInstallRule("bar").Target,
+ hostSymlinkRule("bar").Target,
+ hostInstallRule("baz").Target,
+ hostSymlinkRule("baz").Target,
+ hostInstallRule("qux").Target,
+ hostSymlinkRule("qux").Target,
)
assertOrderOnlys(hostInstallRule("foo"))
// Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
// order-only dependency, so that the tool gets updated when the symlink is depended on.
- assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").target)
+ assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").Target)
assertOrderOnlys(hostSymlinkRule("foo"))
// Check device install rule dependencies
assertDeps(installRule("foo"), outputRule("foo").Output.String())
assertOrderOnlys(installRule("foo"),
- installRule("bar").target,
- symlinkRule("bar").target,
- installRule("baz").target,
- symlinkRule("baz").target,
- installRule("qux").target,
- symlinkRule("qux").target,
+ installRule("bar").Target,
+ symlinkRule("bar").Target,
+ installRule("baz").Target,
+ symlinkRule("baz").Target,
+ installRule("qux").Target,
+ symlinkRule("qux").Target,
)
// Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
// but the current implementation uses a normal dependency.
- assertDeps(symlinkRule("foo"), installRule("foo").target)
+ assertDeps(symlinkRule("foo"), installRule("foo").Target)
assertOrderOnlys(symlinkRule("foo"))
}
-type installMakeRule struct {
- target string
- deps []string
- orderOnlyDeps []string
-}
-
-func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
- var rules []installMakeRule
- for _, node := range nodes {
- if mkParserRule, ok := node.(*mkparser.Rule); ok {
- var rule installMakeRule
-
- if targets := mkParserRule.Target.Words(); len(targets) == 0 {
- t.Fatalf("no targets for rule %s", mkParserRule.Dump())
- } else if len(targets) > 1 {
- t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
- } else if !targets[0].Const() {
- t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
- } else {
- rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
- }
-
- prereqList := &rule.deps
- for _, prereq := range mkParserRule.Prerequisites.Words() {
- if !prereq.Const() {
- t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
- }
-
- if prereq.Value(nil) == "|" {
- prereqList = &rule.orderOnlyDeps
- continue
- }
-
- *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
- }
-
- rules = append(rules, rule)
- }
- }
-
- return rules
-}
-
type PropsTestModuleEmbedded struct {
Embedded_prop *string
}
+type StructInSlice struct {
+ G string
+ H bool
+ I []string
+}
+
type propsTestModule struct {
ModuleBase
DefaultableModuleBase
@@ -636,6 +585,8 @@
E *string
}
F *string `blueprint:"mutated"`
+
+ Slice_of_struct []StructInSlice
}
}
@@ -678,7 +629,7 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Name", "string"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -691,10 +642,10 @@
}
`,
expectedProps: []propInfo{
- propInfo{"A", "string"},
- propInfo{"B", "bool"},
- propInfo{"D", "int64"},
- propInfo{"Name", "string"},
+ propInfo{Name: "A", Type: "string", Value: "abc"},
+ propInfo{Name: "B", Type: "bool", Value: "true"},
+ propInfo{Name: "D", Type: "int64", Value: "123"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -707,10 +658,10 @@
`,
expectedProps: []propInfo{
// for non-pointer cannot distinguish between unused and intentionally set to empty
- propInfo{"A", "string"},
- propInfo{"B", "bool"},
- propInfo{"D", "int64"},
- propInfo{"Name", "string"},
+ propInfo{Name: "A", Type: "string", Value: ""},
+ propInfo{Name: "B", Type: "bool", Value: "true"},
+ propInfo{Name: "D", Type: "int64", Value: "123"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -723,8 +674,8 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Nested.E", "string"},
- propInfo{"Name", "string"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ propInfo{Name: "Nested.E", Type: "string", Value: "abc"},
},
},
{
@@ -739,8 +690,8 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Name", "string"},
- propInfo{"Arch.X86_64.A", "string"},
+ propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "abc"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -751,8 +702,34 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Embedded_prop", "string"},
- propInfo{"Name", "string"},
+ propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ },
+ },
+ {
+ desc: "struct slice",
+ bp: `test {
+ name: "foo",
+ slice_of_struct: [
+ {
+ g: "abc",
+ h: false,
+ i: ["baz"],
+ },
+ {
+ g: "def",
+ h: true,
+ i: [],
+ },
+ ]
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ propInfo{Name: "Slice_of_struct", Type: "struct slice", Values: []string{
+ `android.StructInSlice{G: abc, H: false, I: [baz]}`,
+ `android.StructInSlice{G: def, H: true, I: []}`,
+ }},
},
},
{
@@ -762,19 +739,20 @@
name: "foo_defaults",
a: "a",
b: true,
+ c: ["default_c"],
embedded_prop:"a",
arch: {
x86_64: {
- a: "a",
+ a: "x86_64 a",
},
},
}
test {
name: "foo",
defaults: ["foo_defaults"],
- c: ["a"],
+ c: ["c"],
nested: {
- e: "d",
+ e: "nested e",
},
target: {
linux: {
@@ -784,15 +762,15 @@
}
`,
expectedProps: []propInfo{
- propInfo{"A", "string"},
- propInfo{"B", "bool"},
- propInfo{"C", "string slice"},
- propInfo{"Embedded_prop", "string"},
- propInfo{"Nested.E", "string"},
- propInfo{"Name", "string"},
- propInfo{"Arch.X86_64.A", "string"},
- propInfo{"Target.Linux.A", "string"},
- propInfo{"Defaults", "string slice"},
+ propInfo{Name: "A", Type: "string", Value: "a"},
+ propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"},
+ propInfo{Name: "B", Type: "bool", Value: "true"},
+ propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}},
+ propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}},
+ propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ propInfo{Name: "Nested.E", Type: "string", Value: "nested e"},
+ propInfo{Name: "Target.Linux.A", Type: "string", Value: "a"},
},
},
}
@@ -816,3 +794,120 @@
})
}
}
+
+func TestSortedUniqueNamedPaths(t *testing.T) {
+ type np struct {
+ path, name string
+ }
+ makePaths := func(l []np) NamedPaths {
+ result := make(NamedPaths, 0, len(l))
+ for _, p := range l {
+ result = append(result, NamedPath{PathForTesting(p.path), p.name})
+ }
+ return result
+ }
+
+ tests := []struct {
+ name string
+ in []np
+ expectedOut []np
+ }{
+ {
+ name: "empty",
+ in: []np{},
+ expectedOut: []np{},
+ },
+ {
+ name: "all_same",
+ in: []np{
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ },
+ expectedOut: []np{
+ {"a.txt", "A"},
+ },
+ },
+ {
+ name: "same_path_different_names",
+ in: []np{
+ {"a.txt", "C"},
+ {"a.txt", "A"},
+ {"a.txt", "D"},
+ {"a.txt", "B"},
+ {"a.txt", "E"},
+ },
+ expectedOut: []np{
+ {"a.txt", "A"},
+ {"a.txt", "B"},
+ {"a.txt", "C"},
+ {"a.txt", "D"},
+ {"a.txt", "E"},
+ },
+ },
+ {
+ name: "different_paths_same_name",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "A"},
+ {"a/txt", "A"},
+ {"b", "A"},
+ {"a/b/d", "A"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "A"},
+ {"a/b/d", "A"},
+ {"a/txt", "A"},
+ {"b/b.txt", "A"},
+ {"b", "A"},
+ },
+ },
+ {
+ name: "all_different",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "B"},
+ {"a/txt", "D"},
+ {"b", "C"},
+ {"a/b/d", "E"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ {"a/txt", "D"},
+ {"b/b.txt", "A"},
+ {"b", "C"},
+ },
+ },
+ {
+ name: "some_different",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "B"},
+ {"a/txt", "D"},
+ {"a/b/d", "E"},
+ {"b", "C"},
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ {"a/txt", "D"},
+ {"b/b.txt", "A"},
+ {"b", "C"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ actual := SortedUniqueNamedPaths(makePaths(tt.in))
+ expected := makePaths(tt.expectedOut)
+ t.Logf("actual: %v", actual)
+ t.Logf("expected: %v", expected)
+ AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
+ })
+ }
+}
diff --git a/android/mutator.go b/android/mutator.go
index 739e4ee..f06ecda 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,12 +15,9 @@
package android
import (
- "reflect"
-
"android/soong/bazel"
"github.com/google/blueprint"
- "github.com/google/blueprint/proptools"
)
// Phases:
@@ -235,9 +232,6 @@
// Rename all variants of a module. The new name is not visible to calls to ModuleName,
// AddDependency or OtherModuleName until after this mutator pass is complete.
Rename(name string)
-
- // BazelConversionMode returns whether this mutator is being run as part of Bazel Conversion.
- BazelConversionMode() bool
}
type TopDownMutator func(TopDownMutatorContext)
@@ -553,29 +547,16 @@
t.Module().base().commonProperties.DebugName = name
}
+func (t *topDownMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
+ return t.bp.CreateModule(factory, name, props...)
+}
+
func (t *topDownMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
- inherited := []interface{}{&t.Module().base().commonProperties}
- module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
-
- if t.Module().base().variableProperties != nil && module.base().variableProperties != nil {
- src := t.Module().base().variableProperties
- dst := []interface{}{
- module.base().variableProperties,
- // Put an empty copy of the src properties into dst so that properties in src that are not in dst
- // don't cause a "failed to find property to extend" error.
- proptools.CloneEmptyProperties(reflect.ValueOf(src)).Interface(),
- }
- err := proptools.AppendMatchingProperties(dst, src, nil)
- if err != nil {
- panic(err)
- }
- }
-
- return module
+ return createModule(t, factory, "_topDownMutatorModule", props...)
}
func (t *topDownMutatorContext) createModuleWithoutInheritance(factory ModuleFactory, props ...interface{}) Module {
- module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), props...).(Module)
+ module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), "", props...).(Module)
return module
}
@@ -642,28 +623,11 @@
func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
names ...string) []blueprint.Module {
- if b.bazelConversionMode {
- _, noSelfDeps := RemoveFromList(b.ModuleName(), names)
- if len(noSelfDeps) == 0 {
- return []blueprint.Module(nil)
- }
- // In Bazel conversion mode, mutators should not have created any variants. So, when adding a
- // dependency, the variations would not exist and the dependency could not be added, by
- // specifying no variations, we will allow adding the dependency to succeed.
- return b.bp.AddFarVariationDependencies(nil, tag, noSelfDeps...)
- }
-
return b.bp.AddVariationDependencies(variations, tag, names...)
}
func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
tag blueprint.DependencyTag, names ...string) []blueprint.Module {
- if b.bazelConversionMode {
- // In Bazel conversion mode, mutators should not have created any variants. So, when adding a
- // dependency, the variations would not exist and the dependency could not be added, by
- // specifying no variations, we will allow adding the dependency to succeed.
- return b.bp.AddFarVariationDependencies(nil, tag, names...)
- }
return b.bp.AddFarVariationDependencies(variations, tag, names...)
}
diff --git a/android/namespace_test.go b/android/namespace_test.go
index ea399da..87d1320 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -15,7 +15,6 @@
package android
import (
- "errors"
"path/filepath"
"reflect"
"testing"
@@ -24,577 +23,555 @@
)
func TestDependingOnModuleInSameNamespace(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- a := getModule(ctx, "a")
- b := getModule(ctx, "b")
- if !dependsOn(ctx, b, a) {
+ a := getModule(result, "a")
+ b := getModule(result, "b")
+ if !dependsOn(result, b, a) {
t.Errorf("module b does not depend on module a in the same namespace")
}
}
func TestDependingOnModuleInRootNamespace(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
".": `
- test_module {
- name: "b",
- deps: ["a"],
- }
- test_module {
- name: "a",
- }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
+ test_module {
+ name: "a",
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- a := getModule(ctx, "a")
- b := getModule(ctx, "b")
- if !dependsOn(ctx, b, a) {
+ a := getModule(result, "a")
+ b := getModule(result, "b")
+ if !dependsOn(result, b, a) {
t.Errorf("module b in root namespace does not depend on module a in the root namespace")
}
}
func TestImplicitlyImportRootNamespace(t *testing.T) {
- _ = setupTest(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
".": `
- test_module {
- name: "a",
- }
+ test_module {
+ name: "a",
+ }
`,
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- // setupTest will report any errors
+ // RunTest will report any errors
}
func TestDependingOnBlueprintModuleInRootNamespace(t *testing.T) {
- _ = setupTest(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
".": `
- blueprint_test_module {
- name: "a",
- }
+ blueprint_test_module {
+ name: "a",
+ }
`,
"dir1": `
- soong_namespace {
- }
- blueprint_test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ }
+ blueprint_test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- // setupTest will report any errors
+ // RunTest will report any errors
}
func TestDependingOnModuleInImportedNamespace(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir2": `
- soong_namespace {
- imports: ["dir1"],
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ imports: ["dir1"],
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- a := getModule(ctx, "a")
- b := getModule(ctx, "b")
- if !dependsOn(ctx, b, a) {
+ a := getModule(result, "a")
+ b := getModule(result, "b")
+ if !dependsOn(result, b, a) {
t.Errorf("module b does not depend on module a in the same namespace")
}
}
func TestDependingOnModuleInNonImportedNamespace(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir2": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir3": `
- soong_namespace {
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
-
- expectedErrors := []error{
- errors.New(
- `dir3/Android.bp:4:4: "b" depends on undefined module "a"
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir3/Android.bp:4:5: "b" depends on undefined module "a"
Module "b" is defined in namespace "dir3" which can read these 2 namespaces: ["dir3" "."]
-Module "a" can be found in these namespaces: ["dir1" "dir2"]`),
- }
-
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+Module "a" can be found in these namespaces: ["dir1" "dir2"]\E`)).
+ RunTest(t)
}
func TestDependingOnModuleByFullyQualifiedReference(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir2": `
- soong_namespace {
- }
- test_module {
- name: "b",
- deps: ["//dir1:a"],
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "b",
+ deps: ["//dir1:a"],
+ }
`,
- },
- )
- a := getModule(ctx, "a")
- b := getModule(ctx, "b")
- if !dependsOn(ctx, b, a) {
+ }),
+ ).RunTest(t)
+
+ a := getModule(result, "a")
+ b := getModule(result, "b")
+ if !dependsOn(result, b, a) {
t.Errorf("module b does not depend on module a")
}
}
func TestSameNameInTwoNamespaces(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- id: "1",
- }
- test_module {
- name: "b",
- deps: ["a"],
- id: "2",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ id: "1",
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ id: "2",
+ }
`,
"dir2": `
- soong_namespace {
- }
- test_module {
- name: "a",
- id:"3",
- }
- test_module {
- name: "b",
- deps: ["a"],
- id:"4",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ id:"3",
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ id:"4",
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- one := findModuleById(ctx, "1")
- two := findModuleById(ctx, "2")
- three := findModuleById(ctx, "3")
- four := findModuleById(ctx, "4")
- if !dependsOn(ctx, two, one) {
+ one := findModuleById(result, "1")
+ two := findModuleById(result, "2")
+ three := findModuleById(result, "3")
+ four := findModuleById(result, "4")
+ if !dependsOn(result, two, one) {
t.Fatalf("Module 2 does not depend on module 1 in its namespace")
}
- if dependsOn(ctx, two, three) {
+ if dependsOn(result, two, three) {
t.Fatalf("Module 2 depends on module 3 in another namespace")
}
- if !dependsOn(ctx, four, three) {
+ if !dependsOn(result, four, three) {
t.Fatalf("Module 4 does not depend on module 3 in its namespace")
}
- if dependsOn(ctx, four, one) {
+ if dependsOn(result, four, one) {
t.Fatalf("Module 4 depends on module 1 in another namespace")
}
}
func TestSearchOrder(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- id: "1",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ id: "1",
+ }
`,
"dir2": `
- soong_namespace {
- }
- test_module {
- name: "a",
- id:"2",
- }
- test_module {
- name: "b",
- id:"3",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ id:"2",
+ }
+ test_module {
+ name: "b",
+ id:"3",
+ }
`,
"dir3": `
- soong_namespace {
- }
- test_module {
- name: "a",
- id:"4",
- }
- test_module {
- name: "b",
- id:"5",
- }
- test_module {
- name: "c",
- id:"6",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ id:"4",
+ }
+ test_module {
+ name: "b",
+ id:"5",
+ }
+ test_module {
+ name: "c",
+ id:"6",
+ }
`,
".": `
- test_module {
- name: "a",
- id: "7",
- }
- test_module {
- name: "b",
- id: "8",
- }
- test_module {
- name: "c",
- id: "9",
- }
- test_module {
- name: "d",
- id: "10",
- }
+ test_module {
+ name: "a",
+ id: "7",
+ }
+ test_module {
+ name: "b",
+ id: "8",
+ }
+ test_module {
+ name: "c",
+ id: "9",
+ }
+ test_module {
+ name: "d",
+ id: "10",
+ }
`,
"dir4": `
- soong_namespace {
- imports: ["dir1", "dir2", "dir3"]
- }
- test_module {
- name: "test_me",
- id:"0",
- deps: ["a", "b", "c", "d"],
- }
+ soong_namespace {
+ imports: ["dir1", "dir2", "dir3"]
+ }
+ test_module {
+ name: "test_me",
+ id:"0",
+ deps: ["a", "b", "c", "d"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- testMe := findModuleById(ctx, "0")
- if !dependsOn(ctx, testMe, findModuleById(ctx, "1")) {
+ testMe := findModuleById(result, "0")
+ if !dependsOn(result, testMe, findModuleById(result, "1")) {
t.Errorf("test_me doesn't depend on id 1")
}
- if !dependsOn(ctx, testMe, findModuleById(ctx, "3")) {
+ if !dependsOn(result, testMe, findModuleById(result, "3")) {
t.Errorf("test_me doesn't depend on id 3")
}
- if !dependsOn(ctx, testMe, findModuleById(ctx, "6")) {
+ if !dependsOn(result, testMe, findModuleById(result, "6")) {
t.Errorf("test_me doesn't depend on id 6")
}
- if !dependsOn(ctx, testMe, findModuleById(ctx, "10")) {
+ if !dependsOn(result, testMe, findModuleById(result, "10")) {
t.Errorf("test_me doesn't depend on id 10")
}
- if numDeps(ctx, testMe) != 4 {
- t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(ctx, testMe))
+ if numDeps(result, testMe) != 4 {
+ t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(result, testMe))
}
}
func TestTwoNamespacesCanImportEachOther(t *testing.T) {
- _ = setupTest(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- imports: ["dir2"]
- }
- test_module {
- name: "a",
- }
- test_module {
- name: "c",
- deps: ["b"],
- }
+ soong_namespace {
+ imports: ["dir2"]
+ }
+ test_module {
+ name: "a",
+ }
+ test_module {
+ name: "c",
+ deps: ["b"],
+ }
`,
"dir2": `
- soong_namespace {
- imports: ["dir1"],
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ imports: ["dir1"],
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- // setupTest will report any errors
+ // RunTest will report any errors
}
func TestImportingNonexistentNamespace(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- imports: ["a_nonexistent_namespace"]
- }
- test_module {
- name: "a",
- deps: ["a_nonexistent_module"]
- }
+ soong_namespace {
+ imports: ["a_nonexistent_namespace"]
+ }
+ test_module {
+ name: "a",
+ deps: ["a_nonexistent_module"]
+ }
`,
- },
- )
-
- // should complain about the missing namespace and not complain about the unresolvable dependency
- expectedErrors := []error{
- errors.New(`dir1/Android.bp:2:4: module "soong_namespace": namespace a_nonexistent_namespace does not exist`),
- }
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+ }),
+ ).
+ // should complain about the missing namespace and not complain about the unresolvable dependency
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:2:5: module "soong_namespace": namespace a_nonexistent_namespace does not exist\E`)).
+ RunTest(t)
}
func TestNamespacesDontInheritParentNamespaces(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir1/subdir1": `
- soong_namespace {
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
-
- expectedErrors := []error{
- errors.New(`dir1/subdir1/Android.bp:4:4: "b" depends on undefined module "a"
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/subdir1/Android.bp:4:5: "b" depends on undefined module "a"
Module "b" is defined in namespace "dir1/subdir1" which can read these 2 namespaces: ["dir1/subdir1" "."]
-Module "a" can be found in these namespaces: ["dir1"]`),
- }
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+Module "a" can be found in these namespaces: ["dir1"]\E`)).
+ RunTest(t)
}
func TestModulesDoReceiveParentNamespace(t *testing.T) {
- _ = setupTest(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir1/subdir": `
- test_module {
- name: "b",
- deps: ["a"],
- }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
- },
- )
+ }),
+ ).RunTest(t)
- // setupTest will report any errors
+ // RunTest will report any errors
}
func TestNamespaceImportsNotTransitive(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ }
`,
"dir2": `
- soong_namespace {
- imports: ["dir1"],
- }
- test_module {
- name: "b",
- deps: ["a"],
- }
+ soong_namespace {
+ imports: ["dir1"],
+ }
+ test_module {
+ name: "b",
+ deps: ["a"],
+ }
`,
"dir3": `
- soong_namespace {
- imports: ["dir2"],
- }
- test_module {
- name: "c",
- deps: ["a"],
- }
+ soong_namespace {
+ imports: ["dir2"],
+ }
+ test_module {
+ name: "c",
+ deps: ["a"],
+ }
`,
- },
- )
-
- expectedErrors := []error{
- errors.New(`dir3/Android.bp:5:4: "c" depends on undefined module "a"
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir3/Android.bp:5:5: "c" depends on undefined module "a"
Module "c" is defined in namespace "dir3" which can read these 3 namespaces: ["dir3" "dir2" "."]
-Module "a" can be found in these namespaces: ["dir1"]`),
- }
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+Module "a" can be found in these namespaces: ["dir1"]\E`)).
+ RunTest(t)
}
func TestTwoNamepacesInSameDir(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- soong_namespace {
- }
+ soong_namespace {
+ }
+ soong_namespace {
+ }
`,
- },
- )
-
- expectedErrors := []error{
- errors.New(`dir1/Android.bp:4:4: namespace dir1 already exists`),
- }
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:4:5: namespace dir1 already exists\E`)).
+ RunTest(t)
}
func TestNamespaceNotAtTopOfFile(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- test_module {
- name: "a"
- }
- soong_namespace {
- }
+ test_module {
+ name: "a"
+ }
+ soong_namespace {
+ }
`,
- },
- )
-
- expectedErrors := []error{
- errors.New(`dir1/Android.bp:5:4: a namespace must be the first module in the file`),
- }
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:5:5: a namespace must be the first module in the file\E`)).
+ RunTest(t)
}
func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) {
- _, errs := setupTestExpectErrs(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a"
- }
- test_module {
- name: "a"
- }
+ soong_namespace {
+ }
+ test_module {
+ name: "a"
+ }
+ test_module {
+ name: "a"
+ }
`,
- },
- )
-
- expectedErrors := []error{
- errors.New(`dir1/Android.bp:7:4: module "a" already defined
- dir1/Android.bp:4:4 <-- previous definition here`),
- }
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:7:5: module "a" already defined
+ dir1/Android.bp:4:5 <-- previous definition here\E`)).
+ RunTest(t)
}
func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) {
- _, errs := setupTestFromFiles(t,
- map[string][]byte{
- "Android.bp": []byte(`
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ FixtureWithRootAndroidBp(`
build = ["include.bp"]
- `),
- "include.bp": []byte(`
+ `),
+ FixtureAddTextFile("include.bp", `
soong_namespace {
}
- `),
- },
- )
-
- expectedErrors := []error{
- errors.New(`include.bp:2:5: A namespace may only be declared in a file named Android.bp`),
- }
-
- if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
- t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
- }
+ `),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(
+ `\Qinclude.bp:2:5: A namespace may only be declared in a file named Android.bp\E`,
+ )).
+ RunTest(t)
}
// so that the generated .ninja file will have consistent names
func TestConsistentNamespaceNames(t *testing.T) {
- ctx := setupTest(t,
- map[string]string{
+ result := GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": "soong_namespace{}",
"dir2": "soong_namespace{}",
"dir3": "soong_namespace{}",
- })
+ }),
+ ).RunTest(t)
- ns1, _ := ctx.NameResolver.namespaceAt("dir1")
- ns2, _ := ctx.NameResolver.namespaceAt("dir2")
- ns3, _ := ctx.NameResolver.namespaceAt("dir3")
+ ns1, _ := result.NameResolver.namespaceAt("dir1")
+ ns2, _ := result.NameResolver.namespaceAt("dir2")
+ ns3, _ := result.NameResolver.namespaceAt("dir3")
actualIds := []string{ns1.id, ns2.id, ns3.id}
expectedIds := []string{"1", "2", "3"}
if !reflect.DeepEqual(actualIds, expectedIds) {
@@ -604,103 +581,88 @@
// so that the generated .ninja file will have consistent names
func TestRename(t *testing.T) {
- _ = setupTest(t,
- map[string]string{
+ GroupFixturePreparers(
+ prepareForTestWithNamespace,
+ dirBpToPreparer(map[string]string{
"dir1": `
- soong_namespace {
- }
- test_module {
- name: "a",
- deps: ["c"],
- }
- test_module {
- name: "b",
- rename: "c",
- }
- `})
- // setupTest will report any errors
+ soong_namespace {
+ }
+ test_module {
+ name: "a",
+ deps: ["c"],
+ }
+ test_module {
+ name: "b",
+ rename: "c",
+ }
+ `,
+ }),
+ ).RunTest(t)
+
+ // RunTest will report any errors
}
// some utils to support the tests
-func mockFiles(bps map[string]string) (files map[string][]byte) {
- files = make(map[string][]byte, len(bps))
+var prepareForTestWithNamespace = GroupFixturePreparers(
+ FixtureRegisterWithContext(registerNamespaceBuildComponents),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PreArchMutators(RegisterNamespaceMutator)
+ }),
+ FixtureModifyContext(func(ctx *TestContext) {
+ ctx.RegisterModuleType("test_module", newTestModule)
+ ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("rename", renameMutator)
+ })
+ }),
+)
+
+// dirBpToPreparer takes a map from directory to the contents of the Android.bp file and produces a
+// FixturePreparer.
+func dirBpToPreparer(bps map[string]string) FixturePreparer {
+ files := make(MockFS, len(bps))
files["Android.bp"] = []byte("")
for dir, text := range bps {
files[filepath.Join(dir, "Android.bp")] = []byte(text)
}
- return files
+ return files.AddToFixture()
}
-func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []error) {
- result := GroupFixturePreparers(
- FixtureModifyContext(func(ctx *TestContext) {
- ctx.RegisterModuleType("test_module", newTestModule)
- ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("rename", renameMutator)
- })
- }),
- PrepareForTestWithNamespace,
- bps.AddToFixture(),
- ).
- // Ignore errors for now so tests can check them later.
- ExtendWithErrorHandler(FixtureIgnoreErrors).
- RunTest(t)
-
- return result.TestContext, result.Errs
-}
-
-func setupTestExpectErrs(t *testing.T, bps map[string]string) (ctx *TestContext, errs []error) {
- files := make(map[string][]byte, len(bps))
- files["Android.bp"] = []byte("")
- for dir, text := range bps {
- files[filepath.Join(dir, "Android.bp")] = []byte(text)
- }
- return setupTestFromFiles(t, files)
-}
-
-func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
- t.Helper()
- ctx, errs := setupTestExpectErrs(t, bps)
- FailIfErrored(t, errs)
- return ctx
-}
-
-func dependsOn(ctx *TestContext, module TestingModule, possibleDependency TestingModule) bool {
+func dependsOn(result *TestResult, module TestingModule, possibleDependency TestingModule) bool {
depends := false
visit := func(dependency blueprint.Module) {
if dependency == possibleDependency.module {
depends = true
}
}
- ctx.VisitDirectDeps(module.module, visit)
+ result.VisitDirectDeps(module.module, visit)
return depends
}
-func numDeps(ctx *TestContext, module TestingModule) int {
+func numDeps(result *TestResult, module TestingModule) int {
count := 0
visit := func(dependency blueprint.Module) {
count++
}
- ctx.VisitDirectDeps(module.module, visit)
+ result.VisitDirectDeps(module.module, visit)
return count
}
-func getModule(ctx *TestContext, moduleName string) TestingModule {
- return ctx.ModuleForTests(moduleName, "")
+func getModule(result *TestResult, moduleName string) TestingModule {
+ return result.ModuleForTests(moduleName, "")
}
-func findModuleById(ctx *TestContext, id string) (module TestingModule) {
+func findModuleById(result *TestResult, id string) (module TestingModule) {
visit := func(candidate blueprint.Module) {
testModule, ok := candidate.(*testModule)
if ok {
if testModule.properties.Id == id {
- module = newTestingModule(ctx.config, testModule)
+ module = newTestingModule(result.config, testModule)
}
}
}
- ctx.VisitAllModules(visit)
+ result.VisitAllModules(visit)
return module
}
@@ -747,7 +709,7 @@
}
}
-func (b *blueprintTestModule) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+func (b *blueprintTestModule) DynamicDependencies(_ blueprint.DynamicDependerModuleContext) []string {
return b.properties.Deps
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 6f9ae58..aa47bca 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -57,6 +57,7 @@
AddNeverAllowRules(createUncompressDexRules()...)
AddNeverAllowRules(createMakefileGoalRules()...)
AddNeverAllowRules(createInitFirstStageRules()...)
+ AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
}
// Add a NeverAllow rule to the set of rules to apply.
@@ -228,6 +229,15 @@
}
}
+func createProhibitFrameworkAccessRules() []Rule {
+ return []Rule{
+ NeverAllow().
+ With("libs", "framework").
+ WithoutMatcher("sdk_version", Regexp("(core_.*|^$)")).
+ Because("framework can't be used when building against SDK"),
+ }
+}
+
func neverallowMutator(ctx BottomUpMutatorContext) {
m, ok := ctx.Module().(Module)
if !ok {
@@ -249,7 +259,7 @@
continue
}
- if !n.appliesToProperties(ctx, properties) {
+ if !n.appliesToProperties(properties) {
continue
}
@@ -261,20 +271,12 @@
continue
}
- if !n.appliesToBootclasspathJar(ctx) {
- continue
- }
-
ctx.ModuleErrorf("violates " + n.String())
}
}
-type ValueMatcherContext interface {
- Config() Config
-}
-
type ValueMatcher interface {
- Test(ValueMatcherContext, string) bool
+ Test(string) bool
String() string
}
@@ -282,7 +284,7 @@
expected string
}
-func (m *equalMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *equalMatcher) Test(value string) bool {
return m.expected == value
}
@@ -293,7 +295,7 @@
type anyMatcher struct {
}
-func (m *anyMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *anyMatcher) Test(value string) bool {
return true
}
@@ -307,7 +309,7 @@
prefix string
}
-func (m *startsWithMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *startsWithMatcher) Test(value string) bool {
return strings.HasPrefix(value, m.prefix)
}
@@ -319,7 +321,7 @@
re *regexp.Regexp
}
-func (m *regexMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *regexMatcher) Test(value string) bool {
return m.re.MatchString(value)
}
@@ -331,7 +333,7 @@
allowed []string
}
-func (m *notInListMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *notInListMatcher) Test(value string) bool {
return !InList(value, m.allowed)
}
@@ -341,7 +343,7 @@
type isSetMatcher struct{}
-func (m *isSetMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *isSetMatcher) Test(value string) bool {
return value != ""
}
@@ -351,19 +353,6 @@
var isSetMatcherInstance = &isSetMatcher{}
-type sdkVersionMatcher struct {
- condition func(ctx ValueMatcherContext, spec SdkSpec) bool
- description string
-}
-
-func (m *sdkVersionMatcher) Test(ctx ValueMatcherContext, value string) bool {
- return m.condition(ctx, SdkSpecFromWithConfig(ctx.Config(), value))
-}
-
-func (m *sdkVersionMatcher) String() string {
- return ".sdk-version(" + m.description + ")"
-}
-
type ruleProperty struct {
fields []string // e.x.: Vndk.Enabled
matcher ValueMatcher
@@ -397,8 +386,6 @@
NotModuleType(types ...string) Rule
- BootclasspathJar() Rule
-
With(properties, value string) Rule
WithMatcher(properties string, matcher ValueMatcher) Rule
@@ -514,12 +501,6 @@
return r
}
-// BootclasspathJar whether this rule only applies to Jars in the Bootclasspath
-func (r *rule) BootclasspathJar() Rule {
- r.onlyBootclasspathJar = true
- return r
-}
-
func (r *rule) String() string {
s := []string{"neverallow requirements. Not allowed:"}
if len(r.paths) > 0 {
@@ -537,9 +518,6 @@
if len(r.osClasses) > 0 {
s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
}
- if r.onlyBootclasspathJar {
- s = append(s, "in bootclasspath jar")
- }
if len(r.unlessPaths) > 0 {
s = append(s, fmt.Sprintf("EXCEPT in dirs: %q", r.unlessPaths))
}
@@ -580,14 +558,6 @@
return matches
}
-func (r *rule) appliesToBootclasspathJar(ctx BottomUpMutatorContext) bool {
- if !r.onlyBootclasspathJar {
- return true
- }
-
- return InList(ctx.ModuleName(), ctx.Config().BootJars())
-}
-
func (r *rule) appliesToOsClass(osClass OsClass) bool {
if len(r.osClasses) == 0 {
return true
@@ -606,10 +576,9 @@
return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
}
-func (r *rule) appliesToProperties(ctx ValueMatcherContext,
- properties []interface{}) bool {
- includeProps := hasAllProperties(ctx, properties, r.props)
- excludeProps := hasAnyProperty(ctx, properties, r.unlessProps)
+func (r *rule) appliesToProperties(properties []interface{}) bool {
+ includeProps := hasAllProperties(properties, r.props)
+ excludeProps := hasAnyProperty(properties, r.unlessProps)
return includeProps && !excludeProps
}
@@ -629,16 +598,6 @@
return ¬InListMatcher{allowed}
}
-func LessThanSdkVersion(sdk string) ValueMatcher {
- return &sdkVersionMatcher{
- condition: func(ctx ValueMatcherContext, spec SdkSpec) bool {
- return spec.ApiLevel.LessThan(
- SdkSpecFromWithConfig(ctx.Config(), sdk).ApiLevel)
- },
- description: "lessThan=" + sdk,
- }
-}
-
// assorted utils
func cleanPaths(paths []string) []string {
@@ -657,28 +616,25 @@
return names
}
-func hasAnyProperty(ctx ValueMatcherContext, properties []interface{},
- props []ruleProperty) bool {
+func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
for _, v := range props {
- if hasProperty(ctx, properties, v) {
+ if hasProperty(properties, v) {
return true
}
}
return false
}
-func hasAllProperties(ctx ValueMatcherContext, properties []interface{},
- props []ruleProperty) bool {
+func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
for _, v := range props {
- if !hasProperty(ctx, properties, v) {
+ if !hasProperty(properties, v) {
return false
}
}
return true
}
-func hasProperty(ctx ValueMatcherContext, properties []interface{},
- prop ruleProperty) bool {
+func hasProperty(properties []interface{}, prop ruleProperty) bool {
for _, propertyStruct := range properties {
propertiesValue := reflect.ValueOf(propertyStruct).Elem()
for _, v := range prop.fields {
@@ -692,7 +648,7 @@
}
check := func(value string) bool {
- return prop.matcher.Test(ctx, value)
+ return prop.matcher.Test(value)
}
if matchValue(propertiesValue, check) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 59016d4..86f1a37 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -327,46 +327,19 @@
"Only boot images may be imported as a makefile goal.",
},
},
+ // Tests for the rule prohibiting the use of framework
{
- name: "min_sdk too low",
+ name: "prohibit framework",
fs: map[string][]byte{
"Android.bp": []byte(`
java_library {
- name: "min_sdk_too_low",
- min_sdk_version: "30",
+ name: "foo",
+ libs: ["framework"],
+ sdk_version: "current",
}`),
},
- rules: []Rule{
- NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
- },
expectedErrors: []string{
- "module \"min_sdk_too_low\": violates neverallow",
- },
- },
- {
- name: "min_sdk high enough",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "min_sdk_high_enough",
- min_sdk_version: "31",
- }`),
- },
- rules: []Rule{
- NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
- },
- },
- {
- name: "current min_sdk high enough",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "current_min_sdk_high_enough",
- min_sdk_version: "current",
- }`),
- },
- rules: []Rule{
- NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
+ "framework can't be used when building against SDK",
},
},
}
@@ -452,10 +425,9 @@
}
type mockJavaLibraryProperties struct {
- Libs []string
- Min_sdk_version *string
- Sdk_version *string
- Uncompress_dex *bool
+ Libs []string
+ Sdk_version *string
+ Uncompress_dex *bool
}
type mockJavaLibraryModule struct {
diff --git a/android/notices.go b/android/notices.go
index 194a734..562a156 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -15,102 +15,86 @@
package android
import (
+ "fmt"
"path/filepath"
"strings"
-
- "github.com/google/blueprint"
)
-func init() {
- pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
- pctx.SourcePathVariable("generate_notice", "build/soong/scripts/generate-notice-files.py")
-
- pctx.HostBinToolVariable("minigzip", "minigzip")
-}
-
-type NoticeOutputs struct {
- Merged OptionalPath
- TxtOutput OptionalPath
- HtmlOutput OptionalPath
- HtmlGzOutput OptionalPath
-}
-
-var (
- mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
- Command: `${merge_notices} --output $out $in`,
- CommandDeps: []string{"${merge_notices}"},
- Description: "merge notice files into $out",
- })
-
- generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
- Command: `rm -rf $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
- `mkdir -p $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
- `${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` +
- `${minigzip} -c $htmlOut > $out`,
- CommandDeps: []string{"${generate_notice}", "${minigzip}"},
- Description: "produce notice file $out",
- }, "txtOut", "htmlOut", "title", "inputDir")
-)
-
-func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
- ctx.Build(pctx, BuildParams{
- Rule: mergeNoticesRule,
- Description: "merge notices",
- Inputs: noticePaths,
- Output: mergedNotice,
- })
-}
-
-func BuildNoticeOutput(ctx ModuleContext, installPath InstallPath, installFilename string,
- noticePaths []Path) NoticeOutputs {
- // Merge all NOTICE files into one.
- // TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
- //
- // generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
- // about input NOTICE file paths.
- // 1. Their relative paths to the src root become their NOTICE index titles. We want to use
- // on-device paths as titles, and so output the merged NOTICE file the corresponding location.
- // 2. They must end with .txt extension. Otherwise, they're ignored.
- noticeRelPath := InstallPathToOnDevicePath(ctx, installPath.Join(ctx, installFilename+".txt"))
- mergedNotice := PathForModuleOut(ctx, filepath.Join("NOTICE_FILES/src", noticeRelPath))
- MergeNotices(ctx, mergedNotice, noticePaths)
-
- // Transform the merged NOTICE file into a gzipped HTML file.
- txtOuptut := PathForModuleOut(ctx, "NOTICE_txt", "NOTICE.txt")
- htmlOutput := PathForModuleOut(ctx, "NOTICE_html", "NOTICE.html")
- htmlGzOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
- title := "Notices for " + ctx.ModuleName()
- ctx.Build(pctx, BuildParams{
- Rule: generateNoticeRule,
- Description: "generate notice output",
- Input: mergedNotice,
- Output: htmlGzOutput,
- ImplicitOutputs: WritablePaths{txtOuptut, htmlOutput},
- Args: map[string]string{
- "txtOut": txtOuptut.String(),
- "htmlOut": htmlOutput.String(),
- "title": title,
- "inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
- },
- })
-
- return NoticeOutputs{
- Merged: OptionalPathForPath(mergedNotice),
- TxtOutput: OptionalPathForPath(txtOuptut),
- HtmlOutput: OptionalPathForPath(htmlOutput),
- HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
+func modulesOutputDirs(ctx BuilderContext, modules ...Module) []string {
+ dirs := make([]string, 0, len(modules))
+ for _, module := range modules {
+ paths, err := outputFilesForModule(ctx, module, "")
+ if err != nil {
+ continue
+ }
+ for _, path := range paths {
+ if path != nil {
+ dirs = append(dirs, filepath.Dir(path.String()))
+ }
+ }
}
+ return SortedUniqueStrings(dirs)
}
-// BuildNoticeTextOutputFromLicenseMetadata writes out a notice text file based on the module's
-// generated license metadata file.
-func BuildNoticeTextOutputFromLicenseMetadata(ctx ModuleContext, outputFile WritablePath) {
+func modulesLicenseMetadata(ctx BuilderContext, modules ...Module) Paths {
+ result := make(Paths, 0, len(modules))
+ for _, module := range modules {
+ if mf := module.base().licenseMetadataFile; mf != nil {
+ result = append(result, mf)
+ }
+ }
+ return result
+}
+
+// buildNoticeOutputFromLicenseMetadata writes out a notice file.
+func buildNoticeOutputFromLicenseMetadata(ctx BuilderContext, tool, ruleName string, outputFile WritablePath, libraryName, stripPrefix string, modules ...Module) {
depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
rule := NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("textnotice").
+ if len(modules) == 0 {
+ if mctx, ok := ctx.(ModuleContext); ok {
+ modules = []Module{mctx.Module()}
+ } else {
+ panic(fmt.Errorf("%s %q needs a module to generate the notice for", ruleName, libraryName))
+ }
+ }
+ if libraryName == "" {
+ libraryName = modules[0].Name()
+ }
+ cmd := rule.Command().
+ BuiltTool(tool).
FlagWithOutput("-o ", outputFile).
- FlagWithDepFile("-d ", depsFile).
- Input(ctx.Module().base().licenseMetadataFile)
- rule.Build("container_notice", "container notice file")
+ FlagWithDepFile("-d ", depsFile)
+ if stripPrefix != "" {
+ cmd = cmd.FlagWithArg("--strip_prefix ", stripPrefix)
+ }
+ outputs := modulesOutputDirs(ctx, modules...)
+ if len(outputs) > 0 {
+ cmd = cmd.FlagForEachArg("--strip_prefix ", outputs)
+ }
+ if libraryName != "" {
+ cmd = cmd.FlagWithArg("--product ", libraryName)
+ }
+ cmd = cmd.Inputs(modulesLicenseMetadata(ctx, modules...))
+ rule.Build(ruleName, "container notice file")
+}
+
+// BuildNoticeTextOutputFromLicenseMetadata writes out a notice text file based
+// on the license metadata files for the input `modules` defaulting to the
+// current context module if none given.
+func BuildNoticeTextOutputFromLicenseMetadata(ctx BuilderContext, outputFile WritablePath, ruleName, libraryName, stripPrefix string, modules ...Module) {
+ buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...)
+}
+
+// BuildNoticeHtmlOutputFromLicenseMetadata writes out a notice text file based
+// on the license metadata files for the input `modules` defaulting to the
+// current context module if none given.
+func BuildNoticeHtmlOutputFromLicenseMetadata(ctx BuilderContext, outputFile WritablePath, ruleName, libraryName, stripPrefix string, modules ...Module) {
+ buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...)
+}
+
+// BuildNoticeXmlOutputFromLicenseMetadata writes out a notice text file based
+// on the license metadata files for the input `modules` defaulting to the
+// current context module if none given.
+func BuildNoticeXmlOutputFromLicenseMetadata(ctx BuilderContext, outputFile WritablePath, ruleName, libraryName, stripPrefix string, modules ...Module) {
+ buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...)
}
diff --git a/android/packaging.go b/android/packaging.go
index e3a0b54..ecd84a2 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -40,6 +40,8 @@
executable bool
effectiveLicenseFiles *Paths
+
+ partition string
}
// Get file name of installed package
@@ -67,6 +69,10 @@
return *p.effectiveLicenseFiles
}
+func (p *PackagingSpec) Partition() string {
+ return p.partition
+}
+
type PackageModule interface {
Module
packagingBase() *PackagingBase
@@ -76,11 +82,14 @@
// be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
+ // GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
+ GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
+
// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
// followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
// etc.) from the extracted files
- CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) []string
+ CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string
}
// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
@@ -211,7 +220,7 @@
}
}
-// Returns transitive PackagingSpecs from deps
+// See PackageModule.GatherPackagingSpecs
func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
m := make(map[string]PackagingSpec)
ctx.VisitDirectDeps(func(child Module) {
@@ -229,10 +238,10 @@
// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
// entries into the specified directory.
-func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, m map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
+func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
seenDir := make(map[string]bool)
- for _, k := range SortedStringKeys(m) {
- ps := m[k]
+ for _, k := range SortedStringKeys(specs) {
+ ps := specs[k]
destPath := dir.Join(ctx, ps.relPathInPackage).String()
destDir := filepath.Dir(destPath)
entries = append(entries, ps.relPathInPackage)
@@ -254,14 +263,13 @@
}
// See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
- m := p.GatherPackagingSpecs(ctx)
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) {
builder := NewRuleBuilder(pctx, ctx)
dir := PathForModuleOut(ctx, ".zip")
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
- entries = p.CopySpecsToDir(ctx, builder, m, dir)
+ entries = p.CopySpecsToDir(ctx, builder, specs, dir)
builder.Command().
BuiltTool("soong_zip").
diff --git a/android/packaging_test.go b/android/packaging_test.go
index ff7446c..91ac1f3 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -95,7 +95,7 @@
func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
zipFile := PathForModuleOut(ctx, "myzip.zip")
- m.entries = m.CopyDepsToZip(ctx, zipFile)
+ m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile)
}
func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) {
diff --git a/android/paths.go b/android/paths.go
index 4c69de7..e0e5ae5 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -405,6 +405,13 @@
return PathsForModuleSrcExcludes(ctx, paths, nil)
}
+type SourceInput struct {
+ Context ModuleMissingDepsPathContext
+ Paths []string
+ ExcludePaths []string
+ IncludeDirs bool
+}
+
// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
// those listed in excludes. Elements of paths and excludes are resolved as:
// * filepath, relative to local module directory, resolves as a filepath relative to the local
@@ -423,12 +430,21 @@
// missing dependencies
// * otherwise, a ModuleError is thrown.
func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
- ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
- if ctx.Config().AllowMissingDependencies() {
- ctx.AddMissingDependencies(missingDeps)
+ return PathsRelativeToModuleSourceDir(SourceInput{
+ Context: ctx,
+ Paths: paths,
+ ExcludePaths: excludes,
+ IncludeDirs: true,
+ })
+}
+
+func PathsRelativeToModuleSourceDir(input SourceInput) Paths {
+ ret, missingDeps := PathsAndMissingDepsRelativeToModuleSourceDir(input)
+ if input.Context.Config().AllowMissingDependencies() {
+ input.Context.AddMissingDependencies(missingDeps)
} else {
for _, m := range missingDeps {
- ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
+ input.Context.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
}
}
return ret
@@ -543,23 +559,31 @@
// Properties passed as the paths argument must have been annotated with struct tag
// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
// path_deps mutator.
-func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) {
- prefix := pathForModuleSrc(ctx).String()
+func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) {
+ return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{
+ Context: ctx,
+ Paths: paths,
+ ExcludePaths: excludes,
+ IncludeDirs: true,
+ })
+}
+
+func PathsAndMissingDepsRelativeToModuleSourceDir(input SourceInput) (Paths, []string) {
+ prefix := pathForModuleSrc(input.Context).String()
var expandedExcludes []string
- if excludes != nil {
- expandedExcludes = make([]string, 0, len(excludes))
+ if input.ExcludePaths != nil {
+ expandedExcludes = make([]string, 0, len(input.ExcludePaths))
}
var missingExcludeDeps []string
-
- for _, e := range excludes {
+ for _, e := range input.ExcludePaths {
if m, t := SrcIsModuleWithTag(e); m != "" {
- modulePaths, err := getPathsFromModuleDep(ctx, e, m, t)
+ modulePaths, err := getPathsFromModuleDep(input.Context, e, m, t)
if m, ok := err.(missingDependencyError); ok {
missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...)
} else if err != nil {
- reportPathError(ctx, err)
+ reportPathError(input.Context, err)
} else {
expandedExcludes = append(expandedExcludes, modulePaths.Strings()...)
}
@@ -568,19 +592,24 @@
}
}
- if paths == nil {
+ if input.Paths == nil {
return nil, missingExcludeDeps
}
var missingDeps []string
- expandedSrcFiles := make(Paths, 0, len(paths))
- for _, s := range paths {
- srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes)
+ expandedSrcFiles := make(Paths, 0, len(input.Paths))
+ for _, s := range input.Paths {
+ srcFiles, err := expandOneSrcPath(sourcePathInput{
+ context: input.Context,
+ path: s,
+ expandedExcludes: expandedExcludes,
+ includeDirs: input.IncludeDirs,
+ })
if depErr, ok := err.(missingDependencyError); ok {
missingDeps = append(missingDeps, depErr.missingDeps...)
} else if err != nil {
- reportPathError(ctx, err)
+ reportPathError(input.Context, err)
}
expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
}
@@ -596,44 +625,59 @@
return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
}
+type sourcePathInput struct {
+ context ModuleWithDepsPathContext
+ path string
+ expandedExcludes []string
+ includeDirs bool
+}
+
// Expands one path string to Paths rooted from the module's local source
// directory, excluding those listed in the expandedExcludes.
// Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax.
-func expandOneSrcPath(ctx ModuleWithDepsPathContext, sPath string, expandedExcludes []string) (Paths, error) {
+func expandOneSrcPath(input sourcePathInput) (Paths, error) {
excludePaths := func(paths Paths) Paths {
- if len(expandedExcludes) == 0 {
+ if len(input.expandedExcludes) == 0 {
return paths
}
remainder := make(Paths, 0, len(paths))
for _, p := range paths {
- if !InList(p.String(), expandedExcludes) {
+ if !InList(p.String(), input.expandedExcludes) {
remainder = append(remainder, p)
}
}
return remainder
}
- if m, t := SrcIsModuleWithTag(sPath); m != "" {
- modulePaths, err := getPathsFromModuleDep(ctx, sPath, m, t)
+ if m, t := SrcIsModuleWithTag(input.path); m != "" {
+ modulePaths, err := getPathsFromModuleDep(input.context, input.path, m, t)
if err != nil {
return nil, err
} else {
return excludePaths(modulePaths), nil
}
- } else if pathtools.IsGlob(sPath) {
- paths := GlobFiles(ctx, pathForModuleSrc(ctx, sPath).String(), expandedExcludes)
- return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
} else {
- p := pathForModuleSrc(ctx, sPath)
- if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
- ReportPathErrorf(ctx, "%s: %s", p, err.Error())
- } else if !exists && !ctx.Config().TestAllowNonExistentPaths {
- ReportPathErrorf(ctx, "module source path %q does not exist", p)
- }
+ p := pathForModuleSrc(input.context, input.path)
+ if pathtools.IsGlob(input.path) {
+ paths := GlobFiles(input.context, p.String(), input.expandedExcludes)
+ return PathsWithModuleSrcSubDir(input.context, paths, ""), nil
+ } else {
+ if exists, _, err := input.context.Config().fs.Exists(p.String()); err != nil {
+ ReportPathErrorf(input.context, "%s: %s", p, err.Error())
+ } else if !exists && !input.context.Config().TestAllowNonExistentPaths {
+ ReportPathErrorf(input.context, "module source path %q does not exist", p)
+ } else if !input.includeDirs {
+ if isDir, err := input.context.Config().fs.IsDir(p.String()); exists && err != nil {
+ ReportPathErrorf(input.context, "%s: %s", p, err.Error())
+ } else if isDir {
+ ReportPathErrorf(input.context, "module source path %q is a directory", p)
+ }
+ }
- if InList(p.String(), expandedExcludes) {
- return nil, nil
+ if InList(p.String(), input.expandedExcludes) {
+ return nil, nil
+ }
+ return Paths{p}, nil
}
- return Paths{p}, nil
}
}
@@ -1013,7 +1057,8 @@
}
// absolute path already checked by validateSafePath
- if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
+ // special-case api surface gen files for now
+ if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") {
return ret, fmt.Errorf("source path %q is in output", ret.String())
}
@@ -1029,7 +1074,8 @@
}
// absolute path already checked by validatePath
- if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
+ // special-case for now
+ if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") {
return ret, fmt.Errorf("source path %q is in output", ret.String())
}
@@ -1315,7 +1361,7 @@
// validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module
// reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath.
p := strings.Join(pathComponents, string(filepath.Separator))
- paths, err := expandOneSrcPath(ctx, p, nil)
+ paths, err := expandOneSrcPath(sourcePathInput{context: ctx, path: p, includeDirs: true})
if err != nil {
if depErr, ok := err.(missingDependencyError); ok {
if ctx.Config().AllowMissingDependencies() {
@@ -1430,14 +1476,11 @@
func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
- arches := ctx.DeviceConfig().Arches()
- if len(arches) == 0 {
- panic("device build with no primary arch")
- }
- currentArch := ctx.Arch()
- archNameAndVariant := currentArch.ArchType.String()
- if currentArch.ArchVariant != "" {
- archNameAndVariant += "_" + currentArch.ArchVariant
+ currentArchType := ctx.Arch().ArchType
+ primaryArchType := ctx.Config().DevicePrimaryArchType()
+ archName := currentArchType.String()
+ if currentArchType != primaryArchType {
+ archName += "_" + primaryArchType.String()
}
var dirName string
@@ -1459,7 +1502,7 @@
}
return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
- version, binderBitness, archNameAndVariant, "source-based",
+ version, binderBitness, archName, "source-based",
fileName+ext)
}
diff --git a/android/proto.go b/android/proto.go
index 64d4d05..c3759f8 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -16,6 +16,7 @@
import (
"android/soong/bazel"
+ "regexp"
"strings"
"github.com/google/blueprint"
@@ -26,6 +27,14 @@
canonicalPathFromRootDefault = true
)
+var (
+ // ignoring case, checks for proto or protos as an independent word in the name, whether at the
+ // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
+ filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
+
+ ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup}
+)
+
// TODO(ccross): protos are often used to communicate between multiple modules. If the only
// way to convert a proto to source is to reference it as a source file, and external modules cannot
// reference source files in other modules, then every module that owns a proto file will need to
@@ -165,12 +174,11 @@
// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
// information necessary for language-specific handling.
-func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
+func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
var info Bp2buildProtoInfo
if srcs.IsEmpty() {
return info, false
}
- m := module.base()
info.Name = m.Name() + "_proto"
attrs := protoAttrs{
@@ -187,7 +195,7 @@
if axis == bazel.NoConfigAxis {
info.Type = props.Proto.Type
- if proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
+ if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
// an empty string indicates to strips the package path
path := ""
attrs.Strip_import_prefix = &path
@@ -205,3 +213,13 @@
return info, true
}
+
+func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+ m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+ labelStr := label.Label
+ if !exists || !IsFilegroup(ctx, m) {
+ return labelStr, false
+ }
+ likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
+ return labelStr, likelyProtos
+}
diff --git a/android/register.go b/android/register.go
index 10e14e0..c505833 100644
--- a/android/register.go
+++ b/android/register.go
@@ -59,6 +59,7 @@
var moduleTypes []moduleType
var moduleTypesForDocs = map[string]reflect.Value{}
+var moduleTypeByFactory = map[reflect.Value]string{}
type singleton struct {
// True if this should be registered as a pre-singleton, false otherwise.
@@ -140,6 +141,7 @@
// RegisterModuleType was a lambda.
func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
moduleTypesForDocs[name] = factory
+ moduleTypeByFactory[factory] = name
}
func RegisterSingletonType(name string, factory SingletonFactory) {
@@ -228,6 +230,10 @@
return moduleTypesForDocs
}
+func ModuleTypeByFactory() map[reflect.Value]string {
+ return moduleTypeByFactory
+}
+
// Interface for registering build components.
//
// Provided to allow registration of build components to be shared between the runtime
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 1c6b1c0..11da36c 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -101,12 +101,7 @@
}
// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
-//
-// Restat is not compatible with Sbox()
func (r *RuleBuilder) Restat() *RuleBuilder {
- if r.sbox {
- panic("Restat() is not compatible with Sbox()")
- }
r.restat = true
return r
}
@@ -141,8 +136,6 @@
// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
// will ensure that all outputs have been written, and will discard any output files that were not
// specified.
-//
-// Sbox is not compatible with Restat()
func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
if r.sbox {
panic("Sbox() may not be called more than once")
@@ -150,9 +143,6 @@
if len(r.commands) > 0 {
panic("Sbox() may not be called after Command()")
}
- if r.restat {
- panic("Sbox() is not compatible with Restat()")
- }
r.sbox = true
r.outDir = outputDir
r.sboxManifestPath = manifestPath
@@ -470,7 +460,7 @@
func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- BuiltTool("dep_fixer").
+ builtToolWithoutDeps("dep_fixer").
Inputs(depFiles.Paths())
}
@@ -636,11 +626,14 @@
ctx: r.ctx,
},
}
- sboxCmd.Text("rm -rf").Output(r.outDir)
- sboxCmd.Text("&&")
- sboxCmd.BuiltTool("sbox").
- Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
- Flag("--manifest").Input(r.sboxManifestPath)
+ sboxCmd.builtToolWithoutDeps("sbox").
+ FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
+ FlagWithArg("--output-dir ", r.outDir.String()).
+ FlagWithInput("--manifest ", r.sboxManifestPath)
+
+ if r.restat {
+ sboxCmd.Flag("--write-if-changed")
+ }
// Replace the command string, and add the sbox tool and manifest textproto to the
// dependencies of the final sbox rule.
@@ -1040,6 +1033,19 @@
// It is equivalent to:
// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+ if c.rule.ctx.Config().UseHostMusl() {
+ // If the host is using musl, assume that the tool was built against musl libc and include
+ // libc_musl.so in the sandbox.
+ // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
+ // this could be a dependency + TransitivePackagingSpecs.
+ c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
+ }
+ return c.builtToolWithoutDeps(tool)
+}
+
+// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
+// internally by RuleBuilder for helper tools that are known to be compiled statically.
+func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 3766bb0..86647eb 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -678,32 +678,32 @@
})
t.Run("sbox", func(t *testing.T) {
outDir := "out/soong/.intermediates/foo_sbox"
- outFile := filepath.Join(outDir, "gen/foo_sbox")
- depFile := filepath.Join(outDir, "gen/foo_sbox.d")
+ sboxOutDir := filepath.Join(outDir, "gen")
+ outFile := filepath.Join(sboxOutDir, "foo_sbox")
+ depFile := filepath.Join(sboxOutDir, "foo_sbox.d")
rspFile := filepath.Join(outDir, "rsp")
rspFile2 := filepath.Join(outDir, "rsp2")
manifest := filepath.Join(outDir, "sbox.textproto")
sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
sandboxPath := shared.TempDirForOutDir("out/soong")
- cmd := `rm -rf ` + outDir + `/gen && ` +
- sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
+ cmd := sbox + ` --sandbox-path ` + sandboxPath + ` --output-dir ` + sboxOutDir + ` --manifest ` + manifest
module := result.ModuleForTests("foo_sbox", "")
check(t, module.Output("gen/foo_sbox"), module.Output(rspFile2),
cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
})
t.Run("sbox_inputs", func(t *testing.T) {
outDir := "out/soong/.intermediates/foo_sbox_inputs"
- outFile := filepath.Join(outDir, "gen/foo_sbox_inputs")
- depFile := filepath.Join(outDir, "gen/foo_sbox_inputs.d")
+ sboxOutDir := filepath.Join(outDir, "gen")
+ outFile := filepath.Join(sboxOutDir, "foo_sbox_inputs")
+ depFile := filepath.Join(sboxOutDir, "foo_sbox_inputs.d")
rspFile := filepath.Join(outDir, "rsp")
rspFile2 := filepath.Join(outDir, "rsp2")
manifest := filepath.Join(outDir, "sbox.textproto")
sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
sandboxPath := shared.TempDirForOutDir("out/soong")
- cmd := `rm -rf ` + outDir + `/gen && ` +
- sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
+ cmd := sbox + ` --sandbox-path ` + sandboxPath + ` --output-dir ` + sboxOutDir + ` --manifest ` + manifest
module := result.ModuleForTests("foo_sbox_inputs", "")
check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
diff --git a/android/sdk.go b/android/sdk.go
index 1d63d7a..2dc0bd7 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -23,24 +23,8 @@
"github.com/google/blueprint/proptools"
)
-// RequiredSdks provides access to the set of SDKs required by an APEX and its contents.
-//
-// Extracted from SdkAware to make it easier to define custom subsets of the
-// SdkAware interface and improve code navigation within the IDE.
-//
-// In addition to its use in SdkAware this interface must also be implemented by
-// APEX to specify the SDKs required by that module and its contents. e.g. APEX
-// is expected to implement RequiredSdks() by reading its own properties like
-// `uses_sdks`.
-type RequiredSdks interface {
- // RequiredSdks returns the set of SDKs required by an APEX and its contents.
- RequiredSdks() SdkRefs
-}
-
// sdkAwareWithoutModule is provided simply to improve code navigation with the IDE.
type sdkAwareWithoutModule interface {
- RequiredSdks
-
// SdkMemberComponentName will return the name to use for a component of this module based on the
// base name of this module.
//
@@ -81,7 +65,6 @@
ContainingSdk() SdkRef
MemberName() string
- BuildWithSdks(sdks SdkRefs)
}
// SdkAware is the interface that must be supported by any module to become a member of SDK or to be
@@ -150,9 +133,6 @@
// The SDK that this module is a member of. nil if it is not a member of any SDK
ContainingSdk *SdkRef `blueprint:"mutated"`
- // The list of SDK names and versions that are used to build this module
- RequiredSdks SdkRefs `blueprint:"mutated"`
-
// Name of the module that this sdk member is representing
Sdk_member_name *string
}
@@ -208,16 +188,6 @@
return proptools.String(s.properties.Sdk_member_name)
}
-// BuildWithSdks is used to mark that this module has to be built with the given SDK(s).
-func (s *SdkBase) BuildWithSdks(sdks SdkRefs) {
- s.properties.RequiredSdks = sdks
-}
-
-// RequiredSdks returns the SDK(s) that this module has to be built with
-func (s *SdkBase) RequiredSdks() SdkRefs {
- return s.properties.RequiredSdks
-}
-
// InitSdkAwareModule initializes the SdkBase struct. This must be called by all modules including
// SdkBase.
func InitSdkAwareModule(m SdkAware) {
@@ -700,6 +670,9 @@
// host OS variant explicitly and disable all other host OS'es.
IsHostOsDependent() bool
+ // SupportedLinkages returns the names of the linkage variants supported by this module.
+ SupportedLinkages() []string
+
// AddDependencies adds dependencies from the SDK module to all the module variants the member
// type contributes to the SDK. `names` is the list of module names given in the member type
// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
@@ -763,6 +736,9 @@
// SupportedTraits returns the set of traits supported by this member type.
SupportedTraits() SdkMemberTraitSet
+
+ // Overrides returns whether type overrides other SdkMemberType
+ Overrides(SdkMemberType) bool
}
var _ sdkRegisterable = (SdkMemberType)(nil)
@@ -786,6 +762,13 @@
type SdkMemberTypeBase struct {
PropertyName string
+ // Property names that this SdkMemberTypeBase can override, this is useful when a module type is a
+ // superset of another module type.
+ OverridesPropertyNames map[string]bool
+
+ // The names of linkage variants supported by this module.
+ SupportedLinkageNames []string
+
// When set to true BpPropertyNotRequired indicates that the member type does not require the
// property to be specifiable in an Android.bp file.
BpPropertyNotRequired bool
@@ -826,6 +809,14 @@
return NewSdkMemberTraitSet(b.Traits)
}
+func (b *SdkMemberTypeBase) Overrides(other SdkMemberType) bool {
+ return b.OverridesPropertyNames[other.SdkPropertyName()]
+}
+
+func (b *SdkMemberTypeBase) SupportedLinkages() []string {
+ return b.SupportedLinkageNames
+}
+
// registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
// modules.
var registeredModuleExportsMemberTypes = &sdkRegistry{}
@@ -991,3 +982,10 @@
}
var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})
+
+// AdditionalSdkInfo contains additional properties to add to the generated SDK info file.
+type AdditionalSdkInfo struct {
+ Properties map[string]interface{}
+}
+
+var AdditionalSdkInfoProvider = blueprint.NewProvider(AdditionalSdkInfo{})
diff --git a/android/singleton.go b/android/singleton.go
index 7ff96c9..7c6cf4f 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -29,6 +29,10 @@
ModuleType(module blueprint.Module) string
BlueprintFile(module blueprint.Module) string
+ // ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer` enforcing visibility rules.
+ // Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
+ ModuleVariantsFromName(referer Module, name string) []Module
+
// ModuleProvider returns the value, if any, for the provider for a module. If the value for the
// provider was not set it returns the zero value of the type of the provider, which means the
// return value can always be type-asserted to the type of the provider. The return value should
@@ -251,3 +255,30 @@
func (s *singletonContextAdaptor) FinalModule(module Module) Module {
return s.SingletonContext.FinalModule(module).(Module)
}
+
+func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
+ // get qualified module name for visibility enforcement
+ qualified := createQualifiedModuleName(s.ModuleName(referer), s.ModuleDir(referer))
+
+ modules := s.SingletonContext.ModuleVariantsFromName(referer, name)
+ result := make([]Module, 0, len(modules))
+ for _, m := range modules {
+ if module, ok := m.(Module); ok {
+ // enforce visibility
+ depName := s.ModuleName(module)
+ depDir := s.ModuleDir(module)
+ depQualified := qualifiedModuleName{depDir, depName}
+ // Targets are always visible to other targets in their own package.
+ if depQualified.pkg != qualified.pkg {
+ rule := effectiveVisibilityRules(s.Config(), depQualified)
+ if !rule.matches(qualified) {
+ s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility",
+ referer.Name(), depQualified, "//"+s.ModuleDir(referer))
+ continue
+ }
+ }
+ result = append(result, module)
+ }
+ }
+ return result
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index eb5554c..9d98478 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -46,8 +46,8 @@
PrepareForTestWithAndroidMk,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
- ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
}),
+ PrepareForTestWithMakevars,
)
func TestSingletonModule(t *testing.T) {
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 91bbce6..bd73645 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -378,6 +378,7 @@
ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
return (map[string]blueprint.ModuleFactory)(nil)
}
+ defer r.Close()
mtDef, errs := soongconfig.Parse(r, from)
if ctx.Config().runningAsBp2Build {
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index acb9d18..ceb8e45 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -386,6 +386,46 @@
})).RunTest(t)
}
+func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
+ bp := `
+ soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c", "soc_a"],
+ }
+
+ soong_config_module_type {
+ name: "acme_test",
+ module_type: "test",
+ config_namespace: "acme",
+ variables: ["board"],
+ properties: ["cflags", "srcs", "defaults"],
+ }
+ `
+
+ fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+ return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = vars
+ })
+ }
+
+ GroupFixturePreparers(
+ fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
+ ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ // TODO(b/171232169): improve the error message for non-existent properties
+ `Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
+ })).RunTest(t)
+}
+
func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
config := TestConfig(buildDir, nil, bp, fs)
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 9bf3344..8fe1ff1 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,6 +10,7 @@
"blueprint-parser",
"blueprint-proptools",
"soong-bazel",
+ "soong-starlark-format",
],
srcs: [
"config.go",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 09a5057..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -25,6 +25,8 @@
"github.com/google/blueprint"
"github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
+
+ "android/soong/starlark_fmt"
)
const conditionsDefault = "conditions_default"
@@ -177,10 +179,14 @@
return []error{fmt.Errorf("values property must be set")}
}
+ vals := make(map[string]bool, len(stringProps.Values))
for _, name := range stringProps.Values {
if err := checkVariableName(name); err != nil {
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+ } else if _, ok := vals[name]; ok {
+ return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
}
+ vals[name] = true
}
v.variables[base.variable] = &stringVariable{
@@ -235,7 +241,12 @@
// string vars, bool vars and value vars created by every
// soong_config_module_type in this build.
type Bp2BuildSoongConfigDefinitions struct {
- StringVars map[string]map[string]bool
+ // varCache contains a cache of string variables namespace + property
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ varCache map[string]bool
+
+ StringVars map[string][]string
BoolVars map[string]bool
ValueVars map[string]bool
}
@@ -253,7 +264,7 @@
defer bp2buildSoongConfigVarsLock.Unlock()
if defs.StringVars == nil {
- defs.StringVars = make(map[string]map[string]bool)
+ defs.StringVars = make(map[string][]string)
}
if defs.BoolVars == nil {
defs.BoolVars = make(map[string]bool)
@@ -261,15 +272,24 @@
if defs.ValueVars == nil {
defs.ValueVars = make(map[string]bool)
}
+ if defs.varCache == nil {
+ defs.varCache = make(map[string]bool)
+ }
for _, moduleType := range mtDef.ModuleTypes {
for _, v := range moduleType.Variables {
key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ if _, keyInCache := defs.varCache[key]; keyInCache {
+ continue
+ } else {
+ defs.varCache[key] = true
+ }
+
if strVar, ok := v.(*stringVariable); ok {
- if _, ok := defs.StringVars[key]; !ok {
- defs.StringVars[key] = make(map[string]bool, 0)
- }
for _, value := range strVar.values {
- defs.StringVars[key][value] = true
+ defs.StringVars[key] = append(defs.StringVars[key], value)
}
} else if _, ok := v.(*boolVariable); ok {
defs.BoolVars[key] = true
@@ -302,29 +322,16 @@
// String emits the Soong config variable definitions as Starlark dictionaries.
func (defs Bp2BuildSoongConfigDefinitions) String() string {
ret := ""
- ret += "soong_config_bool_variables = {\n"
- for _, boolVar := range sortedStringKeys(defs.BoolVars) {
- ret += fmt.Sprintf(" \"%s\": True,\n", boolVar)
- }
- ret += "}\n"
- ret += "\n"
+ ret += "soong_config_bool_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
+ ret += "\n\n"
- ret += "soong_config_value_variables = {\n"
- for _, valueVar := range sortedStringKeys(defs.ValueVars) {
- ret += fmt.Sprintf(" \"%s\": True,\n", valueVar)
- }
- ret += "}\n"
- ret += "\n"
+ ret += "soong_config_value_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
+ ret += "\n\n"
- ret += "soong_config_string_variables = {\n"
- for _, stringVar := range sortedStringKeys(defs.StringVars) {
- ret += fmt.Sprintf(" \"%s\": [\n", stringVar)
- for _, choice := range sortedStringKeys(defs.StringVars[stringVar]) {
- ret += fmt.Sprintf(" \"%s\",\n", choice)
- }
- ret += fmt.Sprintf(" ],\n")
- }
- ret += "}"
+ ret += "soong_config_string_variables = "
+ ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
return ret
}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b14f8b4..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -367,19 +367,19 @@
func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
testCases := []struct {
+ desc string
defs Bp2BuildSoongConfigDefinitions
expected string
}{
{
+ desc: "all empty",
defs: Bp2BuildSoongConfigDefinitions{},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only bool",
defs: Bp2BuildSoongConfigDefinitions{
BoolVars: map[string]bool{
"bool_var": true,
@@ -389,39 +389,35 @@
"bool_var": True,
}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only value vars",
defs: Bp2BuildSoongConfigDefinitions{
ValueVars: map[string]bool{
"value_var": true,
},
},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
soong_config_value_variables = {
"value_var": True,
}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only string vars",
defs: Bp2BuildSoongConfigDefinitions{
- StringVars: map[string]map[string]bool{
- "string_var": map[string]bool{
- "choice1": true,
- "choice2": true,
- "choice3": true,
+ StringVars: map[string][]string{
+ "string_var": []string{
+ "choice1",
+ "choice2",
+ "choice3",
},
},
},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
soong_config_string_variables = {
"string_var": [
@@ -430,6 +426,7 @@
"choice3",
],
}`}, {
+ desc: "all vars",
defs: Bp2BuildSoongConfigDefinitions{
BoolVars: map[string]bool{
"bool_var_one": true,
@@ -438,15 +435,15 @@
"value_var_one": true,
"value_var_two": true,
},
- StringVars: map[string]map[string]bool{
- "string_var_one": map[string]bool{
- "choice1": true,
- "choice2": true,
- "choice3": true,
+ StringVars: map[string][]string{
+ "string_var_one": []string{
+ "choice1",
+ "choice2",
+ "choice3",
},
- "string_var_two": map[string]bool{
- "foo": true,
- "bar": true,
+ "string_var_two": []string{
+ "foo",
+ "bar",
},
},
},
@@ -466,15 +463,17 @@
"choice3",
],
"string_var_two": [
- "bar",
"foo",
+ "bar",
],
}`},
}
for _, test := range testCases {
- actual := test.defs.String()
- if actual != test.expected {
- t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
- }
+ t.Run(test.desc, func(t *testing.T) {
+ actual := test.defs.String()
+ if actual != test.expected {
+ t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+ }
+ })
}
}
diff --git a/android/testing.go b/android/testing.go
index 8daf6b7..85bdca4 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -15,6 +15,7 @@
package android
import (
+ "bytes"
"fmt"
"path/filepath"
"regexp"
@@ -23,6 +24,8 @@
"sync"
"testing"
+ mkparser "android/soong/androidmk/parser"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -80,6 +83,8 @@
FixtureRegisterWithContext(registerLicenseMutators),
)
+var PrepareForTestWithGenNotice = FixtureRegisterWithContext(RegisterGenNoticeBuildComponents)
+
func registerLicenseMutators(ctx RegistrationContext) {
ctx.PreArchMutators(RegisterLicensesPackageMapper)
ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
@@ -115,6 +120,10 @@
ctx.PreArchMutators(RegisterNamespaceMutator)
})
+var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+})
+
// Test fixture preparer that will register most java build components.
//
// Singletons and mutators should only be added here if they are needed for a majority of java
@@ -204,7 +213,7 @@
ctx.finalDeps = append(ctx.finalDeps, f)
}
-func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConfig) {
+func (ctx *TestContext) RegisterBp2BuildConfig(config bp2BuildConversionAllowlist) {
ctx.config.bp2buildPackageConfig = config
}
@@ -602,6 +611,62 @@
"\nall singletons: %v", name, allSingletonNames))
}
+type InstallMakeRule struct {
+ Target string
+ Deps []string
+ OrderOnlyDeps []string
+}
+
+func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []InstallMakeRule {
+ var rules []InstallMakeRule
+ for _, node := range nodes {
+ if mkParserRule, ok := node.(*mkparser.Rule); ok {
+ var rule InstallMakeRule
+
+ if targets := mkParserRule.Target.Words(); len(targets) == 0 {
+ t.Fatalf("no targets for rule %s", mkParserRule.Dump())
+ } else if len(targets) > 1 {
+ t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
+ } else if !targets[0].Const() {
+ t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
+ } else {
+ rule.Target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
+ }
+
+ prereqList := &rule.Deps
+ for _, prereq := range mkParserRule.Prerequisites.Words() {
+ if !prereq.Const() {
+ t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
+ }
+
+ if prereq.Value(nil) == "|" {
+ prereqList = &rule.OrderOnlyDeps
+ continue
+ }
+
+ *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
+ }
+
+ rules = append(rules, rule)
+ }
+ }
+
+ return rules
+}
+
+func (ctx *TestContext) InstallMakeRulesForTesting(t *testing.T) []InstallMakeRule {
+ installs := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
+ buf := bytes.NewBuffer(append([]byte(nil), installs...))
+ parser := mkparser.NewParser("makevars", buf)
+
+ nodes, errs := parser.Parse()
+ if len(errs) > 0 {
+ t.Fatalf("error parsing install rules: %s", errs[0])
+ }
+
+ return parseMkRules(t, ctx.config, nodes)
+}
+
func (ctx *TestContext) Config() Config {
return ctx.config
}
@@ -781,19 +846,21 @@
return p
}
-func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) TestingBuildParams {
+func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) (TestingBuildParams, []string) {
+ var searchedDescriptions []string
for _, p := range b.provider.BuildParamsForTests() {
+ searchedDescriptions = append(searchedDescriptions, p.Description)
if strings.Contains(p.Description, desc) {
- return b.newTestingBuildParams(p)
+ return b.newTestingBuildParams(p), searchedDescriptions
}
}
- return TestingBuildParams{}
+ return TestingBuildParams{}, searchedDescriptions
}
func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
- p := b.maybeBuildParamsFromDescription(desc)
+ p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc)
if p.Rule == nil {
- panic(fmt.Errorf("couldn't find description %q", desc))
+ panic(fmt.Errorf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n")))
}
return p
}
@@ -860,7 +927,8 @@
// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
// BuildParams if no rule is found.
func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
- return b.maybeBuildParamsFromDescription(desc)
+ p, _ := b.maybeBuildParamsFromDescription(desc)
+ return p
}
// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
diff --git a/android/util.go b/android/util.go
index 0ee253e..47c4583 100644
--- a/android/util.go
+++ b/android/util.go
@@ -65,21 +65,55 @@
return buf.String()
}
-// SorterStringKeys returns the keys of the given string-keyed map in the ascending order
+// SorterStringKeys returns the keys of the given string-keyed map in the ascending order.
func SortedStringKeys(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic(fmt.Sprintf("%#v is not a map", m))
}
- keys := v.MapKeys()
- s := make([]string, 0, len(keys))
- for _, key := range keys {
- s = append(s, key.String())
+ if v.Len() == 0 {
+ return nil
+ }
+ iter := v.MapRange()
+ s := make([]string, 0, v.Len())
+ for iter.Next() {
+ s = append(s, iter.Key().String())
}
sort.Strings(s)
return s
}
+// stringValues returns the values of the given string-valued map in randomized map order.
+func stringValues(m interface{}) []string {
+ v := reflect.ValueOf(m)
+ if v.Kind() != reflect.Map {
+ panic(fmt.Sprintf("%#v is not a map", m))
+ }
+ if v.Len() == 0 {
+ return nil
+ }
+ iter := v.MapRange()
+ s := make([]string, 0, v.Len())
+ for iter.Next() {
+ s = append(s, iter.Value().String())
+ }
+ return s
+}
+
+// SortedStringValues returns the values of the given string-valued map in the ascending order.
+func SortedStringValues(m interface{}) []string {
+ s := stringValues(m)
+ sort.Strings(s)
+ return s
+}
+
+// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order
+// with duplicates removed.
+func SortedUniqueStringValues(m interface{}) []string {
+ s := stringValues(m)
+ return SortedUniqueStrings(s)
+}
+
// IndexList returns the index of the first occurrence of the given string in the list or -1
func IndexList(s string, list []string) int {
for i, l := range list {
diff --git a/android/util_test.go b/android/util_test.go
index 09bec01..9b9253b 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -640,3 +640,117 @@
})
}
}
+
+func TestSortedStringKeys(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"a": "foo", "b": "bar"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "interface values",
+ in: map[string]interface{}{"a": nil, "b": nil},
+ expected: []string{"a", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedStringKeys(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestSortedStringValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"foo": "a", "bar": "b"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "duplicates",
+ in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+ expected: []string{"a", "b", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedStringValues(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestSortedUniqueStringValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"foo": "a", "bar": "b"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "duplicates",
+ in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+ expected: []string{"a", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedUniqueStringValues(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
diff --git a/android/variable.go b/android/variable.go
index ff77fef..9478c0c 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -191,6 +191,7 @@
Platform_sdk_version_or_codename *string `json:",omitempty"`
Platform_sdk_final *bool `json:",omitempty"`
Platform_sdk_extension_version *int `json:",omitempty"`
+ Platform_base_sdk_extension_version *int `json:",omitempty"`
Platform_version_active_codenames []string `json:",omitempty"`
Platform_vndk_version *string `json:",omitempty"`
Platform_systemsdk_versions []string `json:",omitempty"`
@@ -198,8 +199,10 @@
Platform_preview_sdk_version *string `json:",omitempty"`
Platform_min_supported_target_sdk_version *string `json:",omitempty"`
Platform_base_os *string `json:",omitempty"`
+ Platform_version_last_stable *string `json:",omitempty"`
DeviceName *string `json:",omitempty"`
+ DeviceProduct *string `json:",omitempty"`
DeviceArch *string `json:",omitempty"`
DeviceArchVariant *string `json:",omitempty"`
DeviceCpuVariant *string `json:",omitempty"`
@@ -305,10 +308,11 @@
JavaCoveragePaths []string `json:",omitempty"`
JavaCoverageExcludePaths []string `json:",omitempty"`
- GcovCoverage *bool `json:",omitempty"`
- ClangCoverage *bool `json:",omitempty"`
- NativeCoveragePaths []string `json:",omitempty"`
- NativeCoverageExcludePaths []string `json:",omitempty"`
+ GcovCoverage *bool `json:",omitempty"`
+ ClangCoverage *bool `json:",omitempty"`
+ NativeCoveragePaths []string `json:",omitempty"`
+ NativeCoverageExcludePaths []string `json:",omitempty"`
+ ClangCoverageContinuousMode *bool `json:",omitempty"`
// Set by NewConfig
Native_coverage *bool `json:",omitempty"`
@@ -348,6 +352,8 @@
RecoverySnapshotDirsIncluded []string `json:",omitempty"`
HostFakeSnapshotEnabled bool `json:",omitempty"`
+ MultitreeUpdateMeta bool `json:",omitempty"`
+
BoardVendorSepolicyDirs []string `json:",omitempty"`
BoardOdmSepolicyDirs []string `json:",omitempty"`
BoardReqdMaskPolicy []string `json:",omitempty"`
@@ -364,6 +370,9 @@
PlatformSepolicyVersion *string `json:",omitempty"`
TotSepolicyVersion *string `json:",omitempty"`
+ SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
+ ProductSepolicyPrebuiltApiDir *string `json:",omitempty"`
+
PlatformSepolicyCompatVersions []string `json:",omitempty"`
VendorVars map[string]map[string]string `json:",omitempty"`
@@ -383,6 +392,8 @@
CertificateOverrides []string `json:",omitempty"`
PackageNameOverrides []string `json:",omitempty"`
+ ApexGlobalMinSdkVersionOverride *string `json:",omitempty"`
+
EnforceSystemCertificate *bool `json:",omitempty"`
EnforceSystemCertificateAllowList []string `json:",omitempty"`
@@ -417,9 +428,10 @@
ShippingApiLevel *string `json:",omitempty"`
- BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
- BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
- BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
+ BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
+ BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
+ BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
+ BuildBrokenInputDirModules []string `json:",omitempty"`
BuildDebugfsRestrictionsEnabled bool `json:",omitempty"`
@@ -451,16 +463,18 @@
*v = productVariables{
BuildNumberFile: stringPtr("build_number.txt"),
- Platform_version_name: stringPtr("S"),
- Platform_sdk_version: intPtr(30),
- Platform_sdk_codename: stringPtr("S"),
- Platform_sdk_final: boolPtr(false),
- Platform_version_active_codenames: []string{"S"},
- Platform_vndk_version: stringPtr("S"),
+ Platform_version_name: stringPtr("S"),
+ Platform_base_sdk_extension_version: intPtr(30),
+ Platform_sdk_version: intPtr(30),
+ Platform_sdk_codename: stringPtr("S"),
+ Platform_sdk_final: boolPtr(false),
+ Platform_version_active_codenames: []string{"S"},
+ Platform_vndk_version: stringPtr("S"),
HostArch: stringPtr("x86_64"),
HostSecondaryArch: stringPtr("x86"),
DeviceName: stringPtr("generic_arm64"),
+ DeviceProduct: stringPtr("aosp_arm-eng"),
DeviceArch: stringPtr("arm64"),
DeviceArchVariant: stringPtr("armv8-a"),
DeviceCpuVariant: stringPtr("generic"),
diff --git a/android/visibility.go b/android/visibility.go
index 5d1be6b..b209599 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -234,7 +234,7 @@
// Checks the per-module visibility rule lists before defaults expansion.
func visibilityRuleChecker(ctx BottomUpMutatorContext) {
- qualified := createQualifiedModuleName(ctx)
+ qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir())
if m, ok := ctx.Module().(Module); ok {
visibilityProperties := m.visibilityProperties()
for _, p := range visibilityProperties {
@@ -435,7 +435,7 @@
return
}
- qualified := createQualifiedModuleName(ctx)
+ qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir())
// Visit all the dependencies making sure that this module has access to them all.
ctx.VisitDirectDeps(func(dep Module) {
@@ -486,9 +486,7 @@
return rule
}
-func createQualifiedModuleName(ctx BaseModuleContext) qualifiedModuleName {
- moduleName := ctx.ModuleName()
- dir := ctx.ModuleDir()
+func createQualifiedModuleName(moduleName, dir string) qualifiedModuleName {
qualified := qualifiedModuleName{dir, moduleName}
return qualified
}
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 714c92a..a66f0b6 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -135,7 +135,49 @@
name: "libexample",
visibility: ["//visibility:public"],
}
-
+
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }
+
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "other/Android.bp": []byte(`
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }
+
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
+ // Verify that //visibility:private allows the module to be referenced from the current
+ // directory only.
+ name: "//visibility:private",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:private"],
+ }
+
mock_library {
name: "libsamepackage",
deps: ["libexample"],
@@ -151,18 +193,61 @@
deps: ["libexample"],
}`),
},
+ expectedErrors: []string{
+ `module "libnested" variant "android_common": depends on //top:libexample which is not` +
+ ` visible to this module`,
+ `module "libother" variant "android_common": depends on //top:libexample which is not` +
+ ` visible to this module`,
+ },
},
{
// Verify that //visibility:private allows the module to be referenced from the current
// directory only.
- name: "//visibility:private",
+ name: "//visibility:private (notices)",
fs: MockFS{
"top/Android.bp": []byte(`
mock_library {
name: "libexample",
visibility: ["//visibility:private"],
}
-
+
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "nested-notice" references "//top:libexample" which is not visible to this` +
+ ` module\nYou may need to add "//top/nested" to its visibility`,
+ `module "other-notice" references "//top:libexample" which is not visible to this module\n` +
+ `You may need to add "//other" to its visibility`,
+ },
+ },
+ {
+ // Verify that :__pkg__ allows the module to be referenced from the current directory only.
+ name: ":__pkg__",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "libexample",
+ visibility: [":__pkg__"],
+ }
+
mock_library {
name: "libsamepackage",
deps: ["libexample"],
@@ -187,34 +272,32 @@
},
{
// Verify that :__pkg__ allows the module to be referenced from the current directory only.
- name: ":__pkg__",
+ name: ":__pkg__ (notices)",
fs: MockFS{
"top/Android.bp": []byte(`
mock_library {
name: "libexample",
visibility: [":__pkg__"],
}
-
- mock_library {
- name: "libsamepackage",
- deps: ["libexample"],
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
}`),
"top/nested/Android.bp": []byte(`
- mock_library {
- name: "libnested",
- deps: ["libexample"],
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
}`),
"other/Android.bp": []byte(`
- mock_library {
- name: "libother",
- deps: ["libexample"],
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
}`),
},
expectedErrors: []string{
- `module "libnested" variant "android_common": depends on //top:libexample which is not` +
- ` visible to this module`,
- `module "libother" variant "android_common": depends on //top:libexample which is not` +
- ` visible to this module`,
+ `module "nested-notice" references "//top:libexample" which is not visible to this module`,
+ `module "other-notice" references "//top:libexample" which is not visible to this module`,
},
},
{
@@ -227,7 +310,7 @@
name: "libexample",
visibility: ["//top/nested"],
}
-
+
mock_library {
name: "libsamepackage",
deps: ["libexample"],
@@ -256,6 +339,42 @@
},
},
{
+ // Verify that //top/nested allows the module to be referenced from the current directory and
+ // the top/nested directory only, not a subdirectory of top/nested and not peak directory.
+ name: "//top/nested (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "libexample",
+ visibility: ["//top/nested"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/again/Android.bp": []byte(`
+ gen_notice {
+ name: "nestedagain-notice",
+ for: ["libexample"],
+ }`),
+ "peak/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "other-notice" references "//top:libexample" which is not visible to this module`,
+ `module "nestedagain-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
// Verify that :__subpackages__ allows the module to be referenced from the current directory
// and sub directories but nowhere else.
name: ":__subpackages__",
@@ -265,7 +384,7 @@
name: "libexample",
visibility: [":__subpackages__"],
}
-
+
mock_library {
name: "libsamepackage",
deps: ["libexample"],
@@ -287,6 +406,36 @@
},
},
{
+ // Verify that :__subpackages__ allows the module to be referenced from the current directory
+ // and sub directories but nowhere else.
+ name: ":__subpackages__ (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "libexample",
+ visibility: [":__subpackages__"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "peak/other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "other-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
// Verify that //top/nested:__subpackages__ allows the module to be referenced from the current
// directory and sub directories but nowhere else.
name: "//top/nested:__subpackages__",
@@ -296,7 +445,7 @@
name: "libexample",
visibility: ["//top/nested:__subpackages__", "//other"],
}
-
+
mock_library {
name: "libsamepackage",
deps: ["libexample"],
@@ -318,6 +467,36 @@
},
},
{
+ // Verify that //top/nested:__subpackages__ allows the module to be referenced from the current
+ // directory and sub directories but nowhere else.
+ name: "//top/nested:__subpackages__ (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "libexample",
+ visibility: ["//top/nested:__subpackages__", "//other"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "top/other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "other-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
// Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from
// the current directory, top/nested and peak and all its subpackages.
name: `["//top/nested", "//peak:__subpackages__"]`,
@@ -327,7 +506,7 @@
name: "libexample",
visibility: ["//top/nested", "//peak:__subpackages__"],
}
-
+
mock_library {
name: "libsamepackage",
deps: ["libexample"],
@@ -345,6 +524,33 @@
},
},
{
+ // Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from
+ // the current directory, top/nested and peak and all its subpackages.
+ name: `["//top/nested", "//peak:__subpackages__ (notices)"]`,
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "libexample",
+ visibility: ["//top/nested", "//peak:__subpackages__"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "peak/other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
// Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__
name: `//vendor`,
fs: MockFS{
@@ -353,7 +559,7 @@
name: "libexample",
visibility: ["//vendor:__subpackages__"],
}
-
+
mock_library {
name: "libsamepackage",
visibility: ["//vendor/apps/AcmeSettings"],
@@ -418,6 +624,45 @@
},
},
{
+ // Check that visibility is the union of the defaults modules.
+ name: "defaults union, basic (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//other"],
+ }
+ mock_library {
+ name: "libexample",
+ visibility: ["//top/nested"],
+ defaults: ["libexample_defaults"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "defaults union, multiple defaults",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -459,6 +704,47 @@
},
},
{
+ name: "defaults union, multiple defaults (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults_1",
+ visibility: ["//other"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ visibility: ["//top/nested"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "//visibility:public mixed with other in defaults",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -500,6 +786,29 @@
},
},
{
+ name: "//visibility:public overriding defaults (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//namespace"],
+ }
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:public"],
+ defaults: ["libexample_defaults"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ effectiveVisibility: map[qualifiedModuleName][]string{
+ qualifiedModuleName{pkg: "top", name: "libexample"}: {"//visibility:public"},
+ },
+ },
+ {
name: "//visibility:public mixed with other from different defaults 1",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -523,6 +832,34 @@
},
},
{
+ name: "//visibility:public mixed with other from different defaults 1",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults_1",
+ visibility: ["//namespace"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ visibility: ["//visibility:public"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
name: "//visibility:public mixed with other from different defaults 2",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -546,6 +883,29 @@
},
},
{
+ name: "//visibility:public mixed with other from different defaults 2 (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults_1",
+ visibility: ["//visibility:public"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ visibility: ["//namespace"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
name: "//visibility:private in defaults",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -580,6 +940,39 @@
},
},
{
+ name: "//visibility:private in defaults (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//visibility:private"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults"],
+ }
+
+ gen_notice {
+ name: "libexample-notice",
+ for: ["libexample"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "nested-notice" references "//top:libexample" which is not visible to this module`,
+ `module "other-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "//visibility:private mixed with other in defaults",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -706,6 +1099,27 @@
},
},
{
+ name: "//visibility:override discards //visibility:private (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//visibility:private"],
+ }
+ mock_library {
+ name: "libexample",
+ // Make this visibility to //other but not //visibility:private
+ visibility: ["//visibility:override", "//other"],
+ defaults: ["libexample_defaults"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
name: "//visibility:override discards //visibility:public",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -735,6 +1149,35 @@
},
},
{
+ name: "//visibility:override discards //visibility:public (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//visibility:public"],
+ }
+ mock_library {
+ name: "libexample",
+ // Make this visibility to //other but not //visibility:public
+ visibility: ["//visibility:override", "//other"],
+ defaults: ["libexample_defaults"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ "namespace/Android.bp": []byte(`
+ gen_notice {
+ name: "namespace-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "namespace-notice" references "//top:libexample" which is not visible to this module\nYou may need to add "//namespace" to its visibility`,
+ },
+ },
+ {
name: "//visibility:override discards defaults supplied rules",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -764,6 +1207,35 @@
},
},
{
+ name: "//visibility:override discards defaults supplied rules (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//namespace"],
+ }
+ mock_library {
+ name: "libexample",
+ // Make this visibility to //other but not //namespace
+ visibility: ["//visibility:override", "//other"],
+ defaults: ["libexample_defaults"],
+ }`),
+ "other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libexample"],
+ }`),
+ "namespace/Android.bp": []byte(`
+ gen_notice {
+ name: "namespace-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "namespace-notice" references "//top:libexample" which is not visible to this module\nYou may need to add "//namespace" to its visibility`,
+ },
+ },
+ {
name: "//visibility:override can override //visibility:public with //visibility:private",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -787,6 +1259,29 @@
},
},
{
+ name: "//visibility:override can override //visibility:public with //visibility:private (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//visibility:public"],
+ }
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:override", "//visibility:private"],
+ defaults: ["libexample_defaults"],
+ }`),
+ "namespace/Android.bp": []byte(`
+ gen_notice {
+ name: "namespace-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "namespace-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "//visibility:override can override //visibility:private with //visibility:public",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -807,6 +1302,26 @@
},
},
{
+ name: "//visibility:override can override //visibility:private with //visibility:public (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults",
+ visibility: ["//visibility:private"],
+ }
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:override", "//visibility:public"],
+ defaults: ["libexample_defaults"],
+ }`),
+ "namespace/Android.bp": []byte(`
+ gen_notice {
+ name: "namespace-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
name: "//visibility:private mixed with itself",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -834,6 +1349,33 @@
` visible to this module`,
},
},
+ {
+ name: "//visibility:private mixed with itself (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_defaults {
+ name: "libexample_defaults_1",
+ visibility: ["//visibility:private"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ visibility: ["//visibility:private"],
+ }
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:private"],
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
// Defaults module's defaults_visibility tests
{
@@ -903,6 +1445,28 @@
},
},
{
+ // This test relies on the default visibility being legacy_public.
+ name: "package default_visibility property used when no visibility specified (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }
+
+ mock_library {
+ name: "libexample",
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "package default_visibility public does not override visibility private",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -926,6 +1490,28 @@
},
},
{
+ name: "package default_visibility public does not override visibility private (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ package {
+ default_visibility: ["//visibility:public"],
+ }
+
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:private"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "package default_visibility private does not override visibility public",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -945,6 +1531,25 @@
},
},
{
+ name: "package default_visibility private does not override visibility public (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }
+
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:public"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ },
+ {
name: "package default_visibility :__subpackages__",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -972,6 +1577,32 @@
},
},
{
+ name: "package default_visibility :__subpackages__ (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ package {
+ default_visibility: [":__subpackages__"],
+ }
+
+ mock_library {
+ name: "libexample",
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "package default_visibility inherited to subpackages",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -981,7 +1612,7 @@
mock_library {
name: "libexample",
- visibility: [":__subpackages__"],
+ visibility: [":__subpackages__"],
}`),
"top/nested/Android.bp": []byte(`
mock_library {
@@ -1000,6 +1631,38 @@
},
},
{
+ name: "package default_visibility inherited to subpackages (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ package {
+ default_visibility: ["//outsider"],
+ }
+
+ mock_library {
+ name: "libexample",
+ visibility: [":__subpackages__"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }
+
+ gen_notice {
+ name: "nested-notice",
+ for: ["libexample"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libexample", "libnested"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top:libexample" which is not visible to this module`,
+ },
+ },
+ {
name: "package default_visibility inherited to subpackages",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -1030,6 +1693,41 @@
},
},
{
+ name: "package default_visibility inherited to subpackages (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ package {
+ default_visibility: ["//outsider"],
+ }
+
+ mock_library {
+ name: "libnested",
+ }`),
+ "top/other/Android.bp": []byte(`
+ mock_library {
+ name: "libother",
+ }
+
+ gen_notice {
+ name: "other-notice",
+ for: ["libother"],
+ }`),
+ "outsider/Android.bp": []byte(`
+ gen_notice {
+ name: "outsider-notice",
+ for: ["libother", "libnested"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outsider-notice" references "//top/other:libother" which is not visible to this` +
+ ` module\nYou may need to add "//outsider" to its visibility`,
+ },
+ },
+ {
name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
fs: MockFS{
"prebuilts/Android.bp": []byte(`
@@ -1052,6 +1750,28 @@
},
},
{
+ name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred) (notices)",
+ fs: MockFS{
+ "prebuilts/Android.bp": []byte(`
+ prebuilt {
+ name: "module",
+ visibility: ["//top/other"],
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Android.bp": []byte(`
+ source {
+ name: "module",
+ visibility: ["//top/other"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["module"],
+ }`),
+ },
+ },
+ {
name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
fs: MockFS{
"prebuilts/Android.bp": []byte(`
@@ -1075,6 +1795,29 @@
},
},
{
+ name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred) (notices)",
+ fs: MockFS{
+ "prebuilts/Android.bp": []byte(`
+ prebuilt {
+ name: "module",
+ visibility: ["//top/other"],
+ prefer: true,
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Android.bp": []byte(`
+ source {
+ name: "module",
+ visibility: ["//top/other"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["module"],
+ }`),
+ },
+ },
+ {
name: "ensure visibility properties are checked for correctness",
fs: MockFS{
"top/Android.bp": []byte(`
@@ -1137,6 +1880,30 @@
}`),
},
},
+ {
+ name: "automatic visibility inheritance enabled (notices)",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_parent {
+ name: "parent",
+ visibility: ["//top/nested"],
+ child: {
+ name: "libchild",
+ visibility: ["//top/other"],
+ },
+ }`),
+ "top/nested/Android.bp": []byte(`
+ gen_notice {
+ name: "nested-notice",
+ for: ["libchild"],
+ }`),
+ "top/other/Android.bp": []byte(`
+ gen_notice {
+ name: "other-notice",
+ for: ["libchild"],
+ }`),
+ },
+ },
}
func TestVisibility(t *testing.T) {
@@ -1147,6 +1914,7 @@
// registration order.
PrepareForTestWithArchMutator,
PrepareForTestWithDefaults,
+ PrepareForTestWithGenNotice,
PrepareForTestWithOverrides,
PrepareForTestWithPackageModule,
PrepareForTestWithPrebuilts,
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index f050a2e..9519be0 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -107,6 +107,7 @@
func (s *sdkRepoHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
dir := android.PathForModuleOut(ctx, "zip")
+ outputZipFile := dir.Join(ctx, "output.zip")
builder := android.NewRuleBuilder(pctx, ctx).
Sbox(dir, android.PathForModuleOut(ctx, "out.sbox.textproto")).
SandboxInputs()
@@ -123,7 +124,7 @@
s.CopySpecsToDir(ctx, builder, packageSpecs, dir)
noticeFile := android.PathForModuleOut(ctx, "NOTICES.txt")
- android.BuildNoticeTextOutputFromLicenseMetadata(ctx, noticeFile)
+ android.BuildNoticeTextOutputFromLicenseMetadata(ctx, noticeFile, "", "", outputZipFile.String())
builder.Command().Text("cp").
Input(noticeFile).
Text(filepath.Join(dir.String(), "NOTICE.txt"))
@@ -209,7 +210,6 @@
}
// Zip up our temporary directory as the sdk-repo
- outputZipFile := dir.Join(ctx, "output.zip")
builder.Command().
BuiltTool("soong_zip").
FlagWithOutput("-o ", outputZipFile).
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 6fac79d..954f8d0 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -17,6 +17,7 @@
import (
"fmt"
"sort"
+ "strconv"
"strings"
mkparser "android/soong/androidmk/parser"
@@ -68,6 +69,8 @@
"LOCAL_MODULE_PATH": prebuiltModulePath,
"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
+ "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG": invert("auto_gen_config"),
+
// composite functions
"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@@ -621,6 +624,16 @@
return err
}
+// Assigns a given boolean value to a given variable in the result bp file. See
+// setVariable documentation for more information about prefix and name.
+func makeBlueprintBoolAssignment(ctx variableAssignmentContext, prefix, name string, value bool) error {
+ expressionValue, err := stringToBoolValue(strconv.FormatBool(value))
+ if err == nil {
+ err = setVariable(ctx.file, false, prefix, name, expressionValue, true)
+ }
+ return err
+}
+
// If variable is a literal variable name, return the name, otherwise return ""
func varLiteralName(variable mkparser.Variable) string {
if len(variable.Name.Variables) == 0 {
@@ -645,7 +658,11 @@
varname := ""
fixed := ""
val := ctx.mkvalue
+
if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" {
+ if varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && val.Strings[1] == "/system/priv-app" {
+ return makeBlueprintBoolAssignment(ctx, "", "privileged", true)
+ }
fixed = val.Strings[1]
varname = val.Variables[0].Name.Strings[0]
// TARGET_OUT_OPTIONAL_EXECUTABLES puts the artifact in xbin, which is
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index b8316a3..aaafdc7 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -411,6 +411,24 @@
return exp, nil
}
+// If local is set to true, then the variable will be added as a part of the
+// variable at file.bpPos. For example, if file.bpPos references a module,
+// then calling this method will set a property on that module if local is set
+// to true. Otherwise, the Variable will be created at the root of the file.
+//
+// prefix should be populated with the top level value to be assigned, and
+// name with a sub-value. If prefix is empty, then name is the top level value.
+// For example, if prefix is "foo" and name is "bar" with a value of "baz", then
+// the following variable will be generated:
+//
+// foo {
+// bar: "baz"
+// }
+//
+// If prefix is the empty string and name is "foo" with a value of "bar", the
+// following variable will be generated (if it is a property):
+//
+// foo: "bar"
func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
if prefix != "" {
name = prefix + "." + name
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 81b5c30..afde68b 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1645,6 +1645,66 @@
}
`,
},
+ {
+ desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is true",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ auto_gen_config: false,
+}
+`,
+ },
+ {
+ desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is false",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := false
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ auto_gen_config: true,
+}
+`,
+ },
+ {
+ desc: "privileged app",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ privileged: true
+}
+`,
+ },
+ {
+ desc: "convert android_app to android_test when having test_suites",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_COMPATIBILITY_SUITE := bar
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_test {
+ name: "foo",
+ test_suites: ["bar"],
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index aac4c4e..8afbe7e 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -24,14 +24,24 @@
// A MakeString is a string that may contain variable substitutions in it.
// It can be considered as an alternating list of raw Strings and variable
// substitutions, where the first and last entries in the list must be raw
-// Strings (possibly empty). A MakeString that starts with a variable
-// will have an empty first raw string, and a MakeString that ends with a
-// variable will have an empty last raw string. Two sequential Variables
-// will have an empty raw string between them.
+// Strings (possibly empty). The entirety of the text before the first variable,
+// between two variables, and after the last variable will be considered a
+// single String value. A MakeString that starts with a variable will have an
+// empty first raw string, and a MakeString that ends with a variable will have
+// an empty last raw string. Two sequential Variables will have an empty raw
+// string between them.
//
// The MakeString is stored as two lists, a list of raw Strings and a list
// of Variables. The raw string list is always one longer than the variable
// list.
+//
+// For example, "$(FOO)/bar/baz" will be represented as the
+// following lists:
+//
+// {
+// Strings: ["", "/bar/baz"],
+// Variables: ["FOO"]
+// }
type MakeString struct {
StringPos Pos
Strings []string
@@ -224,10 +234,10 @@
if n != 0 {
split := splitFunc(s, n)
if n != -1 {
- if len(split) > n {
+ if len(split) > n || len(split) == 0 {
panic("oops!")
} else {
- n -= len(split)
+ n -= len(split) - 1
}
}
curMs.appendString(split[0])
@@ -269,7 +279,7 @@
func (ms *MakeString) EndsWith(ch rune) bool {
s := ms.Strings[len(ms.Strings)-1]
- return s[len(s)-1] == uint8(ch)
+ return len(s) > 0 && s[len(s)-1] == uint8(ch)
}
func (ms *MakeString) ReplaceLiteral(input string, output string) {
diff --git a/androidmk/parser/make_strings_test.go b/androidmk/parser/make_strings_test.go
index fbb289b..7e842a5 100644
--- a/androidmk/parser/make_strings_test.go
+++ b/androidmk/parser/make_strings_test.go
@@ -75,6 +75,16 @@
genMakeString(""),
},
},
+ {
+ // "x$(var1)y bar"
+ in: genMakeString("x", "var1", "y bar"),
+ sep: " ",
+ n: 2,
+ expected: []*MakeString{
+ genMakeString("x", "var1", "y"),
+ genMakeString("bar"),
+ },
+ },
}
func TestMakeStringSplitN(t *testing.T) {
@@ -217,6 +227,36 @@
}
}
+var endsWithTestCases = []struct {
+ in *MakeString
+ endsWith rune
+ expected bool
+}{
+ {
+ in: genMakeString("foo", "X", "bar ="),
+ endsWith: '=',
+ expected: true,
+ },
+ {
+ in: genMakeString("foo", "X", "bar ="),
+ endsWith: ':',
+ expected: false,
+ },
+ {
+ in: genMakeString("foo", "X", ""),
+ endsWith: '=',
+ expected: false,
+ },
+}
+
+func TestMakeStringEndsWith(t *testing.T) {
+ for _, test := range endsWithTestCases {
+ if test.in.EndsWith(test.endsWith) != test.expected {
+ t.Errorf("with:\n%q\nexpected:\n%t\ngot:\n%t", test.in.Dump(), test.expected, !test.expected)
+ }
+ }
+}
+
func dumpArray(a []*MakeString) string {
ret := make([]string, len(a))
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index d24efc1..fb6be38 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -222,7 +222,7 @@
if d == "ifdef" || d == "ifndef" || d == "ifeq" || d == "ifneq" {
d = "el" + d
p.ignoreSpaces()
- expression = p.parseExpression()
+ expression = p.parseExpression('#')
expression.TrimRightSpaces()
} else {
p.errorf("expected ifdef/ifndef/ifeq/ifneq, found %s", d)
@@ -232,7 +232,7 @@
expression, endPos = p.parseDefine()
default:
p.ignoreSpaces()
- expression = p.parseExpression()
+ expression = p.parseExpression('#')
}
p.nodes = append(p.nodes, &Directive{
@@ -338,9 +338,6 @@
value.appendString(`\` + string(p.tok))
}
p.accept(p.tok)
- case '#':
- p.parseComment()
- break loop
case '$':
var variable Variable
variable = p.parseVariable()
@@ -522,7 +519,7 @@
// non-whitespace character after the = until the end of the logical line,
// which may included escaped newlines
p.accept('=')
- value := p.parseExpression()
+ value := p.parseExpression('#')
value.TrimLeftSpaces()
if ident.EndsWith('+') && t == "=" {
ident.TrimRightOne()
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
index f562c29..9efebf8 100644
--- a/androidmk/parser/parser_test.go
+++ b/androidmk/parser/parser_test.go
@@ -34,6 +34,56 @@
},
},
},
+ {
+ name: "Simple warning",
+ in: `$(warning A warning)`,
+ out: []Node{
+ &Variable{
+ Name: SimpleMakeString("warning A warning", NoPos),
+ },
+ },
+ },
+ {
+ name: "Warning with #",
+ in: `$(warning # A warning)`,
+ out: []Node{
+ &Variable{
+ Name: SimpleMakeString("warning # A warning", NoPos),
+ },
+ },
+ },
+ {
+ name: "Findstring with #",
+ in: `$(findstring x,x a #)`,
+ out: []Node{
+ &Variable{
+ Name: SimpleMakeString("findstring x,x a #", NoPos),
+ },
+ },
+ },
+ {
+ name: "If statement",
+ in: `ifeq (a,b) # comment
+endif`,
+ out: []Node{
+ &Directive{
+ NamePos: NoPos,
+ Name: "ifeq",
+ Args: SimpleMakeString("(a,b) ", NoPos),
+ EndPos: NoPos,
+ },
+ &Comment{
+ CommentPos: NoPos,
+ Comment: " comment",
+ },
+ &Directive{
+ NamePos: NoPos,
+ Name: "endif",
+ Args: SimpleMakeString("", NoPos),
+ EndPos: NoPos,
+ },
+ },
+ },
}
func TestParse(t *testing.T) {
diff --git a/apex/Android.bp b/apex/Android.bp
index b9b5428..d3417c2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -14,6 +14,8 @@
"soong-cc",
"soong-filesystem",
"soong-java",
+ "soong-multitree",
+ "soong-provenance",
"soong-python",
"soong-rust",
"soong-sh",
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 8cca137..938c8ed 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -309,7 +309,14 @@
return moduleNames
}
-func (a *apexBundle) writeRequiredModules(w io.Writer) {
+func (a *apexBundle) writeRequiredModules(w io.Writer, moduleNames []string) {
+ if len(moduleNames) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
+ }
+ if len(a.requiredDeps) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
+ }
+
var required []string
var targetRequired []string
var hostRequired []string
@@ -349,10 +356,7 @@
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
data.Entries.WriteLicenseVariables(w)
- if len(moduleNames) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
- }
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, moduleNames)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
@@ -369,8 +373,10 @@
}
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
- fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
- fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+ if a.installable() {
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+ }
// Because apex writes .mk with Custom(), we need to write manually some common properties
// which are available via data.Entries
@@ -388,17 +394,7 @@
if len(a.overridableProperties.Overrides) > 0 {
fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
}
- if len(moduleNames) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
- }
- if len(a.requiredDeps) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
- }
- a.writeRequiredModules(w)
-
- if a.mergedNotices.Merged.Valid() {
- fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
- }
+ a.writeRequiredModules(w, moduleNames)
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
@@ -416,6 +412,7 @@
fmt.Fprintln(w, ".PHONY:", goal)
fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
goal, a.installedFilesFile.String(), distFile)
+ fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String())
}
for _, dist := range data.Entries.GetDistForGoals(a) {
fmt.Fprintf(w, dist)
diff --git a/apex/apex.go b/apex/apex.go
index 0ac6eaa..7a2a04a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -19,6 +19,7 @@
import (
"fmt"
"path/filepath"
+ "regexp"
"sort"
"strings"
@@ -33,6 +34,7 @@
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
+ "android/soong/multitree"
"android/soong/python"
"android/soong/rust"
"android/soong/sh"
@@ -48,7 +50,7 @@
ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
ctx.RegisterModuleType("apex_defaults", defaultsFactory)
ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
- ctx.RegisterModuleType("override_apex", overrideApexFactory)
+ ctx.RegisterModuleType("override_apex", OverrideApexFactory)
ctx.RegisterModuleType("apex_set", apexSetFactory)
ctx.PreArchMutators(registerPreArchMutators)
@@ -76,6 +78,8 @@
ctx.BottomUp("apex", apexMutator).Parallel()
ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+ // Register after apex_info mutator so that it can use ApexVariationName
+ ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
}
type apexBundleProperties struct {
@@ -108,15 +112,6 @@
Multilib apexMultilibProperties
- // List of bootclasspath fragments that are embedded inside this APEX bundle.
- Bootclasspath_fragments []string
-
- // List of systemserverclasspath fragments that are embedded inside this APEX bundle.
- Systemserverclasspath_fragments []string
-
- // List of java libraries that are embedded inside this APEX bundle.
- Java_libs []string
-
// List of sh binaries that are embedded inside this APEX bundle.
Sh_binaries []string
@@ -165,12 +160,6 @@
// or else conflicting build rules may be created.
Multi_install_skip_symbol_files *bool
- // List of SDKs that are used to build this APEX. A reference to an SDK should be either
- // `name#version` or `name` which is an alias for `name#current`. If left empty,
- // `platform#current` is implied. This value affects all modules included in this APEX. In
- // other words, they are also built with the SDKs specified here.
- Uses_sdks []string
-
// The type of APEX to build. Controls what the APEX payload is. Either 'image', 'zip' or
// 'both'. When set to image, contents are stored in a filesystem image inside a zip
// container. When set to zip, contents are stored in a zip container directly. This type is
@@ -316,6 +305,15 @@
// List of BPF programs inside this APEX bundle.
Bpfs []string
+ // List of bootclasspath fragments that are embedded inside this APEX bundle.
+ Bootclasspath_fragments []string
+
+ // List of systemserverclasspath fragments that are embedded inside this APEX bundle.
+ Systemserverclasspath_fragments []string
+
+ // List of java libraries that are embedded inside this APEX bundle.
+ Java_libs []string
+
// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
// Soong). This does not completely prevent installation of the overridden binaries, but if
// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -356,6 +354,7 @@
android.OverridableModuleBase
android.SdkBase
android.BazelModuleBase
+ multitree.ExportableModuleBase
// Properties
properties apexBundleProperties
@@ -412,12 +411,16 @@
// Processed file_contexts files
fileContexts android.WritablePath
- // Struct holding the merged notice file paths in different formats
- mergedNotices android.NoticeOutputs
+ // Path to notice file in html.gz format.
+ htmlGzNotice android.WritablePath
// The built APEX file. This is the main product.
+ // Could be .apex or .capex
outputFile android.WritablePath
+ // The built uncompressed .apex file.
+ outputApexFile android.WritablePath
+
// The built APEX file in app bundle format. This file is not directly installed to the
// device. For an APEX, multiple app bundles are created each of which is for a specific ABI
// like arm, arm64, x86, etc. Then they are processed again (outside of the Android build
@@ -481,11 +484,10 @@
// for each of the files in case when the APEX is flattened.
type apexFile struct {
// buildFile is put in the installDir inside the APEX.
- builtFile android.Path
- noticeFiles android.Paths
- installDir string
- customStem string
- symlinks []string // additional symlinks
+ builtFile android.Path
+ installDir string
+ customStem string
+ symlinks []string // additional symlinks
// Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
// module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
@@ -522,7 +524,6 @@
module: module,
}
if module != nil {
- ret.noticeFiles = module.NoticeFiles()
ret.moduleDir = ctx.OtherModuleDir(module)
ret.requiredModuleNames = module.RequiredModuleNames()
ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
@@ -607,30 +608,34 @@
sourceOnly bool
}
-func (d dependencyTag) ReplaceSourceWithPrebuilt() bool {
+func (d *dependencyTag) String() string {
+ return fmt.Sprintf("apex.dependencyTag{%q}", d.name)
+}
+
+func (d *dependencyTag) ReplaceSourceWithPrebuilt() bool {
return !d.sourceOnly
}
var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
var (
- androidAppTag = dependencyTag{name: "androidApp", payload: true}
- bpfTag = dependencyTag{name: "bpf", payload: true}
- certificateTag = dependencyTag{name: "certificate"}
- executableTag = dependencyTag{name: "executable", payload: true}
- fsTag = dependencyTag{name: "filesystem", payload: true}
- bcpfTag = dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true}
- sscpfTag = dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true}
- compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
- javaLibTag = dependencyTag{name: "javaLib", payload: true}
- jniLibTag = dependencyTag{name: "jniLib", payload: true}
- keyTag = dependencyTag{name: "key"}
- prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
- rroTag = dependencyTag{name: "rro", payload: true}
- sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
- testForTag = dependencyTag{name: "test for"}
- testTag = dependencyTag{name: "test", payload: true}
- shBinaryTag = dependencyTag{name: "shBinary", payload: true}
+ androidAppTag = &dependencyTag{name: "androidApp", payload: true}
+ bpfTag = &dependencyTag{name: "bpf", payload: true}
+ certificateTag = &dependencyTag{name: "certificate"}
+ executableTag = &dependencyTag{name: "executable", payload: true}
+ fsTag = &dependencyTag{name: "filesystem", payload: true}
+ bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true}
+ sscpfTag = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true}
+ compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
+ javaLibTag = &dependencyTag{name: "javaLib", payload: true}
+ jniLibTag = &dependencyTag{name: "jniLib", payload: true}
+ keyTag = &dependencyTag{name: "key"}
+ prebuiltTag = &dependencyTag{name: "prebuilt", payload: true}
+ rroTag = &dependencyTag{name: "rro", payload: true}
+ sharedLibTag = &dependencyTag{name: "sharedLib", payload: true}
+ testForTag = &dependencyTag{name: "test for"}
+ testTag = &dependencyTag{name: "test", payload: true}
+ shBinaryTag = &dependencyTag{name: "shBinary", payload: true}
)
// TODO(jiyong): shorten this function signature
@@ -783,24 +788,8 @@
// Common-arch dependencies come next
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
- ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
- ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
- ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
-
- // Marks that this APEX (in fact all the modules in it) has to be built with the given SDKs.
- // This field currently isn't used.
- // TODO(jiyong): consider dropping this feature
- // TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
- if len(a.properties.Uses_sdks) > 0 {
- sdkRefs := []android.SdkRef{}
- for _, str := range a.properties.Uses_sdks {
- parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
- sdkRefs = append(sdkRefs, parsed)
- }
- a.BuildWithSdks(sdkRefs)
- }
}
// DepsMutator for the overridden properties.
@@ -813,6 +802,9 @@
ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+ ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.overridableProperties.Bootclasspath_fragments...)
+ ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.overridableProperties.Systemserverclasspath_fragments...)
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.overridableProperties.Java_libs...)
if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
// regardless of the TARGET_PREFER_* setting. See b/144532908
@@ -888,9 +880,18 @@
// APEX, but shared across APEXes via the VNDK APEX.
useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
- if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
- mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
- return
+ if proptools.Bool(a.properties.Use_vndk_as_stable) {
+ if !useVndk {
+ mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
+ }
+ mctx.VisitDirectDepsWithTag(sharedLibTag, func(dep android.Module) {
+ if c, ok := dep.(*cc.Module); ok && c.IsVndk() {
+ mctx.PropertyErrorf("use_vndk_as_stable", "Trying to include a VNDK library(%s) while use_vndk_as_stable is true.", dep.Name())
+ }
+ })
+ if mctx.Failed() {
+ return
+ }
}
continueApexDepsWalk := func(child, parent android.Module) bool {
@@ -953,7 +954,6 @@
apexInfo := android.ApexInfo{
ApexVariationName: apexVariationName,
MinSdkVersion: minSdkVersion,
- RequiredSdks: a.RequiredSdks(),
Updatable: a.Updatable(),
UsePlatformApis: a.UsePlatformApis(),
InApexVariants: []string{apexVariationName},
@@ -981,6 +981,7 @@
// apexInfoMutator delegates the work of identifying which modules need an ApexInfo and apex
// specific variant to modules that support the ApexInfoMutator.
+// It also propagates updatable=true to apps of updatable apexes
func apexInfoMutator(mctx android.TopDownMutatorContext) {
if !mctx.Module().Enabled() {
return
@@ -988,8 +989,84 @@
if a, ok := mctx.Module().(ApexInfoMutator); ok {
a.ApexInfoMutator(mctx)
+ }
+ enforceAppUpdatability(mctx)
+}
+
+// apexStrictUpdatibilityLintMutator propagates strict_updatability_linting to transitive deps of a mainline module
+// This check is enforced for updatable modules
+func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) {
+ if !mctx.Module().Enabled() {
return
}
+ if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting() {
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ // b/208656169 Do not propagate strict updatability linting to libcore/
+ // These libs are available on the classpath during compilation
+ // These libs are transitive deps of the sdk. See java/sdk.go:decodeSdkDep
+ // Only skip libraries defined in libcore root, not subdirectories
+ if mctx.OtherModuleDir(child) == "libcore" {
+ // Do not traverse transitive deps of libcore/ libs
+ return false
+ }
+ if android.InList(child.Name(), skipLintJavalibAllowlist) {
+ return false
+ }
+ if lintable, ok := child.(java.LintDepSetsIntf); ok {
+ lintable.SetStrictUpdatabilityLinting(true)
+ }
+ // visit transitive deps
+ return true
+ })
+ }
+}
+
+// enforceAppUpdatability propagates updatable=true to apps of updatable apexes
+func enforceAppUpdatability(mctx android.TopDownMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if apex, ok := mctx.Module().(*apexBundle); ok && apex.Updatable() {
+ // checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
+ mctx.VisitDirectDeps(func(module android.Module) {
+ // ignore android_test_app
+ if app, ok := module.(*java.AndroidApp); ok {
+ app.SetUpdatable(true)
+ }
+ })
+ }
+}
+
+// TODO: b/215736885 Whittle the denylist
+// Transitive deps of certain mainline modules baseline NewApi errors
+// Skip these mainline modules for now
+var (
+ skipStrictUpdatabilityLintAllowlist = []string{
+ "com.android.art",
+ "com.android.art.debug",
+ "com.android.conscrypt",
+ "com.android.media",
+ // test apexes
+ "test_com.android.art",
+ "test_com.android.conscrypt",
+ "test_com.android.media",
+ "test_jitzygote_com.android.art",
+ }
+
+ // TODO: b/215736885 Remove this list
+ skipLintJavalibAllowlist = []string{
+ "conscrypt.module.platform.api.stubs",
+ "conscrypt.module.public.api.stubs",
+ "conscrypt.module.public.api.stubs.system",
+ "conscrypt.module.public.api.stubs.module_lib",
+ "framework-media.stubs",
+ "framework-media.stubs.system",
+ "framework-media.stubs.module_lib",
+ }
+)
+
+func (a *apexBundle) checkStrictUpdatabilityLinting() bool {
+ return a.Updatable() && !android.InList(a.ApexVariationName(), skipStrictUpdatabilityLintAllowlist)
}
// apexUniqueVariationsMutator checks if any dependencies use unique apex variations. If so, use
@@ -1275,11 +1352,32 @@
case "", android.DefaultDistTag:
// This is the default dist path.
return android.Paths{a.outputFile}, nil
+ case imageApexSuffix:
+ // uncompressed one
+ if a.outputApexFile != nil {
+ return android.Paths{a.outputApexFile}, nil
+ }
+ fallthrough
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
+var _ multitree.Exportable = (*apexBundle)(nil)
+
+func (a *apexBundle) Exportable() bool {
+ if a.properties.ApexType == flattenedApex {
+ return false
+ }
+ return true
+}
+
+func (a *apexBundle) TaggedOutputs() map[string]android.Paths {
+ ret := make(map[string]android.Paths)
+ ret["apex"] = android.Paths{a.outputFile}
+ return ret
+}
+
var _ cc.Coverage = (*apexBundle)(nil)
// Implements cc.Coverage
@@ -1396,7 +1494,7 @@
for _, target := range ctx.MultiTargets() {
if target.Arch.ArchType.Multilib == "lib64" {
addDependenciesForNativeModules(ctx, ApexNativeDependencies{
- Native_shared_libs: []string{"libclang_rt.hwasan-aarch64-android"},
+ Native_shared_libs: []string{"libclang_rt.hwasan"},
Tests: nil,
Jni_libs: nil,
Binaries: nil,
@@ -1577,13 +1675,33 @@
var _ androidApp = (*java.AndroidApp)(nil)
var _ androidApp = (*java.AndroidAppImport)(nil)
+func sanitizedBuildIdForPath(ctx android.BaseModuleContext) string {
+ buildId := ctx.Config().BuildId()
+
+ // The build ID is used as a suffix for a filename, so ensure that
+ // the set of characters being used are sanitized.
+ // - any word character: [a-zA-Z0-9_]
+ // - dots: .
+ // - dashes: -
+ validRegex := regexp.MustCompile(`^[\w\.\-\_]+$`)
+ if !validRegex.MatchString(buildId) {
+ ctx.ModuleErrorf("Unable to use build id %s as filename suffix, valid characters are [a-z A-Z 0-9 _ . -].", buildId)
+ }
+ return buildId
+}
+
func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile {
appDir := "app"
if aapp.Privileged() {
appDir = "priv-app"
}
- dirInApex := filepath.Join(appDir, aapp.InstallApkName())
+
+ // TODO(b/224589412, b/226559955): Ensure that the subdirname is suffixed
+ // so that PackageManager correctly invalidates the existing installed apk
+ // in favour of the new APK-in-APEX. See bugs for more information.
+ dirInApex := filepath.Join(appDir, aapp.InstallApkName()+"@"+sanitizedBuildIdForPath(ctx))
fileToCopy := aapp.OutputFile()
+
af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
af.lintDepSets = aapp.LintDepSets()
@@ -1638,7 +1756,7 @@
if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
return false
}
- if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
+ if dt, ok := depTag.(*dependencyTag); ok && !dt.payload {
return false
}
@@ -1736,6 +1854,7 @@
fi := apexFileForRustLibrary(ctx, r)
fi.isJniLib = isJniLib
filesInfo = append(filesInfo, fi)
+ return true // track transitive dependencies
} else {
propertyName := "native_shared_libs"
if isJniLib {
@@ -1815,8 +1934,12 @@
if ap.Privileged() {
appDir = "priv-app"
}
- af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(),
- filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
+ // TODO(b/224589412, b/226559955): Ensure that the dirname is
+ // suffixed so that PackageManager correctly invalidates the
+ // existing installed apk in favour of the new APK-in-APEX.
+ // See bugs for more information.
+ appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
+ af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
af.certificate = java.PresignedCertificate
filesInfo = append(filesInfo, af)
} else {
@@ -2281,6 +2404,7 @@
android.InitSdkAwareModule(module)
android.InitOverridableModule(module, &module.overridableProperties.Overrides)
android.InitBazelModule(module)
+ multitree.InitExportableModule(module)
return module
}
@@ -2331,6 +2455,7 @@
type OverrideApex struct {
android.ModuleBase
android.OverrideModuleBase
+ android.BazelModuleBase
}
func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2339,16 +2464,93 @@
// override_apex is used to create an apex module based on another apex module by overriding some of
// its properties.
-func overrideApexFactory() android.Module {
+func OverrideApexFactory() android.Module {
m := &OverrideApex{}
m.AddProperties(&overridableProperties{})
android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
android.InitOverrideModule(m)
+ android.InitBazelModule(m)
return m
}
+func (o *OverrideApex) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ if ctx.ModuleType() != "override_apex" {
+ return
+ }
+
+ baseApexModuleName := o.OverrideModuleBase.GetOverriddenModuleName()
+ baseModule, baseApexExists := ctx.ModuleFromName(baseApexModuleName)
+ if !baseApexExists {
+ panic(fmt.Errorf("Base apex module doesn't exist: %s", baseApexModuleName))
+ }
+
+ a, baseModuleIsApex := baseModule.(*apexBundle)
+ if !baseModuleIsApex {
+ panic(fmt.Errorf("Base module is not apex module: %s", baseApexModuleName))
+ }
+ attrs, props := convertWithBp2build(a, ctx)
+
+ for _, p := range o.GetProperties() {
+ overridableProperties, ok := p.(*overridableProperties)
+ if !ok {
+ continue
+ }
+
+ // Manifest is either empty or a file in the directory of base APEX and is not overridable.
+ // After it is converted in convertWithBp2build(baseApex, ctx),
+ // the attrs.Manifest.Value.Label is the file path relative to the directory
+ // of base apex. So the following code converts it to a label that looks like
+ // <package of base apex>:<path of manifest file> if base apex and override
+ // apex are not in the same package.
+ baseApexPackage := ctx.OtherModuleDir(a)
+ overrideApexPackage := ctx.ModuleDir()
+ if baseApexPackage != overrideApexPackage {
+ attrs.Manifest.Value.Label = "//" + baseApexPackage + ":" + attrs.Manifest.Value.Label
+ }
+
+ // Key
+ if overridableProperties.Key != nil {
+ attrs.Key = bazel.LabelAttribute{}
+ attrs.Key.SetValue(android.BazelLabelForModuleDepSingle(ctx, *overridableProperties.Key))
+ }
+
+ // Certificate
+ if overridableProperties.Certificate != nil {
+ attrs.Certificate = bazel.LabelAttribute{}
+ attrs.Certificate.SetValue(android.BazelLabelForModuleDepSingle(ctx, *overridableProperties.Certificate))
+ }
+
+ // Prebuilts
+ prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, overridableProperties.Prebuilts)
+ attrs.Prebuilts = bazel.MakeLabelListAttribute(prebuiltsLabelList)
+
+ // Compressible
+ if overridableProperties.Compressible != nil {
+ attrs.Compressible = bazel.BoolAttribute{Value: overridableProperties.Compressible}
+ }
+
+ // Package name
+ //
+ // e.g. com.android.adbd's package name is com.android.adbd, but
+ // com.google.android.adbd overrides the package name to com.google.android.adbd
+ //
+ // TODO: this can be overridden from the product configuration, see
+ // getOverrideManifestPackageName and
+ // PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES.
+ //
+ // Instead of generating the BUILD files differently based on the product config
+ // at the point of conversion, this should be handled by the BUILD file loading
+ // from the soong_injection's product_vars, so product config is decoupled from bp2build.
+ if overridableProperties.Package_name != "" {
+ attrs.Package_name = &overridableProperties.Package_name
+ }
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: o.Name()}, &attrs)
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
// Vality check routines
//
@@ -2370,20 +2572,43 @@
android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
}
+// Returns apex's min_sdk_version string value, honoring overrides
+func (a *apexBundle) minSdkVersionValue(ctx android.EarlyModuleContext) string {
+ // Only override the minSdkVersion value on Apexes which already specify
+ // a min_sdk_version (it's optional for non-updatable apexes), and that its
+ // min_sdk_version value is lower than the one to override with.
+ overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
+ overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
+ originalMinApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+ isMinSdkSet := a.properties.Min_sdk_version != nil
+ isOverrideValueHigher := overrideApiLevel.CompareTo(originalMinApiLevel) > 0
+ if overrideMinSdkValue != "" && isMinSdkSet && isOverrideValueHigher {
+ return overrideMinSdkValue
+ }
+
+ return proptools.String(a.properties.Min_sdk_version)
+}
+
+// Returns apex's min_sdk_version SdkSpec, honoring overrides
func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
return android.SdkSpec{
Kind: android.SdkNone,
ApiLevel: a.minSdkVersion(ctx),
- Raw: String(a.properties.Min_sdk_version),
+ Raw: a.minSdkVersionValue(ctx),
}
}
+// Returns apex's min_sdk_version ApiLevel, honoring overrides
func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
- ver := proptools.String(a.properties.Min_sdk_version)
- if ver == "" {
+ return minSdkVersionFromValue(ctx, a.minSdkVersionValue(ctx))
+}
+
+// Construct ApiLevel object from min_sdk_version string value
+func minSdkVersionFromValue(ctx android.EarlyModuleContext, value string) android.ApiLevel {
+ if value == "" {
return android.NoneApiLevel
}
- apiLevel, err := android.ApiLevelFromUser(ctx, ver)
+ apiLevel, err := android.ApiLevelFromUser(ctx, value)
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
return android.NoneApiLevel
@@ -2438,7 +2663,7 @@
// checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
if a.Updatable() {
- if String(a.properties.Min_sdk_version) == "" {
+ if a.minSdkVersionValue(ctx) == "" {
ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
}
if a.UsePlatformApis() {
@@ -2564,7 +2789,7 @@
// A small list of exceptions where static executables are allowed in APEXes.
func isStaticExecutableAllowed(apex string, exec string) bool {
m := map[string][]string{
- "com.android.runtime": []string{
+ "com.android.runtime": {
"linker",
"linkerconfig",
},
@@ -2575,9 +2800,9 @@
// Collect information for opening IDE project files in java/jdeps.go.
func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
+ dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Java_libs...)
+ dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Bootclasspath_fragments...)
+ dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Systemserverclasspath_fragments...)
dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
}
@@ -3029,33 +3254,6 @@
//
// Module separator
//
- m["com.android.permission"] = []string{
- "car-ui-lib",
- "iconloader",
- "kotlin-annotations",
- "kotlin-stdlib",
- "kotlin-stdlib-jdk7",
- "kotlin-stdlib-jdk8",
- "kotlinx-coroutines-android",
- "kotlinx-coroutines-android-nodeps",
- "kotlinx-coroutines-core",
- "kotlinx-coroutines-core-nodeps",
- "permissioncontroller-statsd",
- "GooglePermissionController",
- "PermissionController",
- "SettingsLibActionBarShadow",
- "SettingsLibAppPreference",
- "SettingsLibBarChartPreference",
- "SettingsLibLayoutPreference",
- "SettingsLibProgressBar",
- "SettingsLibSearchWidget",
- "SettingsLibSettingsTheme",
- "SettingsLibRestrictedLockUtils",
- "SettingsLibHelpUtils",
- }
- //
- // Module separator
- //
m["com.android.runtime"] = []string{
"bionic_libc_platform_headers",
"libarm-optimized-routines-math",
@@ -3198,21 +3396,19 @@
}
func init() {
- android.AddNeverAllowRules(createApexPermittedPackagesRules(qModulesPackages())...)
- android.AddNeverAllowRules(createApexPermittedPackagesRules(rModulesPackages())...)
+ android.AddNeverAllowRules(createBcpPermittedPackagesRules(qBcpPackages())...)
+ android.AddNeverAllowRules(createBcpPermittedPackagesRules(rBcpPackages())...)
}
-func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
- rules := make([]android.Rule, 0, len(modules_packages))
- for module_name, module_packages := range modules_packages {
+func createBcpPermittedPackagesRules(bcpPermittedPackages map[string][]string) []android.Rule {
+ rules := make([]android.Rule, 0, len(bcpPermittedPackages))
+ for jar, permittedPackages := range bcpPermittedPackages {
permittedPackagesRule := android.NeverAllow().
- BootclasspathJar().
- With("apex_available", module_name).
- WithMatcher("permitted_packages", android.NotInList(module_packages)).
- WithMatcher("min_sdk_version", android.LessThanSdkVersion("Tiramisu")).
- Because("jars that are part of the " + module_name +
- " module may only use these package prefixes: " + strings.Join(module_packages, ",") +
- " with min_sdk < T. Please consider the following alternatives:\n" +
+ With("name", jar).
+ WithMatcher("permitted_packages", android.NotInList(permittedPackages)).
+ Because(jar +
+ " bootjar may only use these package prefixes: " + strings.Join(permittedPackages, ",") +
+ ". Please consider the following alternatives:\n" +
" 1. If the offending code is from a statically linked library, consider " +
"removing that dependency and using an alternative already in the " +
"bootclasspath, or perhaps a shared library." +
@@ -3220,55 +3416,56 @@
" 3. Jarjar the offending code. Please be mindful of the potential system " +
"health implications of bundling that code, particularly if the offending jar " +
"is part of the bootclasspath.")
+
rules = append(rules, permittedPackagesRule)
}
return rules
}
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on Q/R/S.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
// Adding code to the bootclasspath in new packages will cause issues on module update.
-func qModulesPackages() map[string][]string {
+func qBcpPackages() map[string][]string {
return map[string][]string{
- "com.android.conscrypt": []string{
+ "conscrypt": {
"android.net.ssl",
"com.android.org.conscrypt",
},
- "com.android.media": []string{
+ "updatable-media": {
"android.media",
},
}
}
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on R/S.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
// Adding code to the bootclasspath in new packages will cause issues on module update.
-func rModulesPackages() map[string][]string {
+func rBcpPackages() map[string][]string {
return map[string][]string{
- "com.android.mediaprovider": []string{
+ "framework-mediaprovider": {
"android.provider",
},
- "com.android.permission": []string{
+ "framework-permission": {
"android.permission",
"android.app.role",
"com.android.permission",
"com.android.role",
},
- "com.android.sdkext": []string{
+ "framework-sdkextensions": {
"android.os.ext",
},
- "com.android.os.statsd": []string{
+ "framework-statsd": {
"android.app",
"android.os",
"android.util",
"com.android.internal.statsd",
"com.android.server.stats",
},
- "com.android.wifi": []string{
+ "framework-wifi": {
"com.android.server.wifi",
"com.android.wifi.x",
"android.hardware.wifi",
"android.net.wifi",
},
- "com.android.tethering": []string{
+ "framework-tethering": {
"android.net",
},
}
@@ -3290,6 +3487,7 @@
Native_shared_libs_32 bazel.LabelListAttribute
Native_shared_libs_64 bazel.LabelListAttribute
Compressible bazel.BoolAttribute
+ Package_name *string
}
type convertedNativeSharedLibs struct {
@@ -3304,10 +3502,13 @@
return
}
+ attrs, props := convertWithBp2build(a, ctx)
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, &attrs)
+}
+
+func convertWithBp2build(a *apexBundle, ctx android.TopDownMutatorContext) (bazelApexBundleAttributes, bazel.BazelTargetModuleProperties) {
var manifestLabelAttribute bazel.LabelAttribute
- if a.properties.Manifest != nil {
- manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.Manifest))
- }
+ manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")))
var androidManifestLabelAttribute bazel.LabelAttribute
if a.properties.AndroidManifest != nil {
@@ -3315,10 +3516,19 @@
}
var fileContextsLabelAttribute bazel.LabelAttribute
- if a.properties.File_contexts != nil {
+ if a.properties.File_contexts == nil {
+ // See buildFileContexts(), if file_contexts is not specified the default one is used, which is //system/sepolicy/apex:<module name>-file_contexts
+ fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, a.Name()+"-file_contexts"))
+ } else if strings.HasPrefix(*a.properties.File_contexts, ":") {
+ // File_contexts is a module
fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.properties.File_contexts))
+ } else {
+ // File_contexts is a file
+ fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.File_contexts))
}
+ // TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
+ // given it's coming via config, we probably don't want to put it in here.
var minSdkVersion *string
if a.properties.Min_sdk_version != nil {
minSdkVersion = a.properties.Min_sdk_version
@@ -3372,7 +3582,12 @@
compressibleAttribute.Value = a.overridableProperties.Compressible
}
- attrs := &bazelApexBundleAttributes{
+ var packageName *string
+ if a.overridableProperties.Package_name != "" {
+ packageName = &a.overridableProperties.Package_name
+ }
+
+ attrs := bazelApexBundleAttributes{
Manifest: manifestLabelAttribute,
Android_manifest: androidManifestLabelAttribute,
File_contexts: fileContextsLabelAttribute,
@@ -3386,14 +3601,15 @@
Binaries: binariesLabelListAttribute,
Prebuilts: prebuiltsLabelListAttribute,
Compressible: compressibleAttribute,
+ Package_name: packageName,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "apex",
- Bzl_load_location: "//build/bazel/rules:apex.bzl",
+ Bzl_load_location: "//build/bazel/rules/apex:apex.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+ return attrs, props
}
// The following conversions are based on this table where the rows are the compile_multilib
diff --git a/apex/apex_test.go b/apex/apex_test.go
index c546fa1..7905710 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -113,6 +113,12 @@
})
}
+func withApexGlobalMinSdkVersionOverride(minSdkOverride *string) android.FixturePreparer {
+ return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ApexGlobalMinSdkVersionOverride = minSdkOverride
+ })
+}
+
var withBinder32bit = android.FixtureModifyProductVariables(
func(variables android.FixtureProductVariables) {
variables.Binder32bit = proptools.BoolPtr(true)
@@ -168,44 +174,42 @@
"system/sepolicy/apex/otherapex-file_contexts": nil,
"system/sepolicy/apex/com.android.vndk-file_contexts": nil,
"system/sepolicy/apex/com.android.vndk.current-file_contexts": nil,
- "mylib.cpp": nil,
- "mytest.cpp": nil,
- "mytest1.cpp": nil,
- "mytest2.cpp": nil,
- "mytest3.cpp": nil,
- "myprebuilt": nil,
- "my_include": nil,
- "foo/bar/MyClass.java": nil,
- "prebuilt.jar": nil,
- "prebuilt.so": nil,
- "vendor/foo/devkeys/test.x509.pem": nil,
- "vendor/foo/devkeys/test.pk8": nil,
- "testkey.x509.pem": nil,
- "testkey.pk8": nil,
- "testkey.override.x509.pem": nil,
- "testkey.override.pk8": nil,
- "vendor/foo/devkeys/testkey.avbpubkey": nil,
- "vendor/foo/devkeys/testkey.pem": nil,
- "NOTICE": nil,
- "custom_notice": nil,
- "custom_notice_for_static_lib": nil,
- "testkey2.avbpubkey": nil,
- "testkey2.pem": nil,
- "myapex-arm64.apex": nil,
- "myapex-arm.apex": nil,
- "myapex.apks": nil,
- "frameworks/base/api/current.txt": nil,
- "framework/aidl/a.aidl": nil,
- "build/make/core/proguard.flags": nil,
- "build/make/core/proguard_basic_keeps.flags": nil,
- "dummy.txt": nil,
- "baz": nil,
- "bar/baz": nil,
- "testdata/baz": nil,
- "AppSet.apks": nil,
- "foo.rs": nil,
- "libfoo.jar": nil,
- "libbar.jar": nil,
+ "mylib.cpp": nil,
+ "mytest.cpp": nil,
+ "mytest1.cpp": nil,
+ "mytest2.cpp": nil,
+ "mytest3.cpp": nil,
+ "myprebuilt": nil,
+ "my_include": nil,
+ "foo/bar/MyClass.java": nil,
+ "prebuilt.jar": nil,
+ "prebuilt.so": nil,
+ "vendor/foo/devkeys/test.x509.pem": nil,
+ "vendor/foo/devkeys/test.pk8": nil,
+ "testkey.x509.pem": nil,
+ "testkey.pk8": nil,
+ "testkey.override.x509.pem": nil,
+ "testkey.override.pk8": nil,
+ "vendor/foo/devkeys/testkey.avbpubkey": nil,
+ "vendor/foo/devkeys/testkey.pem": nil,
+ "NOTICE": nil,
+ "custom_notice": nil,
+ "custom_notice_for_static_lib": nil,
+ "testkey2.avbpubkey": nil,
+ "testkey2.pem": nil,
+ "myapex-arm64.apex": nil,
+ "myapex-arm.apex": nil,
+ "myapex.apks": nil,
+ "frameworks/base/api/current.txt": nil,
+ "framework/aidl/a.aidl": nil,
+ "dummy.txt": nil,
+ "baz": nil,
+ "bar/baz": nil,
+ "testdata/baz": nil,
+ "AppSet.apks": nil,
+ "foo.rs": nil,
+ "libfoo.jar": nil,
+ "libbar.jar": nil,
},
),
@@ -219,6 +223,7 @@
// not because of these tests specifically (it's not used by the tests)
variables.Platform_version_active_codenames = []string{"Q", "Tiramisu"}
variables.Platform_vndk_version = proptools.StringPtr("29")
+ variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
}),
)
@@ -591,15 +596,6 @@
t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
}
- mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("mergeNoticesRule")
- noticeInputs := mergeNoticesRule.Inputs.Strings()
- if len(noticeInputs) != 3 {
- t.Errorf("number of input notice files: expected = 3, actual = %q", len(noticeInputs))
- }
- ensureListContains(t, noticeInputs, "NOTICE")
- ensureListContains(t, noticeInputs, "custom_notice")
- ensureListContains(t, noticeInputs, "custom_notice_for_static_lib")
-
fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
ensureListContains(t, fullDepsInfo, " myjar(minSdkVersion:(no version)) <- myapex")
ensureListContains(t, fullDepsInfo, " mylib2(minSdkVersion:(no version)) <- mylib")
@@ -687,7 +683,7 @@
"etc/myetc",
"javalib/myjar.jar",
"lib64/mylib.so",
- "app/AppFoo/AppFoo.apk",
+ "app/AppFoo@TEST.BUILD_ID/AppFoo.apk",
"overlay/blue/rro.apk",
"etc/bpf/bpf.o",
"etc/bpf/bpf2.o",
@@ -971,6 +967,9 @@
rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
+
+ apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+ ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
}
func TestApexCanUsePrivateApis(t *testing.T) {
@@ -1038,10 +1037,10 @@
// Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because
// of the platform_apis: true)
- mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000_private").Rule("ld").Args["libFlags"]
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
- rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000_private").Rule("rustc").Args["linkFlags"]
+ rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
}
@@ -1412,13 +1411,14 @@
}
cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ name: "libclang_rt.hwasan",
no_libcrt: true,
nocrt: true,
stl: "none",
system_shared_libs: [],
srcs: [""],
stubs: { versions: ["1"] },
+ stem: "libclang_rt.hwasan-aarch64-android",
sanitize: {
never: true,
@@ -1431,7 +1431,7 @@
"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
})
- hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+ hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared")
installed := hwasan.Description("install libclang_rt.hwasan")
ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
@@ -1459,13 +1459,14 @@
}
cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ name: "libclang_rt.hwasan",
no_libcrt: true,
nocrt: true,
stl: "none",
system_shared_libs: [],
srcs: [""],
stubs: { versions: ["1"] },
+ stem: "libclang_rt.hwasan-aarch64-android",
sanitize: {
never: true,
@@ -1479,7 +1480,7 @@
"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
})
- hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+ hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared")
installed := hwasan.Description("install libclang_rt.hwasan")
ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
@@ -2393,6 +2394,7 @@
key: "myapex.key",
apps: ["AppFoo"],
min_sdk_version: "29",
+ updatable: false,
}
apex_key {
@@ -2713,7 +2715,33 @@
ensureListNotContains(t, requireNativeLibs, ":vndk")
}
+func TestVendorApex_use_vndk_as_stable_TryingToIncludeVNDKLib(t *testing.T) {
+ testApexError(t, `Trying to include a VNDK library`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libc++"], // libc++ is a VNDK lib
+ vendor: true,
+ use_vndk_as_stable: true,
+ updatable: false,
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }`)
+}
+
func TestVendorApex_use_vndk_as_stable(t *testing.T) {
+ // myapex myapex2
+ // | |
+ // mybin ------. mybin2
+ // \ \ / |
+ // (stable) .---\--------` |
+ // \ / \ |
+ // \ / \ /
+ // libvndk libvendor
+ // (vndk)
ctx := testApex(t, `
apex {
name: "myapex",
@@ -2744,28 +2772,95 @@
cc_library {
name: "libvendor",
vendor: true,
+ stl: "none",
+ }
+ apex {
+ name: "myapex2",
+ key: "myapex.key",
+ binaries: ["mybin2"],
+ vendor: true,
+ use_vndk_as_stable: false,
+ updatable: false,
+ }
+ cc_binary {
+ name: "mybin2",
+ vendor: true,
+ shared_libs: ["libvndk", "libvendor"],
}
`)
vendorVariant := "android_vendor.29_arm64_armv8-a"
- ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
- libs := names(ldRule.Args["libFlags"])
- // VNDK libs(libvndk/libc++) as they are
- ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
- ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
- // non-stable Vendor libs as APEX variants
- ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+ for _, tc := range []struct {
+ name string
+ apexName string
+ moduleName string
+ moduleVariant string
+ libs []string
+ contents []string
+ requireVndkNamespace bool
+ }{
+ {
+ name: "use_vndk_as_stable",
+ apexName: "myapex",
+ moduleName: "mybin",
+ moduleVariant: vendorVariant + "_apex10000",
+ libs: []string{
+ // should link with vendor variants of VNDK libs(libvndk/libc++)
+ "out/soong/.intermediates/libvndk/" + vendorVariant + "_shared/libvndk.so",
+ "out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared/libc++.so",
+ // unstable Vendor libs as APEX variant
+ "out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
+ },
+ contents: []string{
+ "bin/mybin",
+ "lib64/libvendor.so",
+ // VNDK libs (libvndk/libc++) are not included
+ },
+ requireVndkNamespace: true,
+ },
+ {
+ name: "!use_vndk_as_stable",
+ apexName: "myapex2",
+ moduleName: "mybin2",
+ moduleVariant: vendorVariant + "_myapex2",
+ libs: []string{
+ // should link with "unique" APEX(myapex2) variant of VNDK libs(libvndk/libc++)
+ "out/soong/.intermediates/libvndk/" + vendorVariant + "_shared_myapex2/libvndk.so",
+ "out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared_myapex2/libc++.so",
+ // unstable vendor libs have "merged" APEX variants
+ "out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
+ },
+ contents: []string{
+ "bin/mybin2",
+ "lib64/libvendor.so",
+ // VNDK libs are included as well
+ "lib64/libvndk.so",
+ "lib64/libc++.so",
+ },
+ requireVndkNamespace: false,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ // Check linked libs
+ ldRule := ctx.ModuleForTests(tc.moduleName, tc.moduleVariant).Rule("ld")
+ libs := names(ldRule.Args["libFlags"])
+ for _, lib := range tc.libs {
+ ensureListContains(t, libs, lib)
+ }
+ // Check apex contents
+ ensureExactContents(t, ctx, tc.apexName, "android_common_"+tc.apexName+"_image", tc.contents)
- // VNDK libs are not included when use_vndk_as_stable: true
- ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
- "bin/mybin",
- "lib64/libvendor.so",
- })
-
- apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
- requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
- ensureListContains(t, requireNativeLibs, ":vndk")
+ // Check "requireNativeLibs"
+ apexManifestRule := ctx.ModuleForTests(tc.apexName, "android_common_"+tc.apexName+"_image").Rule("apexManifestRule")
+ requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
+ if tc.requireVndkNamespace {
+ ensureListContains(t, requireNativeLibs, ":vndk")
+ } else {
+ ensureListNotContains(t, requireNativeLibs, ":vndk")
+ }
+ })
+ }
}
func TestProductVariant(t *testing.T) {
@@ -2796,7 +2891,7 @@
)
cflags := strings.Fields(
- ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
+ ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_myapex").Rule("cc").Args["cFlags"])
ensureListContains(t, cflags, "-D__ANDROID_VNDK__")
ensureListContains(t, cflags, "-D__ANDROID_APEX__")
ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__")
@@ -3800,7 +3895,7 @@
}),
withBinder32bit,
withTargets(map[android.OsType][]android.Target{
- android.Android: []android.Target{
+ android.Android: {
{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
},
@@ -4481,12 +4576,20 @@
}
`)
- prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
+ testingModule := ctx.ModuleForTests("myapex", "android_common_myapex")
+ prebuilt := testingModule.Module().(*Prebuilt)
expectedInput := "myapex-arm64.apex"
if prebuilt.inputApex.String() != expectedInput {
t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String())
}
+ android.AssertStringDoesContain(t, "Invalid provenance metadata file",
+ prebuilt.ProvenanceMetaDataFile().String(), "soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto")
+ rule := testingModule.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "myapex-arm64.apex", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.apex", rule.Args["install_path"])
}
func TestPrebuiltMissingSrc(t *testing.T) {
@@ -4506,12 +4609,18 @@
}
`)
- p := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
+ testingModule := ctx.ModuleForTests("myapex", "android_common_myapex")
+ p := testingModule.Module().(*Prebuilt)
expected := "notmyapex.apex"
if p.installFilename != expected {
t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename)
}
+ rule := testingModule.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "myapex-arm.apex", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/apex/notmyapex.apex", rule.Args["install_path"])
}
func TestApexSetFilenameOverride(t *testing.T) {
@@ -4554,13 +4663,19 @@
}
`)
- p := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt").Module().(*Prebuilt)
+ testingModule := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt")
+ p := testingModule.Module().(*Prebuilt)
expected := []string{"myapex"}
actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected)
}
+ rule := testingModule.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "myapex-arm.apex", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex.prebuilt/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "myapex.prebuilt", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.prebuilt.apex", rule.Args["install_path"])
}
func TestPrebuiltApexName(t *testing.T) {
@@ -5569,8 +5684,8 @@
apexRule := module.Rule("apexRule")
copyCmds := apexRule.Args["copy_commands"]
- ensureContains(t, copyCmds, "image.apex/app/AppFoo/AppFoo.apk")
- ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk")
+ ensureContains(t, copyCmds, "image.apex/app/AppFoo@TEST.BUILD_ID/AppFoo.apk")
+ ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk")
appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs")
// JNI libraries are uncompressed
@@ -5587,6 +5702,36 @@
}
}
+func TestApexWithAppImportBuildId(t *testing.T) {
+ invalidBuildIds := []string{"../", "a b", "a/b", "a/b/../c", "/a"}
+ for _, id := range invalidBuildIds {
+ message := fmt.Sprintf("Unable to use build id %s as filename suffix", id)
+ fixture := android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildId = proptools.StringPtr(id)
+ })
+ testApexError(t, message, `apex {
+ name: "myapex",
+ key: "myapex.key",
+ apps: ["AppFooPrebuilt"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ android_app_import {
+ name: "AppFooPrebuilt",
+ apk: "PrebuiltAppFoo.apk",
+ presigned: true,
+ apex_available: ["myapex"],
+ }
+ `, fixture)
+ }
+}
+
func TestApexWithAppImports(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -5632,8 +5777,8 @@
apexRule := module.Rule("apexRule")
copyCmds := apexRule.Args["copy_commands"]
- ensureContains(t, copyCmds, "image.apex/app/AppFooPrebuilt/AppFooPrebuilt.apk")
- ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPrivPrebuilt/AwesomePrebuiltAppFooPriv.apk")
+ ensureContains(t, copyCmds, "image.apex/app/AppFooPrebuilt@TEST.BUILD_ID/AppFooPrebuilt.apk")
+ ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPrivPrebuilt@TEST.BUILD_ID/AwesomePrebuiltAppFooPriv.apk")
}
func TestApexWithAppImportsPrefer(t *testing.T) {
@@ -5674,7 +5819,7 @@
}))
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
- "app/AppFoo/AppFooPrebuilt.apk",
+ "app/AppFoo@TEST.BUILD_ID/AppFooPrebuilt.apk",
})
}
@@ -5707,7 +5852,7 @@
apexRule := module.Rule("apexRule")
copyCmds := apexRule.Args["copy_commands"]
- ensureContains(t, copyCmds, "image.apex/app/TesterHelpAppFoo/TesterHelpAppFoo.apk")
+ ensureContains(t, copyCmds, "image.apex/app/TesterHelpAppFoo@TEST.BUILD_ID/TesterHelpAppFoo.apk")
}
func TestApexPropertiesShouldBeDefaultable(t *testing.T) {
@@ -5786,7 +5931,7 @@
func TestApexAvailable_IndirectDep(t *testing.T) {
// libbbaz is an indirect dep
testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path:
-.*via tag apex\.dependencyTag.*name:sharedLib.*
+.*via tag apex\.dependencyTag\{"sharedLib"\}
.*-> libfoo.*link:shared.*
.*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.*
.*-> libbar.*link:shared.*
@@ -5995,6 +6140,9 @@
apps: ["app"],
bpfs: ["bpf"],
prebuilts: ["myetc"],
+ bootclasspath_fragments: ["mybootclasspath_fragment"],
+ systemserverclasspath_fragments: ["mysystemserverclasspath_fragment"],
+ java_libs: ["myjava_library"],
overrides: ["oldapex"],
updatable: false,
}
@@ -6005,6 +6153,9 @@
apps: ["override_app"],
bpfs: ["override_bpf"],
prebuilts: ["override_myetc"],
+ bootclasspath_fragments: ["override_bootclasspath_fragment"],
+ systemserverclasspath_fragments: ["override_systemserverclasspath_fragment"],
+ java_libs: ["override_java_library"],
overrides: ["unknownapex"],
logging_parent: "com.foo.bar",
package_name: "test.overridden.package",
@@ -6063,6 +6214,78 @@
name: "override_myetc",
src: "override_myprebuilt",
}
+
+ java_library {
+ name: "bcplib",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["bcp.lib"],
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspath_fragment",
+ contents: ["bcplib"],
+ apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
+ }
+
+ java_library {
+ name: "override_bcplib",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["override.bcp.lib"],
+ }
+
+ bootclasspath_fragment {
+ name: "override_bootclasspath_fragment",
+ contents: ["override_bcplib"],
+ apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
+ }
+
+ java_library {
+ name: "systemserverlib",
+ srcs: ["a.java"],
+ apex_available: ["myapex"],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspath_fragment",
+ standalone_contents: ["systemserverlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "override_systemserverlib",
+ srcs: ["a.java"],
+ apex_available: ["myapex"],
+ }
+
+ systemserverclasspath_fragment {
+ name: "override_systemserverclasspath_fragment",
+ standalone_contents: ["override_systemserverlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "myjava_library",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "override_java_library",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ }
`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6078,8 +6301,8 @@
apexRule := module.Rule("apexRule")
copyCmds := apexRule.Args["copy_commands"]
- ensureNotContains(t, copyCmds, "image.apex/app/app/app.apk")
- ensureContains(t, copyCmds, "image.apex/app/override_app/override_app.apk")
+ ensureNotContains(t, copyCmds, "image.apex/app/app@TEST.BUILD_ID/app.apk")
+ ensureContains(t, copyCmds, "image.apex/app/override_app@TEST.BUILD_ID/override_app.apk")
ensureNotContains(t, copyCmds, "image.apex/etc/bpf/bpf.o")
ensureContains(t, copyCmds, "image.apex/etc/bpf/override_bpf.o")
@@ -6097,6 +6320,13 @@
t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent)
}
+ android.AssertArrayString(t, "Bootclasspath_fragments does not match",
+ []string{"override_bootclasspath_fragment"}, apexBundle.overridableProperties.Bootclasspath_fragments)
+ android.AssertArrayString(t, "Systemserverclasspath_fragments does not match",
+ []string{"override_systemserverclasspath_fragment"}, apexBundle.overridableProperties.Systemserverclasspath_fragments)
+ android.AssertArrayString(t, "Java_libs does not match",
+ []string{"override_java_library"}, apexBundle.overridableProperties.Java_libs)
+
optFlags := apexRule.Args["opt_flags"]
ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey")
@@ -6111,15 +6341,139 @@
ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
+ ensureNotContains(t, androidMk, "LOCAL_MODULE := override_bcplib.myapex")
+ ensureNotContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.myapex")
+ ensureNotContains(t, androidMk, "LOCAL_MODULE := override_java_library.pb.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
}
+func TestMinSdkVersionOverride(t *testing.T) {
+ // Override from 29 to 31
+ minSdkOverride31 := "31"
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ updatable: true,
+ min_sdk_version: "29"
+ }
+
+ override_apex {
+ name: "override_myapex",
+ base: "myapex",
+ logging_parent: "com.foo.bar",
+ package_name: "test.overridden.package"
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ runtime_libs: ["libbar"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ min_sdk_version: "apex_inherit"
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ min_sdk_version: "apex_inherit"
+ }
+
+ `, withApexGlobalMinSdkVersionOverride(&minSdkOverride31))
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that direct non-stubs dep is always included
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+ // Ensure that runtime_libs dep in included
+ ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+ // Ensure libraries target overridden min_sdk_version value
+ ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_apex31")
+}
+
+func TestMinSdkVersionOverrideToLowerVersionNoOp(t *testing.T) {
+ // Attempt to override from 31 to 29, should be a NOOP
+ minSdkOverride29 := "29"
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ updatable: true,
+ min_sdk_version: "31"
+ }
+
+ override_apex {
+ name: "override_myapex",
+ base: "myapex",
+ logging_parent: "com.foo.bar",
+ package_name: "test.overridden.package"
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ runtime_libs: ["libbar"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ min_sdk_version: "apex_inherit"
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ min_sdk_version: "apex_inherit"
+ }
+
+ `, withApexGlobalMinSdkVersionOverride(&minSdkOverride29))
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that direct non-stubs dep is always included
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+ // Ensure that runtime_libs dep in included
+ ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+ // Ensure libraries target the original min_sdk_version value rather than the overridden
+ ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_apex31")
+}
+
func TestLegacyAndroid10Support(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -6737,7 +7091,7 @@
apex {
name: "myapex",
key: "myapex.key",
- jni_libs: ["mylib"],
+ jni_libs: ["mylib", "libfoo.rust"],
updatable: false,
}
@@ -6763,15 +7117,41 @@
stl: "none",
apex_available: [ "myapex" ],
}
+
+ rust_ffi_shared {
+ name: "libfoo.rust",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ shared_libs: ["libfoo.shared_from_rust"],
+ prefer_rlib: true,
+ apex_available: ["myapex"],
+ }
+
+ cc_library_shared {
+ name: "libfoo.shared_from_rust",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "11", "12"],
+ },
+ }
+
`)
rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
// Notice mylib2.so (transitive dep) is not added as a jni_lib
- ensureEquals(t, rule.Args["opt"], "-a jniLibs mylib.so")
+ ensureEquals(t, rule.Args["opt"], "-a jniLibs libfoo.rust.so mylib.so")
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"lib64/mylib.so",
"lib64/mylib2.so",
+ "lib64/libfoo.rust.so",
+ "lib64/libc++.so", // auto-added to libfoo.rust by Soong
+ "lib64/liblog.so", // auto-added to libfoo.rust by Soong
})
+
+ // b/220397949
+ ensureListContains(t, names(rule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
}
func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
@@ -6826,7 +7206,7 @@
content := bundleConfigRule.Args["content"]
ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
- ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`)
+ ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo@TEST.BUILD_ID/AppFoo.apk"}]}`)
}
func TestAppSetBundle(t *testing.T) {
@@ -6857,9 +7237,9 @@
if len(copyCmds) != 3 {
t.Fatalf("Expected 3 commands, got %d in:\n%s", len(copyCmds), s)
}
- ensureMatches(t, copyCmds[0], "^rm -rf .*/app/AppSet$")
- ensureMatches(t, copyCmds[1], "^mkdir -p .*/app/AppSet$")
- ensureMatches(t, copyCmds[2], "^unzip .*-d .*/app/AppSet .*/AppSet.zip$")
+ ensureMatches(t, copyCmds[0], "^rm -rf .*/app/AppSet@TEST.BUILD_ID$")
+ ensureMatches(t, copyCmds[1], "^mkdir -p .*/app/AppSet@TEST.BUILD_ID$")
+ ensureMatches(t, copyCmds[2], "^unzip .*-d .*/app/AppSet@TEST.BUILD_ID .*/AppSet.zip$")
}
func TestAppSetBundlePrebuilt(t *testing.T) {
@@ -6920,6 +7300,9 @@
apex_available: [
"some-non-updatable-apex",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -6978,6 +7361,9 @@
apex_available: [
"com.android.art.debug",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
apex_key {
@@ -7380,7 +7766,7 @@
})
}
-func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, bootJars []string, rules []android.Rule) {
+func testBootJarPermittedPackagesRules(t *testing.T, errmsg, bp string, bootJars []string, rules []android.Rule) {
t.Helper()
bp += `
apex_key {
@@ -7419,11 +7805,11 @@
func TestApexPermittedPackagesRules(t *testing.T) {
testcases := []struct {
- name string
- expectedError string
- bp string
- bootJars []string
- modulesPackages map[string][]string
+ name string
+ expectedError string
+ bp string
+ bootJars []string
+ bcpPermittedPackages map[string][]string
}{
{
@@ -7437,7 +7823,6 @@
apex_available: ["myapex"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "30",
}
java_library {
name: "nonbcp_lib2",
@@ -7446,25 +7831,23 @@
permitted_packages: ["a.b"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "30",
}
apex {
name: "myapex",
- min_sdk_version: "30",
key: "myapex.key",
java_libs: ["bcp_lib1", "nonbcp_lib2"],
updatable: false,
}`,
bootJars: []string{"bcp_lib1"},
- modulesPackages: map[string][]string{
- "myapex": []string{
+ bcpPermittedPackages: map[string][]string{
+ "bcp_lib1": []string{
"foo.bar",
},
},
},
{
- name: "Bootclasspath apex jar not satisfying allowed module packages on Q.",
- expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
+ name: "Bootclasspath apex jar not satisfying allowed module packages.",
+ expectedError: `(?s)module "bcp_lib2" .* which is restricted because bcp_lib2 bootjar may only use these package prefixes: foo.bar. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
bp: `
java_library {
name: "bcp_lib1",
@@ -7473,7 +7856,6 @@
permitted_packages: ["foo.bar"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "29",
}
java_library {
name: "bcp_lib2",
@@ -7482,102 +7864,67 @@
permitted_packages: ["foo.bar", "bar.baz"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "29",
}
apex {
name: "myapex",
- min_sdk_version: "29",
key: "myapex.key",
java_libs: ["bcp_lib1", "bcp_lib2"],
updatable: false,
}
`,
bootJars: []string{"bcp_lib1", "bcp_lib2"},
- modulesPackages: map[string][]string{
- "myapex": []string{
+ bcpPermittedPackages: map[string][]string{
+ "bcp_lib1": []string{
+ "foo.bar",
+ },
+ "bcp_lib2": []string{
"foo.bar",
},
},
},
{
- name: "Bootclasspath apex jar not satisfying allowed module packages on R.",
- expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
- bp: `
- java_library {
- name: "bcp_lib1",
- srcs: ["lib1/src/*.java"],
- apex_available: ["myapex"],
- permitted_packages: ["foo.bar"],
- sdk_version: "none",
- system_modules: "none",
- min_sdk_version: "30",
- }
- java_library {
- name: "bcp_lib2",
- srcs: ["lib2/src/*.java"],
- apex_available: ["myapex"],
- permitted_packages: ["foo.bar", "bar.baz"],
- sdk_version: "none",
- system_modules: "none",
- min_sdk_version: "30",
- }
- apex {
- name: "myapex",
- min_sdk_version: "30",
- key: "myapex.key",
- java_libs: ["bcp_lib1", "bcp_lib2"],
- updatable: false,
- }
- `,
- bootJars: []string{"bcp_lib1", "bcp_lib2"},
- modulesPackages: map[string][]string{
- "myapex": []string{
- "foo.bar",
- },
- },
- },
- {
- name: "Bootclasspath apex jar >= T not satisfying Q/R/S allowed module packages.",
+ name: "Updateable Bootclasspath apex jar not satisfying allowed module packages.",
expectedError: "",
bp: `
java_library {
- name: "bcp_lib1",
+ name: "bcp_lib_restricted",
srcs: ["lib1/src/*.java"],
apex_available: ["myapex"],
permitted_packages: ["foo.bar"],
sdk_version: "none",
+ min_sdk_version: "29",
system_modules: "none",
- min_sdk_version: "current",
}
java_library {
- name: "bcp_lib2",
+ name: "bcp_lib_unrestricted",
srcs: ["lib2/src/*.java"],
apex_available: ["myapex"],
permitted_packages: ["foo.bar", "bar.baz"],
sdk_version: "none",
+ min_sdk_version: "29",
system_modules: "none",
- min_sdk_version: "current",
}
apex {
name: "myapex",
- min_sdk_version: "current",
key: "myapex.key",
- java_libs: ["bcp_lib1", "bcp_lib2"],
- updatable: false,
+ java_libs: ["bcp_lib_restricted", "bcp_lib_unrestricted"],
+ updatable: true,
+ min_sdk_version: "29",
}
`,
bootJars: []string{"bcp_lib1", "bcp_lib2"},
- modulesPackages: map[string][]string{
- "myapex": []string{
+ bcpPermittedPackages: map[string][]string{
+ "bcp_lib1_non_updateable": []string{
"foo.bar",
},
+ // bcp_lib2_updateable has no entry here since updateable bcp can contain new packages - tracking via an allowlist is not necessary
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
- rules := createApexPermittedPackagesRules(tc.modulesPackages)
- testApexPermittedPackagesRules(t, tc.expectedError, tc.bp, tc.bootJars, rules)
+ rules := createBcpPermittedPackagesRules(tc.bcpPermittedPackages)
+ testBootJarPermittedPackagesRules(t, tc.expectedError, tc.bp, tc.bootJars, rules)
})
}
}
@@ -8360,6 +8707,9 @@
name: "mybootclasspathfragment",
contents: ["mybootclasspathlib"],
apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -8568,6 +8918,86 @@
ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
}
+func TestAndroidMk_RequiredDeps(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+
+ bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+ data := android.AndroidMkDataForTest(t, ctx, bundle)
+ var builder strings.Builder
+ data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += foo")
+
+ flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
+ flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+ flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
+ var flattenedBuilder strings.Builder
+ flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
+ flattenedAndroidMk := flattenedBuilder.String()
+ ensureContains(t, flattenedAndroidMk, "LOCAL_REQUIRED_MODULES += foo")
+}
+
+func TestApexOutputFileProducer(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ ref string
+ expected_data []string
+ }{
+ {
+ name: "test_using_output",
+ ref: ":myapex",
+ expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.capex:myapex.capex"},
+ },
+ {
+ name: "test_using_apex",
+ ref: ":myapex{.apex}",
+ expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.apex:myapex.apex"},
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ compressible: true,
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_test {
+ name: "`+tc.name+`",
+ srcs: ["a.java"],
+ data: ["`+tc.ref+`"],
+ }
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.CompressedApex = proptools.BoolPtr(true)
+ }))
+ javaTest := ctx.ModuleForTests(tc.name, "android_common").Module().(*java.Test)
+ data := android.AndroidMkEntriesForTest(t, ctx, javaTest)[0].EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+ android.AssertStringPathsRelativeToTopEquals(t, "data", ctx.Config(), tc.expected_data, data)
+ })
+ }
+}
+
func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
preparer := android.GroupFixturePreparers(
PrepareForTestWithApexBuildComponents,
@@ -8600,6 +9030,9 @@
name: "mybootclasspathfragment",
contents: ["mybootclasspathlib"],
apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_sdk_library {
@@ -8700,6 +9133,9 @@
name: "mybootclasspathfragment",
contents: ["mybootclasspathlib"],
apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_sdk_library {
@@ -8762,6 +9198,251 @@
}
}
+func TestApexStrictUpdtabilityLint(t *testing.T) {
+ bpTemplate := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["myjavalib"],
+ updatable: %v,
+ min_sdk_version: "29",
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ java_library {
+ name: "myjavalib",
+ srcs: ["MyClass.java"],
+ apex_available: [ "myapex" ],
+ lint: {
+ strict_updatability_linting: %v,
+ },
+ sdk_version: "current",
+ min_sdk_version: "29",
+ }
+ `
+ fs := android.MockFS{
+ "lint-baseline.xml": nil,
+ }
+
+ testCases := []struct {
+ testCaseName string
+ apexUpdatable bool
+ javaStrictUpdtabilityLint bool
+ lintFileExists bool
+ disallowedFlagExpected bool
+ }{
+ {
+ testCaseName: "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
+ apexUpdatable: true,
+ javaStrictUpdtabilityLint: true,
+ lintFileExists: false,
+ disallowedFlagExpected: false,
+ },
+ {
+ testCaseName: "non-updatable apex respects strict_updatability of javalib",
+ apexUpdatable: false,
+ javaStrictUpdtabilityLint: false,
+ lintFileExists: true,
+ disallowedFlagExpected: false,
+ },
+ {
+ testCaseName: "non-updatable apex respects strict updatability of javalib",
+ apexUpdatable: false,
+ javaStrictUpdtabilityLint: true,
+ lintFileExists: true,
+ disallowedFlagExpected: true,
+ },
+ {
+ testCaseName: "updatable apex sets strict updatability of javalib to true",
+ apexUpdatable: true,
+ javaStrictUpdtabilityLint: false, // will be set to true by mutator
+ lintFileExists: true,
+ disallowedFlagExpected: true,
+ },
+ }
+
+ for _, testCase := range testCases {
+ bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint)
+ fixtures := []android.FixturePreparer{}
+ if testCase.lintFileExists {
+ fixtures = append(fixtures, fs.AddToFixture())
+ }
+
+ result := testApex(t, bp, fixtures...)
+ myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
+ disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
+
+ if disallowedFlagActual != testCase.disallowedFlagExpected {
+ t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+ }
+ }
+}
+
+func TestUpdatabilityLintSkipLibcore(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["myjavalib"],
+ updatable: true,
+ min_sdk_version: "29",
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ java_library {
+ name: "myjavalib",
+ srcs: ["MyClass.java"],
+ apex_available: [ "myapex" ],
+ sdk_version: "current",
+ min_sdk_version: "29",
+ }
+ `
+
+ testCases := []struct {
+ testCaseName string
+ moduleDirectory string
+ disallowedFlagExpected bool
+ }{
+ {
+ testCaseName: "lintable module defined outside libcore",
+ moduleDirectory: "",
+ disallowedFlagExpected: true,
+ },
+ {
+ testCaseName: "lintable module defined in libcore root directory",
+ moduleDirectory: "libcore/",
+ disallowedFlagExpected: false,
+ },
+ {
+ testCaseName: "lintable module defined in libcore child directory",
+ moduleDirectory: "libcore/childdir/",
+ disallowedFlagExpected: true,
+ },
+ }
+
+ for _, testCase := range testCases {
+ lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
+ bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
+ result := testApex(t, "", lintFileCreator, bpFileCreator)
+ myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
+ cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
+ disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
+
+ if disallowedFlagActual != testCase.disallowedFlagExpected {
+ t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+ }
+ }
+}
+
+// checks transtive deps of an apex coming from bootclasspath_fragment
+func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: ["mybootclasspathfragment"],
+ updatable: true,
+ min_sdk_version: "29",
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: ["myjavalib"],
+ apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
+ }
+ java_library {
+ name: "myjavalib",
+ srcs: ["MyClass.java"],
+ apex_available: [ "myapex" ],
+ sdk_version: "current",
+ min_sdk_version: "29",
+ compile_dex: true,
+ }
+ `
+ fs := android.MockFS{
+ "lint-baseline.xml": nil,
+ }
+
+ result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
+ myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
+ if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
+ t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
+ }
+}
+
+// updatable apexes should propagate updatable=true to its apps
+func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: %v,
+ apps: [
+ "myapp",
+ ],
+ min_sdk_version: "30",
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ android_app {
+ name: "myapp",
+ updatable: %v,
+ apex_available: [
+ "myapex",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "30",
+ }
+ `
+ testCases := []struct {
+ name string
+ apex_is_updatable_bp bool
+ app_is_updatable_bp bool
+ app_is_updatable_expected bool
+ }{
+ {
+ name: "Non-updatable apex respects updatable property of non-updatable app",
+ apex_is_updatable_bp: false,
+ app_is_updatable_bp: false,
+ app_is_updatable_expected: false,
+ },
+ {
+ name: "Non-updatable apex respects updatable property of updatable app",
+ apex_is_updatable_bp: false,
+ app_is_updatable_bp: true,
+ app_is_updatable_expected: true,
+ },
+ {
+ name: "Updatable apex respects updatable property of updatable app",
+ apex_is_updatable_bp: true,
+ app_is_updatable_bp: true,
+ app_is_updatable_expected: true,
+ },
+ {
+ name: "Updatable apex sets updatable=true on non-updatable app",
+ apex_is_updatable_bp: true,
+ app_is_updatable_bp: false,
+ app_is_updatable_expected: true,
+ },
+ }
+ for _, testCase := range testCases {
+ result := testApex(t, fmt.Sprintf(bp, testCase.apex_is_updatable_bp, testCase.app_is_updatable_bp))
+ myapp := result.ModuleForTests("myapp", "android_common").Module().(*java.AndroidApp)
+ android.AssertBoolEquals(t, testCase.name, testCase.app_is_updatable_expected, myapp.Updatable())
+ }
+}
+
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 8f44fc5..b298dac 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -110,6 +110,9 @@
apex_available: [
"com.android.art",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`,
)
@@ -209,6 +212,9 @@
apex_available: [
"com.android.art",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
bootclasspath_fragment {
@@ -220,6 +226,9 @@
module: "art-bootclasspath-fragment",
},
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`,
)
@@ -361,6 +370,9 @@
apex_available: [
"com.android.art",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`, contentsInsert(contents))
@@ -553,12 +565,66 @@
`prebuilt_com.android.art`,
})
+ // The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
+ ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "mybootclasspathfragment-dexpreopt-arm64-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+ })
+
// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
// locations for the art image.
module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
+ t.Run("boot image files from preferred prebuilt no boot image in apex", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ commonPreparer,
+
+ // Configure some libraries in the art bootclasspath_fragment that match the source
+ // bootclasspath_fragment's contents property.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ addSource("foo", "bar"),
+
+ // Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
+ addPrebuilt(true, "foo", "bar"),
+
+ java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+ ).RunTest(t)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "etc/boot-image.prof",
+ "etc/classpaths/bootclasspath.pb",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "mybootclasspathfragment-dexpreopt-arm64-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+ })
+ })
+
t.Run("source with inconsistency between config and contents", func(t *testing.T) {
android.GroupFixturePreparers(
commonPreparer,
@@ -631,6 +697,7 @@
// Configure some libraries in the art bootclasspath_fragment.
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
)
bp := `
@@ -798,6 +865,9 @@
apex_available: [
"myapex",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`)
@@ -904,6 +974,9 @@
apex_available: [
"com.android.art",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
apex {
@@ -955,6 +1028,9 @@
module: "art-bootclasspath-fragment",
},
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`)
@@ -1068,6 +1144,9 @@
apex_available: [
"com.android.art",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
apex {
@@ -1120,6 +1199,9 @@
module: "art-bootclasspath-fragment",
},
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`)
@@ -1227,6 +1309,9 @@
apex_available: [
"com.android.art",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
apex {
@@ -1279,6 +1364,9 @@
module: "art-bootclasspath-fragment",
},
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
`)
diff --git a/apex/builder.go b/apex/builder.go
index a66e1e0..9119363 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -305,32 +305,6 @@
return output.OutputPath
}
-// buildNoticeFiles creates a buile rule for aggregating notice files from the modules that
-// contributes to this APEX. The notice files are merged into a big notice file.
-func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs {
- var noticeFiles android.Paths
-
- a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
- if externalDep {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
- noticeFiles = append(noticeFiles, to.NoticeFiles()...)
- return true
- })
-
- // TODO(jiyong): why do we need this? WalkPayloadDeps should have already covered this.
- for _, fi := range a.filesInfo {
- noticeFiles = append(noticeFiles, fi.noticeFiles...)
- }
-
- if len(noticeFiles) == 0 {
- return android.NoticeOutputs{}
- }
-
- return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.SortedUniquePaths(noticeFiles))
-}
-
// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of
// files included in this APEX is shown. The text file is dist'ed so that people can see what's
// included in the APEX without actually downloading and extracting it.
@@ -397,6 +371,12 @@
return output.OutputPath
}
+func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
+ return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{
+ TestOnly: true,
+ })
+}
+
// buildUnflattendApex creates build rules to build an APEX using apexer.
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
apexType := a.properties.ApexType
@@ -408,7 +388,7 @@
imageDir := android.PathForModuleOut(ctx, "image"+suffix)
- installSymbolFiles := !ctx.Config().KatiEnabled() || a.ExportedToMake()
+ installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable()
// b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
// APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
@@ -595,8 +575,15 @@
if a.properties.AndroidManifest != nil {
androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
+
+ if a.testApex {
+ androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
+ }
+
implicitInputs = append(implicitInputs, androidManifestFile)
optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+ } else if a.testApex {
+ optFlags = append(optFlags, "--test_only")
}
// Determine target/min sdk version from the context
@@ -629,12 +616,17 @@
optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent)
}
- a.mergedNotices = a.buildNoticeFiles(ctx, a.Name()+suffix)
- if a.mergedNotices.HtmlGzOutput.Valid() {
- // If there's a NOTICE file, embed it as an asset file in the APEX.
- implicitInputs = append(implicitInputs, a.mergedNotices.HtmlGzOutput.Path())
- optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
- }
+ // Create a NOTICE file, and embed it as an asset file in the APEX.
+ a.htmlGzNotice = android.PathForModuleOut(ctx, "NOTICE.html.gz")
+ android.BuildNoticeHtmlOutputFromLicenseMetadata(ctx, a.htmlGzNotice, "", "", unsignedOutputFile.String())
+ noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("cp").
+ Input(a.htmlGzNotice).
+ Output(noticeAssetPath)
+ builder.Build("notice_dir", "Building notice dir")
+ implicitInputs = append(implicitInputs, noticeAssetPath)
+ optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))
if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled {
// Apexes which are supposed to be installed in builtin dirs(/system, etc)
@@ -797,6 +789,9 @@
Implicits: implicits,
Args: args,
})
+ if suffix == imageApexSuffix {
+ a.outputApexFile = signedOutputFile
+ }
a.outputFile = signedOutputFile
if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
@@ -840,6 +835,10 @@
installSuffix = imageCapexSuffix
}
+ if !a.installable() {
+ a.SkipInstall()
+ }
+
// Install to $OUT/soong/{target,host}/.../apex.
a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
a.compatSymlinks.Paths()...)
@@ -995,7 +994,7 @@
return !externalDep
})
- a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, proptools.String(a.properties.Min_sdk_version), depInfos)
+ a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).Raw, depInfos)
ctx.Build(pctx, android.BuildParams{
Rule: android.Phony,
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 60f18bd..9142eed 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -88,6 +88,9 @@
"baz",
"quuz",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -134,6 +137,9 @@
contents: [
"bar",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
diff --git a/apex/key.go b/apex/key.go
index 829410e..9c5bb05 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -230,7 +230,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "apex_key",
- Bzl_load_location: "//build/bazel/rules:apex_key.bzl",
+ Bzl_load_location: "//build/bazel/rules/apex:apex_key.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 06c39ee..4b48da8 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -125,6 +125,7 @@
unsupported_packages: [
"bar-unsupported-packages.txt",
],
+ split_packages: ["*"],
},
}
@@ -274,6 +275,9 @@
"baz",
"quuz",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -317,6 +321,9 @@
name: "my-bootclasspath-fragment",
contents: ["bar"],
apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
apex_key {
@@ -482,6 +489,9 @@
contents: [
"foo", "bar",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
prebuilt_bootclasspath_fragment {
@@ -599,6 +609,9 @@
generate_classpaths_proto: false,
contents: ["foo"],
apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -656,6 +669,9 @@
contents: [
"foo",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
platform_bootclasspath {
@@ -696,6 +712,9 @@
bootclasspath_fragment {
name: "not-in-apex-fragment",
contents: ["foo"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
platform_bootclasspath {
@@ -746,6 +765,9 @@
name: "apex-fragment",
contents: ["foo", "bar"],
apex_available:[ "myapex" ],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
platform_bootclasspath {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 02d8075..187e0df 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -23,7 +23,7 @@
"android/soong/android"
"android/soong/java"
-
+ "android/soong/provenance"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -65,7 +65,8 @@
// Installed locations of symlinks for backward compatibility.
compatSymlinks android.InstallPaths
- hostRequired []string
+ hostRequired []string
+ requiredModuleNames []string
}
type sanitizedPrebuilt interface {
@@ -195,9 +196,19 @@
}
p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
}
- } else if tag == exportedBootclasspathFragmentTag ||
- tag == exportedSystemserverclasspathFragmentTag {
- // Visit the children of the bootclasspath_fragment and systemserver_fragment.
+ } else if tag == exportedBootclasspathFragmentTag {
+ bcpfModule, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
+ if !ok {
+ ctx.PropertyErrorf("exported_bootclasspath_fragments", "%q is not a prebuilt_bootclasspath_fragment module", name)
+ return false
+ }
+ for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
+ p.requiredModuleNames = append(p.requiredModuleNames, makeModuleName)
+ }
+ // Visit the children of the bootclasspath_fragment.
+ return true
+ } else if tag == exportedSystemserverclasspathFragmentTag {
+ // Visit the children of the systemserver_fragment.
return true
}
@@ -211,6 +222,7 @@
entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
}
+ entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
}
func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
@@ -470,6 +482,8 @@
properties PrebuiltProperties
inputApex android.Path
+
+ provenanceMetaDataFile android.OutputPath
}
type ApexFileProperties struct {
@@ -766,9 +780,14 @@
if p.installable() {
p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks.Paths()...)
+ p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
}
}
+func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
+ return p.provenanceMetaDataFile
+}
+
// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
// module. It extracts the correct apex to use and makes it available for use by apex_set.
type prebuiltApexExtractorModule struct {
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 80af2bd..9e7edc7 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -10,11 +10,11 @@
"configurability.go",
"constants.go",
"properties.go",
+ "testing.go",
],
testSrcs: [
"aquery_test.go",
"properties_test.go",
- "testing.go",
],
pluginFor: [
"soong_build",
diff --git a/bazel/aquery.go b/bazel/aquery.go
index fd8cf67..433d502 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -15,26 +15,34 @@
package bazel
import (
+ "crypto/sha256"
+ "encoding/base64"
"encoding/json"
"fmt"
"path/filepath"
+ "reflect"
"regexp"
+ "sort"
"strings"
"github.com/google/blueprint/proptools"
)
+type artifactId int
+type depsetId int
+type pathFragmentId int
+
// artifact contains relevant portions of Bazel's aquery proto, Artifact.
// Represents a single artifact, whether it's a source file or a derived output file.
type artifact struct {
- Id int
- PathFragmentId int
+ Id artifactId
+ PathFragmentId pathFragmentId
}
type pathFragment struct {
- Id int
+ Id pathFragmentId
Label string
- ParentId int
+ ParentId pathFragmentId
}
// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
@@ -43,13 +51,26 @@
Value string
}
+// AqueryDepset is a depset definition from Bazel's aquery response. This is
+// akin to the `depSetOfFiles` in the response proto, except:
+// * direct artifacts are enumerated by full path instead of by ID
+// * it has a hash of the depset contents, instead of an int ID (for determinism)
+// A depset is a data structure for efficient transitive handling of artifact
+// paths. A single depset consists of one or more artifact paths and one or
+// more "child" depsets.
+type AqueryDepset struct {
+ ContentHash string
+ DirectArtifacts []string
+ TransitiveDepSetHashes []string
+}
+
// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
// data structure for storing large numbers of file paths.
type depSetOfFiles struct {
- Id int
- DirectArtifactIds []int
- TransitiveDepSetIds []int
+ Id depsetId
+ DirectArtifactIds []artifactId
+ TransitiveDepSetIds []depsetId
}
// action contains relevant portions of Bazel's aquery proto, Action.
@@ -57,9 +78,9 @@
type action struct {
Arguments []string
EnvironmentVariables []KeyValuePair
- InputDepSetIds []int
+ InputDepSetIds []depsetId
Mnemonic string
- OutputIds []int
+ OutputIds []artifactId
TemplateContent string
Substitutions []KeyValuePair
}
@@ -79,33 +100,38 @@
Command string
Depfile *string
OutputPaths []string
- InputPaths []string
SymlinkPaths []string
Env []KeyValuePair
Mnemonic string
+
+ // Inputs of this build statement, either as unexpanded depsets or expanded
+ // input paths. There should be no overlap between these fields; an input
+ // path should either be included as part of an unexpanded depset or a raw
+ // input path string, but not both.
+ InputDepsetHashes []string
+ InputPaths []string
}
// A helper type for aquery processing which facilitates retrieval of path IDs from their
// less readable Bazel structures (depset and path fragment).
type aqueryArtifactHandler struct {
- // Maps middleman artifact Id to input artifact depset ID.
- // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
- // if we find a middleman action which has outputs [foo, bar], and output [baz_middleman], then,
- // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
- // that action instead.
- middlemanIdToDepsetIds map[int][]int
- // Maps depset Id to depset struct.
- depsetIdToDepset map[int]depSetOfFiles
+ // Maps depset id to AqueryDepset, a representation of depset which is
+ // post-processed for middleman artifact handling, unhandled artifact
+ // dropping, content hashing, etc.
+ depsetIdToAqueryDepset map[depsetId]AqueryDepset
+ // Maps content hash to AqueryDepset.
+ depsetHashToAqueryDepset map[string]AqueryDepset
+
// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
// may be an expensive operation.
- depsetIdToArtifactIdsCache map[int][]int
- // Maps artifact Id to fully expanded path.
- artifactIdToPath map[int]string
+ depsetHashToArtifactPathsCache map[string][]string
+ // Maps artifact ids to fully expanded paths.
+ artifactIdToPath map[artifactId]string
}
// The tokens should be substituted with the value specified here, instead of the
// one returned in 'substitutions' of TemplateExpand action.
-var TemplateActionOverriddenTokens = map[string]string{
+var templateActionOverriddenTokens = map[string]string{
// Uses "python3" for %python_binary% instead of the value returned by aquery
// which is "py3wrapper.sh". See removePy3wrapperScript.
"%python_binary%": "python3",
@@ -115,15 +141,22 @@
var manifestFilePattern = regexp.MustCompile(".*/.+\\.runfiles/MANIFEST$")
// The file name of py3wrapper.sh, which is used by py_binary targets.
-var py3wrapperFileName = "/py3wrapper.sh"
+const py3wrapperFileName = "/py3wrapper.sh"
+
+func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
+ m := map[K]V{}
+ for _, v := range values {
+ m[keyFn(v)] = v
+ }
+ return m
+}
func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
- pathFragments := map[int]pathFragment{}
- for _, pathFragment := range aqueryResult.PathFragments {
- pathFragments[pathFragment.Id] = pathFragment
- }
+ pathFragments := indexBy(aqueryResult.PathFragments, func(pf pathFragment) pathFragmentId {
+ return pf.Id
+ })
- artifactIdToPath := map[int]string{}
+ artifactIdToPath := map[artifactId]string{}
for _, artifact := range aqueryResult.Artifacts {
artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
if err != nil {
@@ -132,13 +165,12 @@
artifactIdToPath[artifact.Id] = artifactPath
}
- depsetIdToDepset := map[int]depSetOfFiles{}
- for _, depset := range aqueryResult.DepSetOfFiles {
- depsetIdToDepset[depset.Id] = depset
- }
-
- // Do a pass through all actions to identify which artifacts are middleman artifacts.
- middlemanIdToDepsetIds := map[int][]int{}
+ // Map middleman artifact ContentHash to input artifact depset ID.
+ // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
+ // if we find a middleman action which has outputs [foo, bar], and output [baz_middleman], then,
+ // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
+ // that action instead.
+ middlemanIdToDepsetIds := map[artifactId][]depsetId{}
for _, actionEntry := range aqueryResult.Actions {
if actionEntry.Mnemonic == "Middleman" {
for _, outputId := range actionEntry.OutputIds {
@@ -146,196 +178,391 @@
}
}
}
- return &aqueryArtifactHandler{
- middlemanIdToDepsetIds: middlemanIdToDepsetIds,
- depsetIdToDepset: depsetIdToDepset,
- depsetIdToArtifactIdsCache: map[int][]int{},
- artifactIdToPath: artifactIdToPath,
- }, nil
-}
-func (a *aqueryArtifactHandler) getInputPaths(depsetIds []int) ([]string, error) {
- inputPaths := []string{}
+ depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d depSetOfFiles) depsetId {
+ return d.Id
+ })
- for _, inputDepSetId := range depsetIds {
- inputArtifacts, err := a.artifactIdsFromDepsetId(inputDepSetId)
+ aqueryHandler := aqueryArtifactHandler{
+ depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
+ depsetHashToAqueryDepset: map[string]AqueryDepset{},
+ depsetHashToArtifactPathsCache: map[string][]string{},
+ artifactIdToPath: artifactIdToPath,
+ }
+
+ // Validate and adjust aqueryResult.DepSetOfFiles values.
+ for _, depset := range aqueryResult.DepSetOfFiles {
+ _, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
if err != nil {
return nil, err
}
- for _, inputId := range inputArtifacts {
- if middlemanInputDepsetIds, isMiddlemanArtifact := a.middlemanIdToDepsetIds[inputId]; isMiddlemanArtifact {
- // Add all inputs from middleman actions which created middleman artifacts which are
- // in the inputs for this action.
- swappedInputPaths, err := a.getInputPaths(middlemanInputDepsetIds)
- if err != nil {
- return nil, err
- }
- inputPaths = append(inputPaths, swappedInputPaths...)
- } else {
- inputPath, exists := a.artifactIdToPath[inputId]
- if !exists {
- return nil, fmt.Errorf("undefined input artifactId %d", inputId)
- }
- inputPaths = append(inputPaths, inputPath)
- }
+ }
+
+ return &aqueryHandler, nil
+}
+
+// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
+// depset.
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
+ if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
+ return aqueryDepset, nil
+ }
+ transitiveDepsetIds := depset.TransitiveDepSetIds
+ var directArtifactPaths []string
+ for _, artifactId := range depset.DirectArtifactIds {
+ path, pathExists := a.artifactIdToPath[artifactId]
+ if !pathExists {
+ return AqueryDepset{}, fmt.Errorf("undefined input artifactId %d", artifactId)
+ }
+ // Filter out any inputs which are universally dropped, and swap middleman
+ // artifacts with their corresponding depsets.
+ if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[artifactId]; isMiddleman {
+ // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
+ transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
+ } else if strings.HasSuffix(path, py3wrapperFileName) || manifestFilePattern.MatchString(path) {
+ // Drop these artifacts.
+ // See go/python-binary-host-mixed-build for more details.
+ // 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
+ // Bazel py_binary targets, so there is no Ninja build statements generated for creating it.
+ // 2) For MANIFEST file, SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
+ // but it doesn't contain sufficient information so no Ninja build statements are generated
+ // for creating it.
+ // So in mixed build mode, when these two are used as input of some Ninja build statement,
+ // since there is no build statement to create them, they should be removed from input paths.
+ // TODO(b/197135294): Clean up this custom runfiles handling logic when
+ // SourceSymlinkManifest and SymlinkTree actions are supported.
+ } else {
+ // TODO(b/216194240): Filter out bazel tools.
+ directArtifactPaths = append(directArtifactPaths, path)
}
}
- // TODO(b/197135294): Clean up this custom runfiles handling logic when
- // SourceSymlinkManifest and SymlinkTree actions are supported.
- filteredInputPaths := filterOutPy3wrapperAndManifestFileFromInputPaths(inputPaths)
-
- return filteredInputPaths, nil
-}
-
-// See go/python-binary-host-mixed-build for more details.
-// 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
-// Bazel py_binary targets, so there is no Ninja build statements generated for creating it.
-// 2) For MANIFEST file, SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
-// but it doesn't contain sufficient information so no Ninja build statements are generated
-// for creating it.
-// So in mixed build mode, when these two are used as input of some Ninja build statement,
-// since there is no build statement to create them, they should be removed from input paths.
-func filterOutPy3wrapperAndManifestFileFromInputPaths(inputPaths []string) []string {
- filteredInputPaths := []string{}
- for _, path := range inputPaths {
- if strings.HasSuffix(path, py3wrapperFileName) || manifestFilePattern.MatchString(path) {
- continue
+ var childDepsetHashes []string
+ for _, childDepsetId := range transitiveDepsetIds {
+ childDepset, exists := depsetIdToDepset[childDepsetId]
+ if !exists {
+ return AqueryDepset{}, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
}
- filteredInputPaths = append(filteredInputPaths, path)
+ childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset)
+ if err != nil {
+ return AqueryDepset{}, err
+ }
+ childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
}
- return filteredInputPaths
+ aqueryDepset := AqueryDepset{
+ ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
+ DirectArtifacts: directArtifactPaths,
+ TransitiveDepSetHashes: childDepsetHashes,
+ }
+ a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
+ a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
+ return aqueryDepset, nil
}
-func (a *aqueryArtifactHandler) artifactIdsFromDepsetId(depsetId int) ([]int, error) {
- if result, exists := a.depsetIdToArtifactIdsCache[depsetId]; exists {
+// getInputPaths flattens the depsets of the given IDs and returns all transitive
+// input paths contained in these depsets.
+// This is a potentially expensive operation, and should not be invoked except
+// for actions which need specialized input handling.
+func (a *aqueryArtifactHandler) getInputPaths(depsetIds []depsetId) ([]string, error) {
+ var inputPaths []string
+
+ for _, inputDepSetId := range depsetIds {
+ depset := a.depsetIdToAqueryDepset[inputDepSetId]
+ inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
+ if err != nil {
+ return nil, err
+ }
+ for _, inputPath := range inputArtifacts {
+ inputPaths = append(inputPaths, inputPath)
+ }
+ }
+
+ return inputPaths, nil
+}
+
+func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
+ if result, exists := a.depsetHashToArtifactPathsCache[depsetHash]; exists {
return result, nil
}
- if depset, exists := a.depsetIdToDepset[depsetId]; exists {
- result := depset.DirectArtifactIds
- for _, childId := range depset.TransitiveDepSetIds {
- childArtifactIds, err := a.artifactIdsFromDepsetId(childId)
+ if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
+ result := depset.DirectArtifacts
+ for _, childHash := range depset.TransitiveDepSetHashes {
+ childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
if err != nil {
return nil, err
}
result = append(result, childArtifactIds...)
}
- a.depsetIdToArtifactIdsCache[depsetId] = result
+ a.depsetHashToArtifactPathsCache[depsetHash] = result
return result, nil
} else {
- return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+ return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
}
}
-// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
-// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
-// aquery invocation).
-func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
- buildStatements := []BuildStatement{}
-
+// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
+// which should be registered (and output to a ninja file) to correspond with Bazel's
+// action graph, as described by the given action graph json proto.
+// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
+// are one-to-one with Bazel's depSetOfFiles objects.
+func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDepset, error) {
var aqueryResult actionGraphContainer
err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
if err != nil {
- return nil, err
+ return nil, nil, err
}
aqueryHandler, err := newAqueryHandler(aqueryResult)
if err != nil {
- return nil, err
+ return nil, nil, err
}
+ var buildStatements []BuildStatement
+
for _, actionEntry := range aqueryResult.Actions {
if shouldSkipAction(actionEntry) {
continue
}
- outputPaths := []string{}
- var depfile *string
- for _, outputId := range actionEntry.OutputIds {
- outputPath, exists := aqueryHandler.artifactIdToPath[outputId]
- if !exists {
- return nil, fmt.Errorf("undefined outputId %d", outputId)
- }
- ext := filepath.Ext(outputPath)
- if ext == ".d" {
- if depfile != nil {
- return nil, fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
- } else {
- depfile = &outputPath
- }
- } else {
- outputPaths = append(outputPaths, outputPath)
- }
- }
- inputPaths, err := aqueryHandler.getInputPaths(actionEntry.InputDepSetIds)
- if err != nil {
- return nil, err
- }
- buildStatement := BuildStatement{
- Command: strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " "),
- Depfile: depfile,
- OutputPaths: outputPaths,
- InputPaths: inputPaths,
- Env: actionEntry.EnvironmentVariables,
- Mnemonic: actionEntry.Mnemonic,
- }
-
+ var buildStatement BuildStatement
if isSymlinkAction(actionEntry) {
- if len(inputPaths) != 1 || len(outputPaths) != 1 {
- return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
- }
- out := outputPaths[0]
- outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
- out = proptools.ShellEscapeIncludingSpaces(out)
- in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
- // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
- buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
- buildStatement.SymlinkPaths = outputPaths[:]
+ buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
} else if isTemplateExpandAction(actionEntry) && len(actionEntry.Arguments) < 1 {
- if len(outputPaths) != 1 {
- return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
- }
- expandedTemplateContent := expandTemplateContent(actionEntry)
- // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
- // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
- // change \n to space and mess up the format of Python programs.
- // sed is used to convert \\n back to \n before saving to output file.
- // See go/python-binary-host-mixed-build for more details.
- command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
- escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
- buildStatement.Command = command
+ buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
} else if isPythonZipperAction(actionEntry) {
- if len(inputPaths) < 1 || len(outputPaths) != 1 {
- return nil, fmt.Errorf("Expect 1+ input and 1 output to python zipper action, got: input %q, output %q", inputPaths, outputPaths)
- }
- buildStatement.InputPaths, buildStatement.Command = removePy3wrapperScript(buildStatement)
- buildStatement.Command = addCommandForPyBinaryRunfilesDir(buildStatement, inputPaths[0], outputPaths[0])
- // Add the python zip file as input of the corresponding python binary stub script in Ninja build statements.
- // In Ninja build statements, the outputs of dependents of a python binary have python binary stub script as input,
- // which is not sufficient without the python zip file from which runfiles directory is created for py_binary.
- //
- // The following logic relies on that Bazel aquery output returns actions in the order that
- // PythonZipper is after TemplateAction of creating Python binary stub script. If later Bazel doesn't return actions
- // in that order, the following logic might not find the build statement generated for Python binary
- // stub script and the build might fail. So the check of pyBinaryFound is added to help debug in case later Bazel might change aquery output.
- // See go/python-binary-host-mixed-build for more details.
- pythonZipFilePath := outputPaths[0]
- pyBinaryFound := false
- for i, _ := range buildStatements {
- if len(buildStatements[i].OutputPaths) == 1 && buildStatements[i].OutputPaths[0]+".zip" == pythonZipFilePath {
- buildStatements[i].InputPaths = append(buildStatements[i].InputPaths, pythonZipFilePath)
- pyBinaryFound = true
- }
- }
- if !pyBinaryFound {
- return nil, fmt.Errorf("Could not find the correspondinging Python binary stub script of PythonZipper: %q", outputPaths)
- }
+ buildStatement, err = aqueryHandler.pythonZipperActionBuildStatement(actionEntry, buildStatements)
} else if len(actionEntry.Arguments) < 1 {
- return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
+ return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
+ } else {
+ buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
+ }
+
+ if err != nil {
+ return nil, nil, err
}
buildStatements = append(buildStatements, buildStatement)
}
- return buildStatements, nil
+ depsetsByHash := map[string]AqueryDepset{}
+ var depsets []AqueryDepset
+ for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
+ if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
+ // Two depsets collide on hash. Ensure that their contents are identical.
+ if !reflect.DeepEqual(aqueryDepset, prevEntry) {
+ return nil, nil, fmt.Errorf("Two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
+ }
+ } else {
+ depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
+ depsets = append(depsets, aqueryDepset)
+ }
+ }
+
+ // Build Statements and depsets must be sorted by their content hash to
+ // preserve determinism between builds (this will result in consistent ninja file
+ // output). Note they are not sorted by their original IDs nor their Bazel ordering,
+ // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
+ sort.Slice(buildStatements, func(i, j int) bool {
+ // For build statements, compare output lists. In Bazel, each output file
+ // may only have one action which generates it, so this will provide
+ // a deterministic ordering.
+ outputs_i := buildStatements[i].OutputPaths
+ outputs_j := buildStatements[j].OutputPaths
+ if len(outputs_i) != len(outputs_j) {
+ return len(outputs_i) < len(outputs_j)
+ }
+ if len(outputs_i) == 0 {
+ // No outputs for these actions, so compare commands.
+ return buildStatements[i].Command < buildStatements[j].Command
+ }
+ // There may be multiple outputs, but the output ordering is deterministic.
+ return outputs_i[0] < outputs_j[0]
+ })
+ sort.Slice(depsets, func(i, j int) bool {
+ return depsets[i].ContentHash < depsets[j].ContentHash
+ })
+ return buildStatements, depsets, nil
+}
+
+// depsetContentHash computes and returns a SHA256 checksum of the contents of
+// the given depset. This content hash may serve as the depset's identifier.
+// Using a content hash for an identifier is superior for determinism. (For example,
+// using an integer identifier which depends on the order in which the depsets are
+// created would result in nondeterministic depset IDs.)
+func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
+ h := sha256.New()
+ // Use newline as delimiter, as paths cannot contain newline.
+ h.Write([]byte(strings.Join(directPaths, "\n")))
+ h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
+ fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
+ return fullHash
+}
+
+func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []depsetId) ([]string, error) {
+ var hashes []string
+ for _, depsetId := range inputDepsetIds {
+ if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
+ return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+ } else {
+ hashes = append(hashes, aqueryDepset.ContentHash)
+ }
+ }
+ return hashes, nil
+}
+
+func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry action) (BuildStatement, error) {
+ command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
+ inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+
+ buildStatement := BuildStatement{
+ Command: command,
+ Depfile: depfile,
+ OutputPaths: outputPaths,
+ InputDepsetHashes: inputDepsetHashes,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ }
+ return buildStatement, nil
+}
+
+func (a *aqueryArtifactHandler) pythonZipperActionBuildStatement(actionEntry action, prevBuildStatements []BuildStatement) (BuildStatement, error) {
+ inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+
+ if len(inputPaths) < 1 || len(outputPaths) != 1 {
+ return BuildStatement{}, fmt.Errorf("Expect 1+ input and 1 output to python zipper action, got: input %q, output %q", inputPaths, outputPaths)
+ }
+ command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
+ inputPaths, command = removePy3wrapperScript(inputPaths, command)
+ command = addCommandForPyBinaryRunfilesDir(command, inputPaths[0], outputPaths[0])
+ // Add the python zip file as input of the corresponding python binary stub script in Ninja build statements.
+ // In Ninja build statements, the outputs of dependents of a python binary have python binary stub script as input,
+ // which is not sufficient without the python zip file from which runfiles directory is created for py_binary.
+ //
+ // The following logic relies on that Bazel aquery output returns actions in the order that
+ // PythonZipper is after TemplateAction of creating Python binary stub script. If later Bazel doesn't return actions
+ // in that order, the following logic might not find the build statement generated for Python binary
+ // stub script and the build might fail. So the check of pyBinaryFound is added to help debug in case later Bazel might change aquery output.
+ // See go/python-binary-host-mixed-build for more details.
+ pythonZipFilePath := outputPaths[0]
+ pyBinaryFound := false
+ for i := range prevBuildStatements {
+ if len(prevBuildStatements[i].OutputPaths) == 1 && prevBuildStatements[i].OutputPaths[0]+".zip" == pythonZipFilePath {
+ prevBuildStatements[i].InputPaths = append(prevBuildStatements[i].InputPaths, pythonZipFilePath)
+ pyBinaryFound = true
+ }
+ }
+ if !pyBinaryFound {
+ return BuildStatement{}, fmt.Errorf("Could not find the correspondinging Python binary stub script of PythonZipper: %q", outputPaths)
+ }
+
+ buildStatement := BuildStatement{
+ Command: command,
+ Depfile: depfile,
+ OutputPaths: outputPaths,
+ InputPaths: inputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ }
+ return buildStatement, nil
+}
+
+func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+ if len(outputPaths) != 1 {
+ return BuildStatement{}, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
+ }
+ expandedTemplateContent := expandTemplateContent(actionEntry)
+ // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
+ // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
+ // change \n to space and mess up the format of Python programs.
+ // sed is used to convert \\n back to \n before saving to output file.
+ // See go/python-binary-host-mixed-build for more details.
+ command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
+ escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
+ inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+
+ buildStatement := BuildStatement{
+ Command: command,
+ Depfile: depfile,
+ OutputPaths: outputPaths,
+ InputDepsetHashes: inputDepsetHashes,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ }
+ return buildStatement, nil
+}
+
+func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+
+ inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
+ if err != nil {
+ return BuildStatement{}, err
+ }
+ if len(inputPaths) != 1 || len(outputPaths) != 1 {
+ return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+ }
+ out := outputPaths[0]
+ outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
+ out = proptools.ShellEscapeIncludingSpaces(out)
+ in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
+ // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
+ command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
+ symlinkPaths := outputPaths[:]
+
+ buildStatement := BuildStatement{
+ Command: command,
+ Depfile: depfile,
+ OutputPaths: outputPaths,
+ InputPaths: inputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ SymlinkPaths: symlinkPaths,
+ }
+ return buildStatement, nil
+}
+
+func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths []string, depfile *string, err error) {
+ for _, outputId := range actionEntry.OutputIds {
+ outputPath, exists := a.artifactIdToPath[outputId]
+ if !exists {
+ err = fmt.Errorf("undefined outputId %d", outputId)
+ return
+ }
+ ext := filepath.Ext(outputPath)
+ if ext == ".d" {
+ if depfile != nil {
+ err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
+ return
+ } else {
+ depfile = &outputPath
+ }
+ } else {
+ outputPaths = append(outputPaths, outputPath)
+ }
+ }
+ return
}
// expandTemplateContent substitutes the tokens in a template.
@@ -343,7 +570,7 @@
replacerString := []string{}
for _, pair := range actionEntry.Substitutions {
value := pair.Value
- if val, ok := TemplateActionOverriddenTokens[pair.Key]; ok {
+ if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
value = val
}
replacerString = append(replacerString, pair.Key, value)
@@ -372,10 +599,10 @@
// removed from input paths and command of creating python zip file.
// See go/python-binary-host-mixed-build for more details.
// TODO(b/205879240) remove this after py3wrapper.sh could be created in the mixed build mode.
-func removePy3wrapperScript(bs BuildStatement) (newInputPaths []string, newCommand string) {
+func removePy3wrapperScript(inputPaths []string, command string) (newInputPaths []string, newCommand string) {
// Remove from inputs
filteredInputPaths := []string{}
- for _, path := range bs.InputPaths {
+ for _, path := range inputPaths {
if !strings.HasSuffix(path, py3wrapperFileName) {
filteredInputPaths = append(filteredInputPaths, path)
}
@@ -384,7 +611,7 @@
// Remove from command line
var re = regexp.MustCompile(`\S*` + py3wrapperFileName)
- newCommand = re.ReplaceAllString(bs.Command, "")
+ newCommand = re.ReplaceAllString(command, "")
return
}
@@ -395,18 +622,18 @@
// so MANIFEST file could not be created, which also blocks the creation of runfiles directory.
// See go/python-binary-host-mixed-build for more details.
// TODO(b/197135294) create runfiles directory from MANIFEST file once it can be created from SourceSymlinkManifest action.
-func addCommandForPyBinaryRunfilesDir(bs BuildStatement, zipperCommandPath, zipFilePath string) string {
+func addCommandForPyBinaryRunfilesDir(oldCommand string, zipperCommandPath, zipFilePath string) string {
// Unzip the zip file, zipFilePath looks like <python_binary>.zip
runfilesDirName := zipFilePath[0:len(zipFilePath)-4] + ".runfiles"
command := fmt.Sprintf("%s x %s -d %s", zipperCommandPath, zipFilePath, runfilesDirName)
// Create a symbolic link in <python_binary>.runfiles/, which is the expected structure
// when running the python binary stub script.
command += fmt.Sprintf(" && ln -sf runfiles/__main__ %s", runfilesDirName)
- return bs.Command + " && " + command
+ return oldCommand + " && " + command
}
func isSymlinkAction(a action) bool {
- return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
+ return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
}
func isTemplateExpandAction(a action) bool {
@@ -440,8 +667,8 @@
return false
}
-func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
- labels := []string{}
+func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]pathFragment) (string, error) {
+ var labels []string
currId := id
// Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
for currId > 0 {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 68e50c2..c9c8909 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -223,7 +223,7 @@
"parentId": 13
}]
}`
- actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
+ actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
expectedBuildStatements := []BuildStatement{}
for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
expectedBuildStatements = append(expectedBuildStatements,
@@ -234,11 +234,6 @@
OutputPaths: []string{
fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
},
- InputPaths: []string{
- "../sourceroot/bionic/libc/SYSCALLS.TXT",
- "../sourceroot/bionic/libc/tools/gensyscalls.py",
- "../bazel_tools/tools/genrule/genrule-setup.sh",
- },
Env: []KeyValuePair{
KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
},
@@ -246,6 +241,19 @@
})
}
assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+
+ expectedFlattenedInputs := []string{
+ "../sourceroot/bionic/libc/SYSCALLS.TXT",
+ "../sourceroot/bionic/libc/tools/gensyscalls.py",
+ "../bazel_tools/tools/genrule/genrule-setup.sh",
+ }
+ // In this example, each depset should have the same expected inputs.
+ for _, actualDepset := range actualDepsets {
+ actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
+ if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
+ t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
+ }
+ }
}
func TestInvalidOutputId(t *testing.T) {
@@ -280,11 +288,11 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, "undefined outputId 3")
}
-func TestInvalidInputDepsetId(t *testing.T) {
+func TestInvalidInputDepsetIdFromAction(t *testing.T) {
const inputString = `
{
"artifacts": [{
@@ -316,10 +324,47 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, "undefined input depsetId 2")
}
+func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2],
+ "transitiveDepSetIds": [42]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
+}
+
func TestInvalidInputArtifactId(t *testing.T) {
const inputString = `
{
@@ -352,7 +397,7 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, "undefined input artifactId 3")
}
@@ -389,7 +434,7 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, "undefined path fragment id 3")
}
@@ -431,7 +476,7 @@
}]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ actual, _, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -492,7 +537,7 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
}
@@ -699,23 +744,31 @@
}]
}`
- actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
- // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
- // are given via a deep depset, but the depset is flattened when returned as a
- // BuildStatement slice.
- inputPaths := []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root"}
- for i := 1; i < 20; i++ {
- inputPaths = append(inputPaths, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
- }
+ actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
+
expectedBuildStatements := []BuildStatement{
BuildStatement{
Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
- InputPaths: inputPaths,
Mnemonic: "Action",
},
}
assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+
+ // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
+ // are given via a deep depset, but the depset is flattened when returned as a
+ // BuildStatement slice.
+ expectedFlattenedInputs := []string{}
+ for i := 1; i < 20; i++ {
+ expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
+ }
+ expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
+
+ actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
+ actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
+ if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
+ t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
+ }
}
func TestMiddlemenAction(t *testing.T) {
@@ -785,24 +838,73 @@
}]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ actualBuildStatements, actualDepsets, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
}
- if expected := 1; len(actual) != expected {
- t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
+ if expected := 1; len(actualBuildStatements) != expected {
+ t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
}
- bs := actual[0]
- expectedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
- if !reflect.DeepEqual(bs.InputPaths, expectedInputs) {
- t.Errorf("Expected main action inputs %q, but got %q", expectedInputs, bs.InputPaths)
+ expectedDepsetFiles := [][]string{
+ {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
+ {"middleinput_one", "middleinput_two"},
+ }
+ assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
+
+ bs := actualBuildStatements[0]
+ if len(bs.InputPaths) > 0 {
+ t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
}
expectedOutputs := []string{"output"}
if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
}
+
+ expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
+ actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
+
+ if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
+ t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
+ }
+}
+
+// Returns the contents of given depsets in concatenated post order.
+func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
+ depsetsByHash := map[string]AqueryDepset{}
+ for _, depset := range allDepsets {
+ depsetsByHash[depset.ContentHash] = depset
+ }
+ result := []string{}
+ for _, depsetId := range depsetHashesToFlatten {
+ result = append(result, flattenDepset(depsetId, depsetsByHash)...)
+ }
+ return result
+}
+
+// Returns the contents of a given depset in post order.
+func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
+ depset := allDepsets[depsetHashToFlatten]
+ result := []string{}
+ for _, depsetId := range depset.TransitiveDepSetHashes {
+ result = append(result, flattenDepset(depsetId, allDepsets)...)
+ }
+ result = append(result, depset.DirectArtifacts...)
+ return result
+}
+
+func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
+ t.Helper()
+ if len(actualDepsets) != len(expectedDepsetFiles) {
+ t.Errorf("Expected %s depsets, but got %s depsets", expectedDepsetFiles, actualDepsets)
+ }
+ for i, actualDepset := range actualDepsets {
+ actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
+ if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
+ t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
+ }
+ }
}
func TestSimpleSymlink(t *testing.T) {
@@ -849,7 +951,7 @@
}]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ actual, _, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
@@ -913,7 +1015,7 @@
}]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ actual, _, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
@@ -970,7 +1072,7 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
}
@@ -1011,7 +1113,7 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
}
@@ -1045,7 +1147,7 @@
}]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ actual, _, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
@@ -1091,7 +1193,7 @@
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `Expect 1 output to template expand action, got: output []`)
}
@@ -1211,7 +1313,7 @@
"label": "python_binary"
}]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ actual, _, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
@@ -1264,7 +1366,7 @@
"label": "python_binary.zip"
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
}
@@ -1360,7 +1462,7 @@
"parentId": 11
}]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ _, _, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["../bazel_tools/tools/zip/zipper/zipper" "python_binary.py"], output []`)
}
diff --git a/bazel/constants.go b/bazel/constants.go
index 6beb496..b10f256 100644
--- a/bazel/constants.go
+++ b/bazel/constants.go
@@ -21,7 +21,7 @@
SoongInjectionDirName = "soong_injection"
- GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT"
+ GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT."
)
// String returns the name of the run.
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 4dc0e4c..5d00b0b 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -114,7 +114,7 @@
rootStaticArchives = []
linker_inputs = cc_info.linking_context.linker_inputs.to_list()
-static_info_tag = "//build/bazel/rules:cc_library_static.bzl%CcStaticLibraryInfo"
+static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo"
if static_info_tag in providers(target):
static_info = providers(target)[static_info_tag]
ccObjectFiles = [f.path for f in static_info.objects]
@@ -149,7 +149,7 @@
rootSharedLibraries.append(path)
toc_file = ""
-toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo"
if toc_file_tag in providers(target):
toc_file = providers(target)[toc_file_tag].toc.path
else:
diff --git a/bazel/properties.go b/bazel/properties.go
index 1300a53..e29b9e1 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -65,6 +65,14 @@
Excludes []Label
}
+// MakeLabelList creates a LabelList from a list Label
+func MakeLabelList(labels []Label) LabelList {
+ return LabelList{
+ Includes: labels,
+ Excludes: nil,
+ }
+}
+
func (ll *LabelList) Equals(other LabelList) bool {
if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
return false
@@ -354,6 +362,15 @@
return keys
}
+// MakeLabelAttribute turns a string into a LabelAttribute
+func MakeLabelAttribute(label string) *LabelAttribute {
+ return &LabelAttribute{
+ Value: &Label{
+ Label: label,
+ },
+ }
+}
+
type configToBools map[string]bool
func (ctb configToBools) setValue(config string, value *bool) {
@@ -392,6 +409,11 @@
return false
}
+// SetValue sets value for the no config axis
+func (ba *BoolAttribute) SetValue(value *bool) {
+ ba.SetSelectValue(NoConfigAxis, "", value)
+}
+
// SetSelectValue sets value for the given axis/config.
func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
axis.validateConfig(config)
@@ -635,6 +657,11 @@
}
}
+// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
+func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
+ return MakeLabelListAttribute(MakeLabelList([]Label{value}))
+}
+
func (lla *LabelListAttribute) SetValue(list LabelList) {
lla.SetSelectValue(NoConfigAxis, "", list)
}
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index c7f9776..7b76b74 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -329,7 +329,7 @@
func TestPartitionLabelListAttribute(t *testing.T) {
testCases := []struct {
name string
- ctx *otherModuleTestContext
+ ctx *OtherModuleTestContext
labelList LabelListAttribute
filters LabelPartitions
expected PartitionToLabelListAttribute
@@ -337,7 +337,7 @@
}{
{
name: "no configurable values",
- ctx: &otherModuleTestContext{},
+ ctx: &OtherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
},
@@ -354,7 +354,7 @@
},
{
name: "no configurable values, remainder partition",
- ctx: &otherModuleTestContext{},
+ ctx: &OtherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
},
@@ -371,7 +371,7 @@
},
{
name: "no configurable values, empty partition",
- ctx: &otherModuleTestContext{},
+ ctx: &OtherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
},
@@ -387,8 +387,8 @@
},
{
name: "no configurable values, has map",
- ctx: &otherModuleTestContext{
- modules: []testModuleInfo{testModuleInfo{name: "srcs", typ: "fg", dir: "dir"}},
+ ctx: &OtherModuleTestContext{
+ Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}},
},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
@@ -406,7 +406,7 @@
},
{
name: "configurable values, keeps empty if excludes",
- ctx: &otherModuleTestContext{},
+ ctx: &OtherModuleTestContext{},
labelList: LabelListAttribute{
ConfigurableValues: configurableLabelLists{
ArchConfigurationAxis: labelListSelectValues{
@@ -450,7 +450,7 @@
},
{
name: "error for multiple partitions same value",
- ctx: &otherModuleTestContext{},
+ ctx: &OtherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
},
diff --git a/bazel/testing.go b/bazel/testing.go
index 23c8350..9a43b61 100644
--- a/bazel/testing.go
+++ b/bazel/testing.go
@@ -20,86 +20,86 @@
"github.com/google/blueprint"
)
-// testModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
+// TestModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
// a blueprint ModuleContext
-type testModuleInfo struct {
- name string
- typ string
- dir string
+type TestModuleInfo struct {
+ ModuleName string
+ Typ string
+ Dir string
}
// Name returns name for testModuleInfo -- required to implement blueprint.Module
-func (mi testModuleInfo) Name() string {
- return mi.name
+func (mi TestModuleInfo) Name() string {
+ return mi.ModuleName
}
// GenerateBuildActions unused, but required to implmeent blueprint.Module
-func (mi testModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
+func (mi TestModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
-func (mi testModuleInfo) equals(other testModuleInfo) bool {
- return mi.name == other.name && mi.typ == other.typ && mi.dir == other.dir
+func (mi TestModuleInfo) equals(other TestModuleInfo) bool {
+ return mi.ModuleName == other.ModuleName && mi.Typ == other.Typ && mi.Dir == other.Dir
}
// ensure testModuleInfo implements blueprint.Module
-var _ blueprint.Module = testModuleInfo{}
+var _ blueprint.Module = TestModuleInfo{}
-// otherModuleTestContext is a mock context that implements OtherModuleContext
-type otherModuleTestContext struct {
- modules []testModuleInfo
+// OtherModuleTestContext is a mock context that implements OtherModuleContext
+type OtherModuleTestContext struct {
+ Modules []TestModuleInfo
errors []string
}
// ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists
-func (omc *otherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
- for _, m := range omc.modules {
- if m.name == name {
+func (omc *OtherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
+ for _, m := range omc.Modules {
+ if m.ModuleName == name {
return m, true
}
}
- return testModuleInfo{}, false
+ return TestModuleInfo{}, false
}
// testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc
-func (omc *otherModuleTestContext) testModuleInfo(m blueprint.Module) (testModuleInfo, bool) {
- mi, ok := m.(testModuleInfo)
+func (omc *OtherModuleTestContext) testModuleInfo(m blueprint.Module) (TestModuleInfo, bool) {
+ mi, ok := m.(TestModuleInfo)
if !ok {
- return testModuleInfo{}, false
+ return TestModuleInfo{}, false
}
- for _, other := range omc.modules {
+ for _, other := range omc.Modules {
if other.equals(mi) {
return mi, true
}
}
- return testModuleInfo{}, false
+ return TestModuleInfo{}, false
}
// OtherModuleType returns type of m if it exists in omc
-func (omc *otherModuleTestContext) OtherModuleType(m blueprint.Module) string {
+func (omc *OtherModuleTestContext) OtherModuleType(m blueprint.Module) string {
if mi, ok := omc.testModuleInfo(m); ok {
- return mi.typ
+ return mi.Typ
}
return ""
}
// OtherModuleName returns name of m if it exists in omc
-func (omc *otherModuleTestContext) OtherModuleName(m blueprint.Module) string {
+func (omc *OtherModuleTestContext) OtherModuleName(m blueprint.Module) string {
if mi, ok := omc.testModuleInfo(m); ok {
- return mi.name
+ return mi.ModuleName
}
return ""
}
// OtherModuleDir returns dir of m if it exists in omc
-func (omc *otherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
+func (omc *OtherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
if mi, ok := omc.testModuleInfo(m); ok {
- return mi.dir
+ return mi.Dir
}
return ""
}
-func (omc *otherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
+func (omc *OtherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
omc.errors = append(omc.errors, fmt.Sprintf(format, args...))
}
// Ensure otherModuleTestContext implements OtherModuleContext
-var _ OtherModuleContext = &otherModuleTestContext{}
+var _ OtherModuleContext = &OtherModuleTestContext{}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 4bcfa61..e0ce194 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -18,6 +18,7 @@
],
deps: [
"soong-android",
+ "soong-android-allowlists",
"soong-android-soongconfig",
"soong-shared",
"soong-apex",
@@ -28,6 +29,7 @@
"soong-genrule",
"soong-python",
"soong-sh",
+ "soong-starlark-format",
"soong-ui-metrics",
],
testSrcs: [
@@ -43,9 +45,18 @@
"cc_library_shared_conversion_test.go",
"cc_library_static_conversion_test.go",
"cc_object_conversion_test.go",
+ "cc_prebuilt_library_conversion_test.go",
+ "cc_prebuilt_library_shared_test.go",
+ "cc_prebuilt_library_static_test.go",
"conversion_test.go",
"filegroup_conversion_test.go",
"genrule_conversion_test.go",
+ "java_binary_host_conversion_test.go",
+ "java_import_conversion_test.go",
+ "java_library_conversion_test.go",
+ "java_library_host_conversion_test.go",
+ "java_plugin_conversion_test.go",
+ "java_proto_conversion_test.go",
"performance_test.go",
"prebuilt_etc_conversion_test.go",
"python_binary_conversion_test.go",
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
index 035a352..173b4e4 100644
--- a/bp2build/android_app_certificate_conversion_test.go
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -42,7 +42,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("android_app_certificate", "com.android.apogee.cert", attrNameToString{
+ makeBazelTargetNoRestrictions("android_app_certificate", "com.android.apogee.cert", attrNameToString{
"certificate": `"chamber_of_secrets_dir"`,
}),
}})
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 28de06c..a216c9d 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -74,7 +74,8 @@
package_name: "com.google",
resource_dirs: ["resa", "resb"],
manifest: "manifest/AndroidManifest.xml",
- static_libs: ["static_lib_dep"]
+ static_libs: ["static_lib_dep"],
+ java_version: "7",
}
`,
expectedBazelTargets: []string{
@@ -87,6 +88,45 @@
]`,
"custom_package": `"com.google"`,
"deps": `[":static_lib_dep"]`,
+ "javacopts": `["-source 1.7 -target 1.7"]`,
+ }),
+ }})
+}
+
+func TestAndroidAppArchVariantSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, bp2buildTestCase{
+ description: "Android app - arch variant srcs",
+ moduleTypeUnderTest: "android_app",
+ moduleTypeUnderTestFactory: java.AndroidAppFactory,
+ filesystem: map[string]string{
+ "arm.java": "",
+ "x86.java": "",
+ "res/res.png": "",
+ "AndroidManifest.xml": "",
+ },
+ blueprint: `
+android_app {
+ name: "TestApp",
+ sdk_version: "current",
+ arch: {
+ arm: {
+ srcs: ["arm.java"],
+ },
+ x86: {
+ srcs: ["x86.java"],
+ }
+ }
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("android_binary", "TestApp", attrNameToString{
+ "srcs": `select({
+ "//build/bazel/platforms/arch:arm": ["arm.java"],
+ "//build/bazel/platforms/arch:x86": ["x86.java"],
+ "//conditions:default": [],
+ })`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
}),
}})
}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 9057189..3f1349f 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -41,9 +41,27 @@
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
}
+func runOverrideApexTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ runBp2BuildTestCase(t, registerOverrideApexModuleTypes, tc)
+}
+
+func registerOverrideApexModuleTypes(ctx android.RegistrationContext) {
+ // CC module types needed as they can be APEX dependencies
+ cc.RegisterCCBuildComponents(ctx)
+
+ ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
+ ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
+ ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+ ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
+ ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("apex", apex.BundleFactory)
+}
+
func TestApexBundleSimple(t *testing.T) {
runApexTestCase(t, bp2buildTestCase{
- description: "apex - example with all props",
+ description: "apex - example with all props, file_context is a module in same Android.bp",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
filesystem: map[string]string{},
@@ -71,13 +89,11 @@
bazel_module: { bp2build_available: false },
}
-// TODO(b/194878861): Add bp2build support for prebuilt_etc
cc_library {
name: "pretend_prebuilt_1",
bazel_module: { bp2build_available: false },
}
-// TODO(b/194878861): Add bp2build support for prebuilt_etc
cc_library {
name: "pretend_prebuilt_2",
bazel_module: { bp2build_available: false },
@@ -86,7 +102,7 @@
filegroup {
name: "com.android.apogee-file_contexts",
srcs: [
- "com.android.apogee-file_contexts",
+ "com.android.apogee-file_contexts",
],
bazel_module: { bp2build_available: false },
}
@@ -98,7 +114,7 @@
name: "com.android.apogee",
manifest: "apogee_manifest.json",
androidManifest: "ApogeeAndroidManifest.xml",
- file_contexts: "com.android.apogee-file_contexts",
+ file_contexts: ":com.android.apogee-file_contexts",
min_sdk_version: "29",
key: "com.android.apogee.key",
certificate: "com.android.apogee.certificate",
@@ -117,6 +133,7 @@
"pretend_prebuilt_1",
"pretend_prebuilt_2",
],
+ package_name: "com.android.apogee.test.package",
}
`,
expectedBazelTargets: []string{
@@ -153,6 +170,86 @@
]`,
"updatable": "False",
"compressible": "False",
+ "package_name": `"com.android.apogee.test.package"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_fileContextsInAnotherAndroidBp(t *testing.T) {
+ runApexTestCase(t, bp2buildTestCase{
+ description: "apex - file contexts is a module in another Android.bp",
+ moduleTypeUnderTest: "apex",
+ moduleTypeUnderTestFactory: apex.BundleFactory,
+ filesystem: map[string]string{
+ "a/b/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: `
+apex {
+ name: "com.android.apogee",
+ file_contexts: ":com.android.apogee-file_contexts",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ "file_contexts": `"//a/b:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_fileContextsIsFile(t *testing.T) {
+ runApexTestCase(t, bp2buildTestCase{
+ description: "apex - file contexts is a file",
+ moduleTypeUnderTest: "apex",
+ moduleTypeUnderTestFactory: apex.BundleFactory,
+ filesystem: map[string]string{},
+ blueprint: `
+apex {
+ name: "com.android.apogee",
+ file_contexts: "file_contexts_file",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ "file_contexts": `"file_contexts_file"`,
+ "manifest": `"apex_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_fileContextsIsNotSpecified(t *testing.T) {
+ runApexTestCase(t, bp2buildTestCase{
+ description: "apex - file contexts is not specified",
+ moduleTypeUnderTest: "apex",
+ moduleTypeUnderTestFactory: apex.BundleFactory,
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: `
+apex {
+ name: "com.android.apogee",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
@@ -162,8 +259,16 @@
description: "apex - example with compile_multilib=both",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("both"),
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: createMultilibBlueprint("both"),
expectedBazelTargets: []string{
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
"native_shared_libs_32": `[
@@ -187,6 +292,8 @@
],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
@@ -196,8 +303,16 @@
description: "apex - example with compile_multilib=first",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("first"),
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: createMultilibBlueprint("first"),
expectedBazelTargets: []string{
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
"native_shared_libs_32": `select({
@@ -226,6 +341,8 @@
],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
@@ -235,8 +352,16 @@
description: "apex - example with compile_multilib=32",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("32"),
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: createMultilibBlueprint("32"),
expectedBazelTargets: []string{
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
"native_shared_libs_32": `[
@@ -247,6 +372,8 @@
"//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
@@ -256,8 +383,16 @@
description: "apex - example with compile_multilib=64",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("64"),
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: createMultilibBlueprint("64"),
expectedBazelTargets: []string{
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
"native_shared_libs_64": `select({
@@ -273,6 +408,8 @@
],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
@@ -282,7 +419,15 @@
description: "apex - default property values",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
blueprint: `
apex {
name: "com.android.apogee",
@@ -290,7 +435,8 @@
}
`,
expectedBazelTargets: []string{makeBazelTarget("apex", "com.android.apogee", attrNameToString{
- "manifest": `"apogee_manifest.json"`,
+ "manifest": `"apogee_manifest.json"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
}),
}})
}
@@ -300,7 +446,15 @@
description: "apex - has bazel module props",
moduleTypeUnderTest: "apex",
moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
blueprint: `
apex {
name: "apogee",
@@ -309,7 +463,8 @@
}
`,
expectedBazelTargets: []string{makeBazelTarget("apex", "apogee", attrNameToString{
- "manifest": `"manifest.json"`,
+ "manifest": `"manifest.json"`,
+ "file_contexts": `"//system/sepolicy/apex:apogee-file_contexts"`,
}),
}})
}
@@ -363,3 +518,303 @@
},
}`
}
+
+func TestBp2BuildOverrideApex(t *testing.T) {
+ runOverrideApexTestCase(t, bp2buildTestCase{
+ description: "override_apex",
+ moduleTypeUnderTest: "override_apex",
+ moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+ filesystem: map[string]string{},
+ blueprint: `
+apex_key {
+ name: "com.android.apogee.key",
+ public_key: "com.android.apogee.avbpubkey",
+ private_key: "com.android.apogee.pem",
+ bazel_module: { bp2build_available: false },
+}
+
+android_app_certificate {
+ name: "com.android.apogee.certificate",
+ certificate: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_1",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_2",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "pretend_prebuilt_1",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "pretend_prebuilt_2",
+ bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+
+cc_binary { name: "cc_binary_1", bazel_module: { bp2build_available: false } }
+sh_binary { name: "sh_binary_2", bazel_module: { bp2build_available: false } }
+
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ androidManifest: "ApogeeAndroidManifest.xml",
+ file_contexts: ":com.android.apogee-file_contexts",
+ min_sdk_version: "29",
+ key: "com.android.apogee.key",
+ certificate: "com.android.apogee.certificate",
+ updatable: false,
+ installable: false,
+ compressible: false,
+ native_shared_libs: [
+ "native_shared_lib_1",
+ "native_shared_lib_2",
+ ],
+ binaries: [
+ "cc_binary_1",
+ "sh_binary_2",
+ ],
+ prebuilts: [
+ "pretend_prebuilt_1",
+ "pretend_prebuilt_2",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+
+apex_key {
+ name: "com.google.android.apogee.key",
+ public_key: "com.google.android.apogee.avbpubkey",
+ private_key: "com.google.android.apogee.pem",
+ bazel_module: { bp2build_available: false },
+}
+
+android_app_certificate {
+ name: "com.google.android.apogee.certificate",
+ certificate: "com.google.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ key: "com.google.android.apogee.key",
+ certificate: "com.google.android.apogee.certificate",
+ prebuilts: [],
+ compressible: true,
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+ "android_manifest": `"ApogeeAndroidManifest.xml"`,
+ "binaries": `[
+ ":cc_binary_1",
+ ":sh_binary_2",
+ ]`,
+ "certificate": `":com.google.android.apogee.certificate"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "installable": "False",
+ "key": `":com.google.android.apogee.key"`,
+ "manifest": `"apogee_manifest.json"`,
+ "min_sdk_version": `"29"`,
+ "native_shared_libs_32": `[
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ]`,
+ "native_shared_libs_64": `select({
+ "//build/bazel/platforms/arch:arm64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
+ "prebuilts": `[]`,
+ "updatable": "False",
+ "compressible": "True",
+ }),
+ }})
+}
+
+func TestApexBundleSimple_manifestIsEmpty_baseApexOverrideApexInDifferentAndroidBp(t *testing.T) {
+ runOverrideApexTestCase(t, bp2buildTestCase{
+ description: "override_apex - manifest of base apex is empty, base apex and override_apex is in different Android.bp",
+ moduleTypeUnderTest: "override_apex",
+ moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}`,
+ "a/b/Android.bp": `
+apex {
+ name: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: `
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"//a/b:apex_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_manifestIsSet_baseApexOverrideApexInDifferentAndroidBp(t *testing.T) {
+ runOverrideApexTestCase(t, bp2buildTestCase{
+ description: "override_apex - manifest of base apex is set, base apex and override_apex is in different Android.bp",
+ moduleTypeUnderTest: "override_apex",
+ moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}`,
+ "a/b/Android.bp": `
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ bazel_module: { bp2build_available: false },
+}
+`,
+ },
+ blueprint: `
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"//a/b:apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_manifestIsEmpty_baseApexOverrideApexInSameAndroidBp(t *testing.T) {
+ runOverrideApexTestCase(t, bp2buildTestCase{
+ description: "override_apex - manifest of base apex is empty, base apex and override_apex is in same Android.bp",
+ moduleTypeUnderTest: "override_apex",
+ moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}`,
+ },
+ blueprint: `
+apex {
+ name: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_manifestIsSet_baseApexOverrideApexInSameAndroidBp(t *testing.T) {
+ runOverrideApexTestCase(t, bp2buildTestCase{
+ description: "override_apex - manifest of base apex is set, base apex and override_apex is in same Android.bp",
+ moduleTypeUnderTest: "override_apex",
+ moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}`,
+ },
+ blueprint: `
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_packageNameOverride(t *testing.T) {
+ runOverrideApexTestCase(t, bp2buildTestCase{
+ description: "override_apex - override package name",
+ moduleTypeUnderTest: "override_apex",
+ moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+ filesystem: map[string]string{
+ "system/sepolicy/apex/Android.bp": `
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [ "apogee-file_contexts", ],
+ bazel_module: { bp2build_available: false },
+}`,
+ },
+ blueprint: `
+apex {
+ name: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ package_name: "com.google.android.apogee",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "package_name": `"com.google.android.apogee"`,
+ }),
+ }})
+}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 1d949901..dfa96a2 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -42,7 +42,7 @@
private_key: "com.android.apogee.pem",
}
`,
- expectedBazelTargets: []string{makeBazelTarget("apex_key", "com.android.apogee.key", attrNameToString{
+ expectedBazelTargets: []string{makeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", attrNameToString{
"private_key": `"com.android.apogee.pem"`,
"public_key": `"com.android.apogee.avbpubkey"`,
}),
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index b3bec65..a96a3fc 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -27,6 +27,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -559,48 +560,29 @@
return "", nil
}
- var ret string
switch propertyValue.Kind() {
case reflect.String:
- ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+ return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
case reflect.Bool:
- ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+ return starlark_fmt.PrintBool(propertyValue.Bool()), nil
case reflect.Int, reflect.Uint, reflect.Int64:
- ret = fmt.Sprintf("%v", propertyValue.Interface())
+ return fmt.Sprintf("%v", propertyValue.Interface()), nil
case reflect.Ptr:
return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
case reflect.Slice:
- if propertyValue.Len() == 0 {
- return "[]", nil
- }
-
- if propertyValue.Len() == 1 {
- // Single-line list for list with only 1 element
- ret += "["
- indexedValue, err := prettyPrint(propertyValue.Index(0), indent, emitZeroValues)
+ elements := make([]string, 0, propertyValue.Len())
+ for i := 0; i < propertyValue.Len(); i++ {
+ val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
if err != nil {
return "", err
}
- ret += indexedValue
- ret += "]"
- } else {
- // otherwise, use a multiline list.
- ret += "[\n"
- for i := 0; i < propertyValue.Len(); i++ {
- indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
- if err != nil {
- return "", err
- }
-
- if indexedValue != "" {
- ret += makeIndent(indent + 1)
- ret += indexedValue
- ret += ",\n"
- }
+ if val != "" {
+ elements = append(elements, val)
}
- ret += makeIndent(indent)
- ret += "]"
}
+ return starlark_fmt.PrintList(elements, indent, func(s string) string {
+ return "%s"
+ }), nil
case reflect.Struct:
// Special cases where the bp2build sends additional information to the codegenerator
@@ -611,18 +593,12 @@
return fmt.Sprintf("%q", label.Label), nil
}
- ret = "{\n"
// Sort and print the struct props by the key.
structProps := extractStructProperties(propertyValue, indent)
if len(structProps) == 0 {
return "", nil
}
- for _, k := range android.SortedStringKeys(structProps) {
- ret += makeIndent(indent + 1)
- ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
- }
- ret += makeIndent(indent)
- ret += "}"
+ return starlark_fmt.PrintDict(structProps, indent), nil
case reflect.Interface:
// TODO(b/164227191): implement pretty print for interfaces.
// Interfaces are used for for arch, multilib and target properties.
@@ -631,7 +607,6 @@
return "", fmt.Errorf(
"unexpected kind for property struct field: %s", propertyValue.Kind())
}
- return ret, nil
}
// Converts a reflected property struct value into a map of property names and property values,
@@ -736,13 +711,6 @@
return strings.ReplaceAll(s, "\"", "\\\"")
}
-func makeIndent(indent int) string {
- if indent < 0 {
- panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
- }
- return strings.Repeat(" ", indent)
-}
-
func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
name := ""
if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index b21a477..19209f6 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -20,6 +20,7 @@
"testing"
"android/soong/android"
+ "android/soong/android/allowlists"
"android/soong/python"
)
@@ -200,7 +201,7 @@
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
- ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
ctx.Register()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -500,6 +501,215 @@
}
}
+func TestBp2buildHostAndDevice(t *testing.T) {
+ testCases := []bp2buildTestCase{
+ {
+ description: "host and device, device only",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+ },
+ },
+ {
+ description: "host and device, both",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: true,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{}),
+ },
+ },
+ {
+ description: "host and device, host explicitly disabled",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+ },
+ },
+ {
+ description: "host and device, neither",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: false,
+ device_supported: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+ "target_compatible_with": `["@platforms//:incompatible"]`,
+ }),
+ },
+ },
+ {
+ description: "host and device, neither, cannot override with product_var",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: false,
+ device_supported: false,
+ product_variables: { unbundled_build: { enabled: true } },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+ "target_compatible_with": `["@platforms//:incompatible"]`,
+ }),
+ },
+ },
+ {
+ description: "host and device, both, disabled overrided with product_var",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: true,
+ device_supported: true,
+ enabled: false,
+ product_variables: { unbundled_build: { enabled: true } },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+ "target_compatible_with": `["//build/bazel/product_variables:unbundled_build"]`,
+ }),
+ },
+ },
+ {
+ description: "host and device, neither, cannot override with arch enabled",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: false,
+ device_supported: false,
+ arch: { x86: { enabled: true } },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+ "target_compatible_with": `["@platforms//:incompatible"]`,
+ }),
+ },
+ },
+ {
+ description: "host and device, host only",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: true,
+ device_supported: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+ },
+ },
+ {
+ description: "host only",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostSupported,
+ blueprint: `custom {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+ },
+ },
+ {
+ description: "device only",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryDeviceSupported,
+ blueprint: `custom {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+ },
+ },
+ {
+ description: "host and device default, default",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+ blueprint: `custom {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{}),
+ },
+ },
+ {
+ description: "host and device default, device only",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+ },
+ },
+ {
+ description: "host and device default, host only",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+ blueprint: `custom {
+ name: "foo",
+ device_supported: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+ },
+ },
+ {
+ description: "host and device default, neither",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+ blueprint: `custom {
+ name: "foo",
+ host_supported: false,
+ device_supported: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+ "target_compatible_with": `["@platforms//:incompatible"]`,
+ }),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ runBp2BuildTestCaseSimple(t, tc)
+ })
+ }
+}
+
func TestLoadStatements(t *testing.T) {
testCases := []struct {
bazelTargets BazelTargets
@@ -609,6 +819,7 @@
{
bp: `custom {
name: "bar",
+ host_supported: true,
one_to_many_prop: true,
bazel_module: { bp2build_available: true },
}`,
@@ -633,7 +844,7 @@
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
- ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -679,7 +890,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
},
},
{
@@ -692,7 +903,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
},
},
{
@@ -705,7 +916,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a",
"b",
@@ -724,7 +935,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `["b"]`,
}),
},
@@ -739,7 +950,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"other/a.txt",
"other/b.txt",
@@ -771,7 +982,7 @@
"other/file": "",
},
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a.txt",
"b.txt",
@@ -800,7 +1011,7 @@
}`,
},
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"//other:foo",
"c",
@@ -883,7 +1094,7 @@
{
description: "generates more than 1 target if needed",
moduleTypeUnderTest: "custom",
- moduleTypeUnderTestFactory: customModuleFactory,
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
bp: `custom {
name: "foo",
one_to_many_prop: true,
@@ -922,7 +1133,7 @@
moduleTypeUnderTestFactory android.ModuleFactory
expectedCount map[string]int
description string
- bp2buildConfig android.Bp2BuildConfig
+ bp2buildConfig allowlists.Bp2BuildConfig
checkDir string
fs map[string]string
}{
@@ -937,10 +1148,10 @@
"not_migrated": 0,
"also_not_migrated": 0,
},
- bp2buildConfig: android.Bp2BuildConfig{
- "migrated": android.Bp2BuildDefaultTrueRecursively,
- "migrated/but_not_really": android.Bp2BuildDefaultFalse,
- "not_migrated": android.Bp2BuildDefaultFalse,
+ bp2buildConfig: allowlists.Bp2BuildConfig{
+ "migrated": allowlists.Bp2BuildDefaultTrueRecursively,
+ "migrated/but_not_really": allowlists.Bp2BuildDefaultFalse,
+ "not_migrated": allowlists.Bp2BuildDefaultFalse,
},
fs: map[string]string{
"migrated/Android.bp": `filegroup { name: "a" }`,
@@ -960,9 +1171,9 @@
"package-opt-out": 1,
"package-opt-out/subpackage": 0,
},
- bp2buildConfig: android.Bp2BuildConfig{
- "package-opt-in": android.Bp2BuildDefaultFalse,
- "package-opt-out": android.Bp2BuildDefaultTrueRecursively,
+ bp2buildConfig: allowlists.Bp2BuildConfig{
+ "package-opt-in": allowlists.Bp2BuildDefaultFalse,
+ "package-opt-out": allowlists.Bp2BuildDefaultTrueRecursively,
},
fs: map[string]string{
"package-opt-in/Android.bp": `
@@ -1004,7 +1215,8 @@
config := android.TestConfig(buildDir, nil, "", fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
+ allowlist := android.NewBp2BuildAllowlist().SetDefaultConfig(testCase.bp2buildConfig)
+ ctx.RegisterBp2BuildConfig(allowlist)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
@@ -1090,7 +1302,7 @@
"other/BUILD.bazel": `// definition for fg_bar`,
},
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
`// definition for fg_bar`,
},
},
@@ -1116,7 +1328,7 @@
},
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_bar", map[string]string{}),
+ makeBazelTargetNoRestrictions("filegroup", "fg_bar", map[string]string{}),
`// BUILD file`,
},
},
@@ -1201,7 +1413,7 @@
"dir/f.txt": "",
},
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a.txt",
"b.txt",
@@ -1232,7 +1444,7 @@
"dir/subdir/f.txt": "",
},
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a.txt",
"//dir/subdir:e.txt",
@@ -1263,7 +1475,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"data": `[":reqd"]`,
}),
},
@@ -1294,6 +1506,7 @@
"//conditions:default": [],
})`,
"srcs_version": `"PY3"`,
+ "imports": `["."]`,
}),
},
},
@@ -1319,6 +1532,7 @@
":reqd",
]`,
"srcs_version": `"PY3"`,
+ "imports": `["."]`,
}),
},
},
@@ -1333,7 +1547,7 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"data": `[":reqd"]`,
}),
},
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index a156480..4794269 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -15,12 +15,13 @@
package bp2build
import (
- "android/soong/android"
- "android/soong/cc"
- "android/soong/genrule"
"fmt"
"strings"
"testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
)
const (
@@ -33,10 +34,11 @@
attrs attrNameToString
}
-func generateBazelTargetsForTest(targets []testBazelTarget) []string {
+func generateBazelTargetsForTest(targets []testBazelTarget, hod android.HostOrDeviceSupported) []string {
ret := make([]string, 0, len(targets))
for _, t := range targets {
- ret = append(ret, makeBazelTarget(t.typ, t.name, t.attrs))
+ attrs := t.attrs.clone()
+ ret = append(ret, makeBazelTargetHostOrDevice(t.typ, t.name, attrs, hod))
}
return ret
}
@@ -64,42 +66,33 @@
runCcHostBinaryTestCase(t, tc)
}
-func runCcBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
t.Helper()
moduleTypeUnderTest := "cc_binary"
- testCase := bp2buildTestCase{
- expectedBazelTargets: generateBazelTargetsForTest(tc.targets),
- moduleTypeUnderTest: moduleTypeUnderTest,
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- description: fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
- blueprint: binaryReplacer.Replace(tc.blueprint),
- }
- t.Run(testCase.description, func(t *testing.T) {
+
+ description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+ t.Run(description, func(t *testing.T) {
t.Helper()
- runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
+ runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
+ expectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.DeviceSupported),
+ moduleTypeUnderTest: moduleTypeUnderTest,
+ moduleTypeUnderTestFactory: cc.BinaryFactory,
+ description: description,
+ blueprint: binaryReplacer.Replace(testCase.blueprint),
+ })
})
}
-func runCcHostBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcHostBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
t.Helper()
- testCase := tc
- for i, tar := range testCase.targets {
- if tar.typ != "cc_binary" {
- continue
- }
- tar.attrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- testCase.targets[i] = tar
- }
moduleTypeUnderTest := "cc_binary_host"
- t.Run(testCase.description, func(t *testing.T) {
+ description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+ t.Run(description, func(t *testing.T) {
runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
- expectedBazelTargets: generateBazelTargetsForTest(testCase.targets),
+ expectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.HostSupported),
moduleTypeUnderTest: moduleTypeUnderTest,
moduleTypeUnderTestFactory: cc.BinaryHostFactory,
- description: fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
+ description: description,
blueprint: hostBinaryReplacer.Replace(testCase.blueprint),
})
})
@@ -127,6 +120,9 @@
keep_symbols_list: ["symbol"],
none: true,
},
+ sdk_version: "current",
+ min_sdk_version: "29",
+ use_version_lib: true,
}
`,
targets: []testBazelTarget{
@@ -150,6 +146,9 @@
"keep_symbols_list": ["symbol"],
"none": True,
}`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
+ "use_version_lib": `True`,
},
},
},
@@ -459,7 +458,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
},
include_build_directory: false,
}`,
@@ -483,7 +481,6 @@
srcs: ["foo.proto"],
static_executable: true,
proto: {
- canonical_path_from_root: false,
},
include_build_directory: false,
}`,
@@ -500,3 +497,49 @@
},
})
}
+
+func TestCcBinaryConvertLex(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: `.l and .ll sources converted to .c and .cc`,
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+ lex: { flags: ["--foo_opt", "--bar_opt"] },
+ include_build_directory: false,
+}
+`,
+ targets: []testBazelTarget{
+ {"genlex", "foo_genlex_l", attrNameToString{
+ "srcs": `[
+ "foo1.l",
+ "foo2.l",
+ ]`,
+ "lexopts": `[
+ "--foo_opt",
+ "--bar_opt",
+ ]`,
+ }},
+ {"genlex", "foo_genlex_ll", attrNameToString{
+ "srcs": `[
+ "bar1.ll",
+ "bar2.ll",
+ ]`,
+ "lexopts": `[
+ "--foo_opt",
+ "--bar_opt",
+ ]`,
+ }},
+ {"cc_binary", "foo", attrNameToString{
+ "srcs": `[
+ "bar.cc",
+ ":foo_genlex_ll",
+ ]`,
+ "srcs_c": `[
+ "foo.c",
+ ":foo_genlex_l",
+ ]`,
+ }},
+ },
+ })
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index ee19783..555f5a7 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -115,6 +115,9 @@
},
},
include_build_directory: false,
+ sdk_version: "current",
+ min_sdk_version: "29",
+ use_version_lib: true,
}
`,
expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
@@ -140,6 +143,9 @@
"//build/bazel/platforms/os:linux_bionic": ["bionic.cpp"],
"//conditions:default": [],
})`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
+ "use_version_lib": `True`,
}),
})
}
@@ -279,8 +285,8 @@
srcs: ["both.cpp"],
cflags: ["bothflag"],
shared_libs: ["shared_dep_for_both"],
- static_libs: ["static_dep_for_both"],
- whole_static_libs: ["whole_static_lib_for_both"],
+ static_libs: ["static_dep_for_both", "whole_and_static_lib_for_both"],
+ whole_static_libs: ["whole_static_lib_for_both", "whole_and_static_lib_for_both"],
static: {
srcs: ["staticonly.cpp"],
cflags: ["staticflag"],
@@ -328,6 +334,11 @@
bazel_module: { bp2build_available: false },
}
+cc_library_static {
+ name: "whole_and_static_lib_for_both",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library {
name: "shared_dep_for_shared",
bazel_module: { bp2build_available: false },
@@ -363,6 +374,7 @@
]`,
"whole_archive_deps": `[
":whole_static_lib_for_both",
+ ":whole_and_static_lib_for_both",
":whole_static_lib_for_static",
]`}),
makeBazelTarget("cc_library_shared", "a", attrNameToString{
@@ -384,6 +396,7 @@
]`,
"whole_archive_deps": `[
":whole_static_lib_for_both",
+ ":whole_and_static_lib_for_both",
":whole_static_lib_for_shared",
]`,
}),
@@ -1287,6 +1300,7 @@
"strip": true,
"stubs_symbol_file": true,
"stubs_versions": true,
+ "inject_bssl_hash": true,
}
sharedAttrs := attrNameToString{}
staticAttrs := attrNameToString{}
@@ -1822,6 +1836,33 @@
)
}
+func TestLibcryptoHashInjection(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library - libcrypto hash injection",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{},
+ blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libcrypto",
+ target: {
+ android: {
+ inject_bssl_hash: true,
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ expectedBazelTargets: makeCcLibraryTargets("libcrypto", attrNameToString{
+ "inject_bssl_hash": `select({
+ "//build/bazel/platforms/os:android": True,
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ )
+}
+
func TestCcLibraryCppStdWithGnuExtensions_ConvertsToFeatureAttr(t *testing.T) {
type testCase struct {
cpp_std string
@@ -1838,76 +1879,78 @@
// not set, only emit if gnu_extensions is disabled. the default (gnu+17
// is set in the toolchain.)
{cpp_std: "", gnu_extensions: "", bazel_cpp_std: ""},
- {cpp_std: "", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c99"},
+ {cpp_std: "", gnu_extensions: "false", bazel_cpp_std: "cpp_std_default_no_gnu", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "", gnu_extensions: "true", bazel_cpp_std: ""},
// experimental defaults to gnu++2a
- {cpp_std: "experimental", gnu_extensions: "", bazel_cpp_std: "gnu++2a"},
- {cpp_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c99"},
- {cpp_std: "experimental", gnu_extensions: "true", bazel_cpp_std: "gnu++2a"},
+ {cpp_std: "experimental", gnu_extensions: "", bazel_cpp_std: "cpp_std_experimental"},
+ {cpp_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "cpp_std_experimental_no_gnu", bazel_c_std: "c_std_default_no_gnu"},
+ {cpp_std: "experimental", gnu_extensions: "true", bazel_cpp_std: "cpp_std_experimental"},
// Explicitly setting a c++ std does not use replace gnu++ std even if
// gnu_extensions is true.
// "c++11",
{cpp_std: "c++11", gnu_extensions: "", bazel_cpp_std: "c++11"},
- {cpp_std: "c++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c99"},
+ {cpp_std: "c++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "c++11", gnu_extensions: "true", bazel_cpp_std: "c++11"},
// "c++17",
{cpp_std: "c++17", gnu_extensions: "", bazel_cpp_std: "c++17"},
- {cpp_std: "c++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c99"},
+ {cpp_std: "c++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "c++17", gnu_extensions: "true", bazel_cpp_std: "c++17"},
// "c++2a",
{cpp_std: "c++2a", gnu_extensions: "", bazel_cpp_std: "c++2a"},
- {cpp_std: "c++2a", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c99"},
+ {cpp_std: "c++2a", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "c++2a", gnu_extensions: "true", bazel_cpp_std: "c++2a"},
// "c++98",
{cpp_std: "c++98", gnu_extensions: "", bazel_cpp_std: "c++98"},
- {cpp_std: "c++98", gnu_extensions: "false", bazel_cpp_std: "c++98", bazel_c_std: "c99"},
+ {cpp_std: "c++98", gnu_extensions: "false", bazel_cpp_std: "c++98", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "c++98", gnu_extensions: "true", bazel_cpp_std: "c++98"},
// gnu++ is replaced with c++ if gnu_extensions is explicitly false.
// "gnu++11",
{cpp_std: "gnu++11", gnu_extensions: "", bazel_cpp_std: "gnu++11"},
- {cpp_std: "gnu++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c99"},
+ {cpp_std: "gnu++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "gnu++11", gnu_extensions: "true", bazel_cpp_std: "gnu++11"},
// "gnu++17",
{cpp_std: "gnu++17", gnu_extensions: "", bazel_cpp_std: "gnu++17"},
- {cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c99"},
+ {cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c_std_default_no_gnu"},
{cpp_std: "gnu++17", gnu_extensions: "true", bazel_cpp_std: "gnu++17"},
// some c_std test cases
- {c_std: "experimental", gnu_extensions: "", bazel_c_std: "gnu11"},
- {c_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c11"},
- {c_std: "experimental", gnu_extensions: "true", bazel_c_std: "gnu11"},
+ {c_std: "experimental", gnu_extensions: "", bazel_c_std: "c_std_experimental"},
+ {c_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "cpp_std_default_no_gnu", bazel_c_std: "c_std_experimental_no_gnu"},
+ {c_std: "experimental", gnu_extensions: "true", bazel_c_std: "c_std_experimental"},
{c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "", bazel_cpp_std: "gnu++17", bazel_c_std: "gnu11"},
{c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c11"},
{c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "true", bazel_cpp_std: "gnu++17", bazel_c_std: "gnu11"},
}
for i, tc := range testCases {
- name_prefix := fmt.Sprintf("a_%v", i)
- cppStdProp := ""
- if tc.cpp_std != "" {
- cppStdProp = fmt.Sprintf(" cpp_std: \"%s\",", tc.cpp_std)
- }
- cStdProp := ""
- if tc.c_std != "" {
- cStdProp = fmt.Sprintf(" c_std: \"%s\",", tc.c_std)
- }
- gnuExtensionsProp := ""
- if tc.gnu_extensions != "" {
- gnuExtensionsProp = fmt.Sprintf(" gnu_extensions: %s,", tc.gnu_extensions)
- }
- attrs := attrNameToString{}
- if tc.bazel_cpp_std != "" {
- attrs["cpp_std"] = fmt.Sprintf(`"%s"`, tc.bazel_cpp_std)
- }
- if tc.bazel_c_std != "" {
- attrs["c_std"] = fmt.Sprintf(`"%s"`, tc.bazel_c_std)
- }
+ name := fmt.Sprintf("cpp std: %q, c std: %q, gnu_extensions: %q", tc.cpp_std, tc.c_std, tc.gnu_extensions)
+ t.Run(name, func(t *testing.T) {
+ name_prefix := fmt.Sprintf("a_%v", i)
+ cppStdProp := ""
+ if tc.cpp_std != "" {
+ cppStdProp = fmt.Sprintf(" cpp_std: \"%s\",", tc.cpp_std)
+ }
+ cStdProp := ""
+ if tc.c_std != "" {
+ cStdProp = fmt.Sprintf(" c_std: \"%s\",", tc.c_std)
+ }
+ gnuExtensionsProp := ""
+ if tc.gnu_extensions != "" {
+ gnuExtensionsProp = fmt.Sprintf(" gnu_extensions: %s,", tc.gnu_extensions)
+ }
+ attrs := attrNameToString{}
+ if tc.bazel_cpp_std != "" {
+ attrs["cpp_std"] = fmt.Sprintf(`"%s"`, tc.bazel_cpp_std)
+ }
+ if tc.bazel_c_std != "" {
+ attrs["c_std"] = fmt.Sprintf(`"%s"`, tc.bazel_c_std)
+ }
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: fmt.Sprintf(
- "cc_library with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: fmt.Sprintf(
+ "cc_library with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
cc_library {
name: "%s_full",
%s // cpp_std: *string
@@ -1916,15 +1959,15 @@
include_build_directory: false,
}
`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
- expectedBazelTargets: makeCcLibraryTargets(name_prefix+"_full", attrs),
- })
+ expectedBazelTargets: makeCcLibraryTargets(name_prefix+"_full", attrs),
+ })
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: fmt.Sprintf(
- "cc_library_static with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
+ runCcLibraryStaticTestCase(t, bp2buildTestCase{
+ description: fmt.Sprintf(
+ "cc_library_static with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+ moduleTypeUnderTest: "cc_library_static",
+ moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
cc_library_static {
name: "%s_static",
%s // cpp_std: *string
@@ -1933,17 +1976,17 @@
include_build_directory: false,
}
`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", name_prefix+"_static", attrs),
- },
- })
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", name_prefix+"_static", attrs),
+ },
+ })
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: fmt.Sprintf(
- "cc_library_shared with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: fmt.Sprintf(
+ "cc_library_shared with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+ moduleTypeUnderTest: "cc_library_shared",
+ moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
cc_library_shared {
name: "%s_shared",
%s // cpp_std: *string
@@ -1952,9 +1995,10 @@
include_build_directory: false,
}
`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs),
- },
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs),
+ },
+ })
})
}
}
@@ -1970,8 +2014,7 @@
}`,
expectedBazelTargets: []string{
makeBazelTarget("proto_library", "foo_proto", attrNameToString{
- "srcs": `["foo.proto"]`,
- "strip_import_prefix": `""`,
+ "srcs": `["foo.proto"]`,
}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
"deps": `[":foo_proto"]`,
}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -1996,7 +2039,8 @@
}`,
expectedBazelTargets: []string{
makeBazelTarget("proto_library", "foo_proto", attrNameToString{
- "srcs": `["foo.proto"]`,
+ "srcs": `["foo.proto"]`,
+ "strip_import_prefix": `""`,
}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
"deps": `[":foo_proto"]`,
}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2021,8 +2065,7 @@
}`,
expectedBazelTargets: []string{
makeBazelTarget("proto_library", "foo_proto", attrNameToString{
- "srcs": `["foo.proto"]`,
- "strip_import_prefix": `""`,
+ "srcs": `["foo.proto"]`,
}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
"deps": `[":foo_proto"]`,
}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2043,7 +2086,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
type: "full",
},
include_build_directory: false,
@@ -2071,7 +2113,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
type: "lite",
},
include_build_directory: false,
@@ -2099,7 +2140,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2133,7 +2173,6 @@
name: "a",
srcs: [":a_fg_proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2143,7 +2182,6 @@
name: "b",
srcs: [":b_protos"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2153,7 +2191,6 @@
name: "c",
srcs: [":c-proto-srcs"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2163,7 +2200,6 @@
name: "d",
srcs: [":proto-srcs-d"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2245,6 +2281,7 @@
blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.cpp"],
+ host_supported: true,
target: {
darwin: {
enabled: false,
@@ -2280,6 +2317,7 @@
name: "foo",
srcs: ["foo.cpp"],
enabled: false,
+ host_supported: true,
target: {
darwin: {
enabled: true,
@@ -2344,6 +2382,7 @@
moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
+ host_supported: true,
srcs: ["foo.cpp"],
shared: {
enabled: false
@@ -2409,3 +2448,66 @@
},
)
}
+
+func TestCcLibraryEscapeLdflags(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ ldflags: ["-Wl,--rpath,${ORIGIN}"],
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+ "linkopts": `["-Wl,--rpath,$${ORIGIN}"]`,
+ }),
+ })
+}
+
+func TestCcLibraryConvertLex(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{
+ "foo.c": "",
+ "bar.cc": "",
+ "foo1.l": "",
+ "bar1.ll": "",
+ "foo2.l": "",
+ "bar2.ll": "",
+ },
+ blueprint: `cc_library {
+ name: "foo_lib",
+ srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+ lex: { flags: ["--foo_flags"] },
+ include_build_directory: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: append([]string{
+ makeBazelTarget("genlex", "foo_lib_genlex_l", attrNameToString{
+ "srcs": `[
+ "foo1.l",
+ "foo2.l",
+ ]`,
+ "lexopts": `["--foo_flags"]`,
+ }),
+ makeBazelTarget("genlex", "foo_lib_genlex_ll", attrNameToString{
+ "srcs": `[
+ "bar1.ll",
+ "bar2.ll",
+ ]`,
+ "lexopts": `["--foo_flags"]`,
+ }),
+ },
+ makeCcLibraryTargets("foo_lib", attrNameToString{
+ "srcs": `[
+ "bar.cc",
+ ":foo_lib_genlex_ll",
+ ]`,
+ "srcs_c": `[
+ "foo.c",
+ ":foo_lib_genlex_l",
+ ]`,
+ })...),
+ })
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index e4cfa35..e5bb120 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -112,6 +112,8 @@
export_include_dirs: ["arch_x86_64_exported_include_dir"],
},
},
+ sdk_version: "current",
+ min_sdk_version: "29",
// TODO: Also support export_header_lib_headers
}`,
@@ -130,6 +132,8 @@
":lib-1",
":lib-2",
]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index e8ba573..be09616 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -136,6 +136,8 @@
"header_lib_1",
"header_lib_2"
],
+ sdk_version: "current",
+ min_sdk_version: "29",
// TODO: Also support export_header_lib_headers
}`,
@@ -174,6 +176,8 @@
":whole_static_lib_1",
":whole_static_lib_2",
]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
@@ -431,7 +435,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -493,3 +496,76 @@
},
)
}
+
+func TestCcLibrarySharedSystemSharedLibsSharedEmpty(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_shared system_shared_libs empty shared default",
+ moduleTypeUnderTest: "cc_library_shared",
+ moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ blueprint: soongCcLibrarySharedPreamble + `
+cc_defaults {
+ name: "empty_defaults",
+ shared: {
+ system_shared_libs: [],
+ },
+ include_build_directory: false,
+}
+cc_library_shared {
+ name: "empty",
+ defaults: ["empty_defaults"],
+}
+`,
+ expectedBazelTargets: []string{makeBazelTarget("cc_library_shared", "empty", attrNameToString{
+ "system_dynamic_deps": "[]",
+ })},
+ })
+}
+
+func TestCcLibrarySharedConvertLex(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_shared with lex files",
+ moduleTypeUnderTest: "cc_library_shared",
+ moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ filesystem: map[string]string{
+ "foo.c": "",
+ "bar.cc": "",
+ "foo1.l": "",
+ "bar1.ll": "",
+ "foo2.l": "",
+ "bar2.ll": "",
+ },
+ blueprint: `cc_library_shared {
+ name: "foo_lib",
+ srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+ lex: { flags: ["--foo_flags"] },
+ include_build_directory: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("genlex", "foo_lib_genlex_l", attrNameToString{
+ "srcs": `[
+ "foo1.l",
+ "foo2.l",
+ ]`,
+ "lexopts": `["--foo_flags"]`,
+ }),
+ makeBazelTarget("genlex", "foo_lib_genlex_ll", attrNameToString{
+ "srcs": `[
+ "bar1.ll",
+ "bar2.ll",
+ ]`,
+ "lexopts": `["--foo_flags"]`,
+ }),
+ makeBazelTarget("cc_library_shared", "foo_lib", attrNameToString{
+ "srcs": `[
+ "bar.cc",
+ ":foo_lib_genlex_ll",
+ ]`,
+ "srcs_c": `[
+ "foo.c",
+ ":foo_lib_genlex_l",
+ ]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index f1684c4..be10e86 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -166,6 +166,8 @@
"header_lib_1",
"header_lib_2"
],
+ sdk_version: "current",
+ min_sdk_version: "29",
// TODO: Also support export_header_lib_headers
}`,
@@ -202,6 +204,8 @@
":whole_static_lib_1",
":whole_static_lib_2",
]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
@@ -1436,7 +1440,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 0a6c317..ea58086 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -55,6 +55,8 @@
"a/b/*.c"
],
exclude_srcs: ["a/b/exclude.c"],
+ sdk_version: "current",
+ min_sdk_version: "29",
}
`,
expectedBazelTargets: []string{
@@ -71,6 +73,8 @@
]`,
"srcs": `["a/b/c.c"]`,
"system_dynamic_deps": `[]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
new file mode 100644
index 0000000..3cf8969
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -0,0 +1,250 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package bp2build
+
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/cc"
+)
+
+func TestPrebuiltLibraryStaticAndSharedSimple(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library static and shared simple",
+ moduleTypeUnderTest: "cc_prebuilt_library",
+ moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ srcs: ["libf.so"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ "static_library": `"libf.so"`,
+ }),
+ makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ "shared_library": `"libf.so"`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibraryWithArchVariance(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library with arch variance",
+ moduleTypeUnderTest: "cc_prebuilt_library",
+ moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ arch: {
+ arm64: { srcs: ["libf.so"], },
+ arm: { srcs: ["libg.so"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ "static_library": `select({
+ "//build/bazel/platforms/arch:arm": "libg.so",
+ "//build/bazel/platforms/arch:arm64": "libf.so",
+ "//conditions:default": None,
+ })`,
+ }),
+ makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ "shared_library": `select({
+ "//build/bazel/platforms/arch:arm": "libg.so",
+ "//build/bazel/platforms/arch:arm64": "libf.so",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibraryAdditionalAttrs(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library additional attributes",
+ moduleTypeUnderTest: "cc_prebuilt_library",
+ moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "testdir/1/": "",
+ "testdir/2/": "",
+ },
+ blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ srcs: ["libf.so"],
+ export_include_dirs: ["testdir/1/"],
+ export_system_include_dirs: ["testdir/2/"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ "static_library": `"libf.so"`,
+ "export_includes": `["testdir/1/"]`,
+ "export_system_includes": `["testdir/2/"]`,
+ }),
+ // TODO(b/229374533): When fixed, update this test
+ makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ "shared_library": `"libf.so"`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibrarySharedStanzaFails(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library with shared stanza fails because multiple sources",
+ moduleTypeUnderTest: "cc_prebuilt_library",
+ moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ srcs: ["libf.so"],
+ shared: {
+ srcs: ["libg.so"],
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedErr: fmt.Errorf("Expected at most one source file"),
+ })
+}
+
+func TestPrebuiltLibraryStaticStanzaFails(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library with static stanza fails because multiple sources",
+ moduleTypeUnderTest: "cc_prebuilt_library",
+ moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ srcs: ["libf.so"],
+ static: {
+ srcs: ["libg.so"],
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedErr: fmt.Errorf("Expected at most one source file"),
+ })
+}
+
+func TestPrebuiltLibrarySharedAndStaticStanzas(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library with both shared and static stanzas",
+ moduleTypeUnderTest: "cc_prebuilt_library",
+ moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ static: {
+ srcs: ["libf.so"],
+ },
+ shared: {
+ srcs: ["libg.so"],
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ "static_library": `"libf.so"`,
+ }),
+ makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ "shared_library": `"libg.so"`,
+ }),
+ },
+ })
+}
+
+// TODO(b/228623543): When this bug is fixed, enable this test
+//func TestPrebuiltLibraryOnlyShared(t *testing.T) {
+// runBp2BuildTestCaseSimple(t,
+// bp2buildTestCase{
+// description: "prebuilt library shared only",
+// moduleTypeUnderTest: "cc_prebuilt_library",
+// moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+// filesystem: map[string]string{
+// "libf.so": "",
+// },
+// blueprint: `
+//cc_prebuilt_library {
+// name: "libtest",
+// srcs: ["libf.so"],
+// static: {
+// enabled: false,
+// },
+// bazel_module: { bp2build_available: true },
+//}`,
+// expectedBazelTargets: []string{
+// makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+// "shared_library": `"libf.so"`,
+// }),
+// },
+// })
+//}
+
+// TODO(b/228623543): When this bug is fixed, enable this test
+//func TestPrebuiltLibraryOnlyStatic(t *testing.T) {
+// runBp2BuildTestCaseSimple(t,
+// bp2buildTestCase{
+// description: "prebuilt library static only",
+// moduleTypeUnderTest: "cc_prebuilt_library",
+// moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+// filesystem: map[string]string{
+// "libf.so": "",
+// },
+// blueprint: `
+//cc_prebuilt_library {
+// name: "libtest",
+// srcs: ["libf.so"],
+// shared: {
+// enabled: false,
+// },
+// bazel_module: { bp2build_available: true },
+//}`,
+// expectedBazelTargets: []string{
+// makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+// "static_library": `"libf.so"`,
+// }),
+// },
+// })
+//}
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
index ef2fddc..57905e5 100644
--- a/bp2build/cc_prebuilt_library_shared_test.go
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -1,6 +1,7 @@
package bp2build
import (
+ "fmt"
"testing"
"android/soong/cc"
@@ -59,3 +60,26 @@
},
})
}
+
+func TestSharedPrebuiltLibrarySharedStanzaFails(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library shared with shared stanza fails because multiple sources",
+ moduleTypeUnderTest: "cc_prebuilt_library_shared",
+ moduleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ srcs: ["libf.so"],
+ shared: {
+ srcs: ["libg.so"],
+ },
+ bazel_module: { bp2build_available: true},
+}`,
+ expectedErr: fmt.Errorf("Expected at most one source file"),
+ })
+}
diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go
new file mode 100644
index 0000000..59839c8
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_static_test.go
@@ -0,0 +1,147 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package bp2build
+
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/cc"
+)
+
+func TestStaticPrebuiltLibrary(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library static simple",
+ moduleTypeUnderTest: "cc_prebuilt_library_static",
+ moduleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ srcs: ["libf.so"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_static", "libtest", attrNameToString{
+ "static_library": `"libf.so"`,
+ }),
+ },
+ })
+}
+
+func TestStaticPrebuiltLibraryWithArchVariance(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library static with arch variance",
+ moduleTypeUnderTest: "cc_prebuilt_library_static",
+ moduleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ arch: {
+ arm64: { srcs: ["libf.so"], },
+ arm: { srcs: ["libg.so"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_static", "libtest", attrNameToString{
+ "static_library": `select({
+ "//build/bazel/platforms/arch:arm": "libg.so",
+ "//build/bazel/platforms/arch:arm64": "libf.so",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ })
+}
+
+func TestStaticPrebuiltLibraryStaticStanzaFails(t *testing.T) {
+ runBp2BuildTestCaseSimple(t,
+ bp2buildTestCase{
+ description: "prebuilt library with static stanza fails because multiple sources",
+ moduleTypeUnderTest: "cc_prebuilt_library_static",
+ moduleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+ filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ srcs: ["libf.so"],
+ static: {
+ srcs: ["libg.so"],
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedErr: fmt.Errorf("Expected at most one source file"),
+ })
+}
+
+func TestCcLibraryStaticConvertLex(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_static with lex files",
+ moduleTypeUnderTest: "cc_library_static",
+ moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ filesystem: map[string]string{
+ "foo.c": "",
+ "bar.cc": "",
+ "foo1.l": "",
+ "bar1.ll": "",
+ "foo2.l": "",
+ "bar2.ll": "",
+ },
+ blueprint: `cc_library_static {
+ name: "foo_lib",
+ srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+ lex: { flags: ["--foo_flags"] },
+ include_build_directory: false,
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("genlex", "foo_lib_genlex_l", attrNameToString{
+ "srcs": `[
+ "foo1.l",
+ "foo2.l",
+ ]`,
+ "lexopts": `["--foo_flags"]`,
+ }),
+ makeBazelTarget("genlex", "foo_lib_genlex_ll", attrNameToString{
+ "srcs": `[
+ "bar1.ll",
+ "bar2.ll",
+ ]`,
+ "lexopts": `["--foo_flags"]`,
+ }),
+ makeBazelTarget("cc_library_static", "foo_lib", attrNameToString{
+ "srcs": `[
+ "bar.cc",
+ ":foo_lib_genlex_ll",
+ ]`,
+ "srcs_c": `[
+ "foo.c",
+ ":foo_lib_genlex_l",
+ ]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index dfbb265..d37a523 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -6,6 +6,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
)
// Configurability support for bp2build.
@@ -250,10 +251,10 @@
} else if defaultValue != nil {
// Print an explicit empty list (the default value) even if the value is
// empty, to avoid errors about not finding a configuration that matches.
- ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
+ ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
}
- ret += makeIndent(indent)
+ ret += starlark_fmt.Indention(indent)
ret += "})"
return ret, nil
@@ -262,7 +263,7 @@
// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
// with a provided key.
func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
- s := makeIndent(indent + 1)
+ s := starlark_fmt.Indention(indent + 1)
v, err := prettyPrint(value, indent+1, emitZeroValues)
if err != nil {
return "", err
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 96c12d3..4b013d9 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -7,7 +7,8 @@
"strings"
"android/soong/android"
- "android/soong/cc/config"
+ cc_config "android/soong/cc/config"
+ java_config "android/soong/java/config"
"github.com/google/blueprint/proptools"
)
@@ -22,18 +23,24 @@
var files []BazelFile
files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
- files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars(cfg)))
+ files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+
+ files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
+ files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
+ files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
+
apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
if err != nil {
panic(err)
}
files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
+ files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
return files
}
@@ -145,6 +152,7 @@
"target": true, // interface prop type is not supported yet.
"visibility": true, // Bazel has native visibility semantics. Handle later.
"features": true, // There is already a built-in attribute 'features' which cannot be overridden.
+ "for": true, // reserved keyword, b/233579439
}
)
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 629ca9b..e49d855 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -95,6 +95,14 @@
basename: "constants.bzl",
},
{
+ dir: "java_toolchain",
+ basename: GeneratedBuildFileName,
+ },
+ {
+ dir: "java_toolchain",
+ basename: "constants.bzl",
+ },
+ {
dir: "metrics",
basename: "converted_modules.txt",
},
@@ -103,6 +111,10 @@
basename: "soong_config_variables.bzl",
},
{
+ dir: "product_config",
+ basename: "arch_configuration.bzl",
+ },
+ {
dir: "api_levels",
basename: GeneratedBuildFileName,
},
@@ -110,6 +122,10 @@
dir: "api_levels",
basename: "api_levels.json",
},
+ {
+ dir: "api_levels",
+ basename: "api_levels.bzl",
+ },
}
if len(files) != len(expectedFilePaths) {
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 0666da7..4504892 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -56,6 +56,7 @@
moduleType string
factory android.ModuleFactory
genDir string
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -66,16 +67,19 @@
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
genDir: "$(RULEDIR)",
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
genDir: "$(RULEDIR)",
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
genDir: "$(RULEDIR)",
+ hod: android.HostSupported,
},
}
@@ -97,13 +101,15 @@
}`
for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
+ "cmd": fmt.Sprintf(`"$(location :foo.tool) --genDir=%s arg $(SRCS) $(OUTS)"`, tc.genDir),
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
+ "tools": `[":foo.tool"]`,
+ }
+
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
- "cmd": fmt.Sprintf(`"$(location :foo.tool) --genDir=%s arg $(SRCS) $(OUTS)"`, tc.genDir),
- "outs": `["foo.out"]`,
- "srcs": `["foo.in"]`,
- "tools": `[":foo.tool"]`,
- }),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
@@ -122,6 +128,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -130,14 +137,17 @@
{
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
+ hod: android.HostSupported,
},
}
@@ -158,25 +168,27 @@
bazel_module: { bp2build_available: true },
}`
- expectedBazelTargets :=
- []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
- "cmd": `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
- "outs": `["foo.out"]`,
- "srcs": `["foo.in"]`,
- "tools": `[":foo.tools"]`,
- }),
- makeBazelTarget("genrule", "foo.tools", attrNameToString{
- "cmd": `"cp $(SRCS) $(OUTS)"`,
- "outs": `[
+ for _, tc := range testCases {
+ fooAttrs := attrNameToString{
+ "cmd": `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
+ "tools": `[":foo.tools"]`,
+ }
+ fooToolsAttrs := attrNameToString{
+ "cmd": `"cp $(SRCS) $(OUTS)"`,
+ "outs": `[
"foo_tool.out",
"foo_tool2.out",
]`,
- "srcs": `["foo_tool.in"]`,
- }),
+ "srcs": `["foo_tool.in"]`,
}
- for _, tc := range testCases {
+ expectedBazelTargets := []string{
+ makeBazelTargetHostOrDevice("genrule", "foo", fooAttrs, tc.hod),
+ makeBazelTargetHostOrDevice("genrule", "foo.tools", fooToolsAttrs, tc.hod),
+ }
+
t.Run(tc.moduleType, func(t *testing.T) {
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
bp2buildTestCase{
@@ -193,6 +205,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -201,14 +214,17 @@
{
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
+ hod: android.HostSupported,
},
}
@@ -221,16 +237,18 @@
bazel_module: { bp2build_available: true },
}`
- expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
"cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
"tools": `["//other:foo.tool"]`,
- }),
- }
+ }
- for _, tc := range testCases {
+ expectedBazelTargets := []string{
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+ }
+
t.Run(tc.moduleType, func(t *testing.T) {
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
bp2buildTestCase{
@@ -248,6 +266,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -256,14 +275,17 @@
{
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
+ hod: android.HostSupported,
},
}
@@ -276,16 +298,18 @@
bazel_module: { bp2build_available: true },
}`
- expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
"cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
"outs": `["foo.out"]`,
"srcs": `["//other:other.tool"]`,
"tools": `["//other:foo.tool"]`,
- }),
- }
+ }
- for _, tc := range testCases {
+ expectedBazelTargets := []string{
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+ }
+
t.Run(tc.moduleType, func(t *testing.T) {
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
bp2buildTestCase{
@@ -303,6 +327,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -311,14 +336,17 @@
{
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
+ hod: android.HostSupported,
},
}
@@ -331,8 +359,8 @@
bazel_module: { bp2build_available: true },
}`
- expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
"cmd": `"$(location //other:foo.tool) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
@@ -340,9 +368,12 @@
"//other:foo.tool",
"//other:other.tool",
]`,
- })}
+ }
- for _, tc := range testCases {
+ expectedBazelTargets := []string{
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+ }
+
t.Run(tc.moduleType, func(t *testing.T) {
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
bp2buildTestCase{
@@ -360,6 +391,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -368,14 +400,17 @@
{
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
+ hod: android.HostSupported,
},
}
@@ -388,8 +423,8 @@
bazel_module: { bp2build_available: true },
}`
- expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
"cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
@@ -397,9 +432,12 @@
"//other:foo.tool",
"//other:other.tool",
]`,
- })}
+ }
- for _, tc := range testCases {
+ expectedBazelTargets := []string{
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+ }
+
t.Run(tc.moduleType, func(t *testing.T) {
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
bp2buildTestCase{
@@ -417,6 +455,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -425,14 +464,17 @@
{
moduleType: "cc_genrule",
factory: cc.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule",
factory: java.GenRuleFactory,
+ hod: android.DeviceSupported,
},
{
moduleType: "java_genrule_host",
factory: java.GenRuleFactoryHost,
+ hod: android.HostSupported,
},
}
@@ -444,14 +486,17 @@
bazel_module: { bp2build_available: true },
}`
- expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", attrNameToString{
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
"cmd": `"cp $(SRCS) $(OUTS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
- })}
+ }
- for _, tc := range testCases {
+ expectedBazelTargets := []string{
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+ }
+
t.Run(tc.moduleType, func(t *testing.T) {
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
bp2buildTestCase{
@@ -481,7 +526,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
"cmd": `"do-something $(SRCS) $(OUTS)"`,
"outs": `["out"]`,
"srcs": `["in1"]`,
@@ -506,7 +551,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
"cmd": `"do-something $(SRCS) $(OUTS)"`,
"outs": `[
"out-from-defaults",
@@ -539,7 +584,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
"cmd": `"cp $(SRCS) $(OUTS)"`,
"outs": `["out"]`,
"srcs": `["in1"]`,
@@ -576,7 +621,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
"cmd": `"cmd1 $(SRCS) $(OUTS)"`,
"outs": `[
"out-from-3",
diff --git a/bp2build/gensrcs_conversion_test.go b/bp2build/gensrcs_conversion_test.go
new file mode 100644
index 0000000..ebe60bf
--- /dev/null
+++ b/bp2build/gensrcs_conversion_test.go
@@ -0,0 +1,80 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/genrule"
+ "testing"
+)
+
+func TestGensrcs(t *testing.T) {
+ testcases := []struct {
+ name string
+ bp string
+ expectedBazelAttrs attrNameToString
+ }{
+ {
+ name: "gensrcs with common usage of properties",
+ bp: `
+ gensrcs {
+ name: "foo",
+ srcs: ["test/input.txt", ":external_files"],
+ tool_files: ["program.py"],
+ cmd: "$(location program.py) $(in) $(out)",
+ output_extension: "out",
+ bazel_module: { bp2build_available: true },
+ }`,
+ expectedBazelAttrs: attrNameToString{
+ "srcs": `[
+ "test/input.txt",
+ ":external_files__BP2BUILD__MISSING__DEP",
+ ]`,
+ "tools": `["program.py"]`,
+ "output_extension": `"out"`,
+ "cmd": `"$(location program.py) $(SRC) $(OUT)"`,
+ },
+ },
+ {
+ name: "gensrcs with out_extension unset",
+ bp: `
+ gensrcs {
+ name: "foo",
+ srcs: ["input.txt"],
+ cmd: "cat $(in) > $(out)",
+ bazel_module: { bp2build_available: true },
+ }`,
+ expectedBazelAttrs: attrNameToString{
+ "srcs": `["input.txt"]`,
+ "cmd": `"cat $(SRC) > $(OUT)"`,
+ },
+ },
+ }
+
+ for _, test := range testcases {
+ expectedBazelTargets := []string{
+ makeBazelTarget("gensrcs", "foo", test.expectedBazelAttrs),
+ }
+ t.Run(test.name, func(t *testing.T) {
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ bp2buildTestCase{
+ moduleTypeUnderTest: "gensrcs",
+ moduleTypeUnderTestFactory: genrule.GenSrcsFactory,
+ blueprint: test.bp,
+ expectedBazelTargets: expectedBazelTargets,
+ })
+ })
+ }
+}
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index c683b25..d7a76a8 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -28,6 +28,7 @@
(&tc).moduleTypeUnderTestFactory = java.BinaryHostFactory
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("cc_library_host_shared", cc.LibraryHostSharedFactory)
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
}, tc)
}
@@ -51,6 +52,7 @@
jni_libs: ["jni-lib-1"],
javacflags: ["-Xdoclint:all/protected"],
bazel_module: { bp2build_available: true },
+ java_version: "8",
}`,
expectedBazelTargets: []string{
makeBazelTarget("java_binary", "java-binary-host-1", attrNameToString{
@@ -58,7 +60,44 @@
"main_class": `"com.android.test.MainClass"`,
"deps": `["//other:jni-lib-1"]`,
"jvm_flags": `["-Djava.library.path=$${RUNPATH}other"]`,
- "javacopts": `["-Xdoclint:all/protected"]`,
+ "javacopts": `[
+ "-Xdoclint:all/protected",
+ "-source 1.8 -target 1.8",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostRuntimeDeps(t *testing.T) {
+ runJavaBinaryHostTestCase(t, bp2buildTestCase{
+ description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
+ filesystem: fs,
+ blueprint: `java_binary_host {
+ name: "java-binary-host-1",
+ static_libs: ["java-dep-1"],
+ manifest: "test.mf",
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-dep-1",
+ srcs: ["a.java"],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_binary", "java-binary-host-1", attrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-dep-1"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
},
})
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
index 2f7211c..0b3191c 100644
--- a/bp2build/java_import_conversion_test.go
+++ b/bp2build/java_import_conversion_test.go
@@ -29,7 +29,7 @@
func registerJavaImportModuleTypes(ctx android.RegistrationContext) {
}
-func TestMinimalJavaImport(t *testing.T) {
+func TestJavaImportMinimal(t *testing.T) {
runJavaImportTestCase(t, bp2buildTestCase{
description: "Java import - simple example",
moduleTypeUnderTest: "java_import",
@@ -50,3 +50,36 @@
}),
}})
}
+
+func TestJavaImportArchVariant(t *testing.T) {
+ runJavaImportTestCase(t, bp2buildTestCase{
+ description: "Java import - simple example",
+ moduleTypeUnderTest: "java_import",
+ moduleTypeUnderTestFactory: java.ImportFactory,
+ filesystem: map[string]string{
+ "import.jar": "",
+ },
+ blueprint: `
+java_import {
+ name: "example_import",
+ target: {
+ android: {
+ jars: ["android.jar"],
+ },
+ linux_glibc: {
+ jars: ["linux.jar"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_import", "example_import", attrNameToString{
+ "jars": `select({
+ "//build/bazel/platforms/os:android": ["android.jar"],
+ "//build/bazel/platforms/os:linux": ["linux.jar"],
+ "//conditions:default": [],
+ })`,
+ }),
+ }})
+}
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 5c65ec2..e4d9cbc 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -15,17 +15,23 @@
package bp2build
import (
+ "fmt"
"testing"
"android/soong/android"
"android/soong/java"
)
-func runJavaLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+func runJavaLibraryTestCaseWithRegistrationCtxFunc(t *testing.T, tc bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
t.Helper()
(&tc).moduleTypeUnderTest = "java_library"
(&tc).moduleTypeUnderTestFactory = java.LibraryFactory
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+ runBp2BuildTestCase(t, registrationCtxFunc, tc)
+}
+
+func runJavaLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ runJavaLibraryTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {})
}
func TestJavaLibrary(t *testing.T) {
@@ -55,3 +61,314 @@
},
})
}
+
+func TestJavaLibraryConvertsStaticLibsToDepsAndExports(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ libs: ["java-lib-2"],
+ static_libs: ["java-lib-3"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["b.java"],
+ bazel_module: { bp2build_available: false },
+}
+
+java_library {
+ name: "java-lib-3",
+ srcs: ["c.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `[
+ ":java-lib-2",
+ ":java-lib-3",
+ ]`,
+ "exports": `[":java-lib-3"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryConvertsStaticLibsToExportsIfNoSrcs(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ static_libs: ["java-lib-2"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["a.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "exports": `[":java-lib-2"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryFailsToConvertLibsWithNoSrcs(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ expectedErr: fmt.Errorf("Module has direct dependencies but no sources. Bazel will not allow this."),
+ blueprint: `java_library {
+ name: "java-lib-1",
+ libs: ["java-lib-2"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["a.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{},
+ })
+}
+
+func TestJavaLibraryPlugins(t *testing.T) {
+ runJavaLibraryTestCaseWithRegistrationCtxFunc(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ plugins: ["java-plugin-1"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_plugin {
+ name: "java-plugin-1",
+ srcs: ["a.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "plugins": `[":java-plugin-1"]`,
+ }),
+ },
+ }, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_plugin", java.PluginFactory)
+ })
+}
+
+func TestJavaLibraryJavaVersion(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ java_version: "11",
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "srcs": `["a.java"]`,
+ "javacopts": `["-source 11 -target 11"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryErrorproneJavacflagsEnabledManually(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ javacflags: ["-Xsuper-fast"],
+ errorprone: {
+ enabled: true,
+ javacflags: ["-Xep:SpeedLimit:OFF"],
+ },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "javacopts": `[
+ "-Xsuper-fast",
+ "-Xep:SpeedLimit:OFF",
+ ]`,
+ "srcs": `["a.java"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryErrorproneJavacflagsErrorproneDisabledByDefault(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ javacflags: ["-Xsuper-fast"],
+ errorprone: {
+ javacflags: ["-Xep:SpeedLimit:OFF"],
+ },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "javacopts": `["-Xsuper-fast"]`,
+ "srcs": `["a.java"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryErrorproneJavacflagsErrorproneDisabledManually(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ javacflags: ["-Xsuper-fast"],
+ errorprone: {
+ enabled: false,
+ javacflags: ["-Xep:SpeedLimit:OFF"],
+ },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "javacopts": `["-Xsuper-fast"]`,
+ "srcs": `["a.java"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryLogTags(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ description: "Java library - logtags creates separate dependency",
+ moduleTypeUnderTest: "java_library",
+ moduleTypeUnderTestFactory: java.LibraryFactory,
+ blueprint: `java_library {
+ name: "example_lib",
+ srcs: [
+ "a.java",
+ "b.java",
+ "a.logtag",
+ "b.logtag",
+ ],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("event_log_tags", "example_lib_logtags", attrNameToString{
+ "srcs": `[
+ "a.logtag",
+ "b.logtag",
+ ]`,
+ }),
+ makeBazelTarget("java_library", "example_lib", attrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ":example_lib_logtags",
+ ]`,
+ }),
+ }})
+}
+
+func TestJavaLibraryResources(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ filesystem: map[string]string{
+ "res/a.res": "",
+ "res/b.res": "",
+ "res/dir1/b.res": "",
+ },
+ blueprint: `java_library {
+ name: "java-lib-1",
+ java_resources: ["res/a.res", "res/b.res"],
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "resources": `[
+ "res/a.res",
+ "res/b.res",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryResourceDirs(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ filesystem: map[string]string{
+ "res/a.res": "",
+ "res/b.res": "",
+ "res/dir1/b.res": "",
+ },
+ blueprint: `java_library {
+ name: "java-lib-1",
+ java_resource_dirs: ["res"],
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "resource_strip_prefix": `"res"`,
+ "resources": `[
+ "res/a.res",
+ "res/b.res",
+ "res/dir1/b.res",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryResourcesExcludeDir(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ filesystem: map[string]string{
+ "res/a.res": "",
+ "res/exclude/b.res": "",
+ },
+ blueprint: `java_library {
+ name: "java-lib-1",
+ java_resource_dirs: ["res"],
+ exclude_java_resource_dirs: ["res/exclude"],
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "resource_strip_prefix": `"res"`,
+ "resources": `["res/a.res"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryResourcesExcludeFile(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ filesystem: map[string]string{
+ "res/a.res": "",
+ "res/dir1/b.res": "",
+ "res/dir1/exclude.res": "",
+ },
+ blueprint: `java_library {
+ name: "java-lib-1",
+ java_resource_dirs: ["res"],
+ exclude_java_resources: ["res/dir1/exclude.res"],
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ "resource_strip_prefix": `"res"`,
+ "resources": `[
+ "res/a.res",
+ "res/dir1/b.res",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestJavaLibraryResourcesFailsWithMultipleDirs(t *testing.T) {
+ runJavaLibraryTestCase(t, bp2buildTestCase{
+ filesystem: map[string]string{
+ "res/a.res": "",
+ "res1/a.res": "",
+ },
+ blueprint: `java_library {
+ name: "java-lib-1",
+ java_resource_dirs: ["res", "res1"],
+}`,
+ expectedErr: fmt.Errorf("bp2build does not support more than one directory in java_resource_dirs (b/226423379)"),
+ expectedBazelTargets: []string{},
+ })
+}
diff --git a/bp2build/java_library_host_conversion_test.go b/bp2build/java_library_host_conversion_test.go
index 6ac82db..83cc551 100644
--- a/bp2build/java_library_host_conversion_test.go
+++ b/bp2build/java_library_host_conversion_test.go
@@ -43,14 +43,24 @@
name: "java-lib-host-2",
srcs: ["c.java"],
bazel_module: { bp2build_available: true },
+ java_version: "9",
}`,
expectedBazelTargets: []string{
makeBazelTarget("java_library", "java-lib-host-1", attrNameToString{
"srcs": `["a.java"]`,
"deps": `[":java-lib-host-2"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
makeBazelTarget("java_library", "java-lib-host-2", attrNameToString{
- "srcs": `["c.java"]`,
+ "javacopts": `["-source 1.9 -target 1.9"]`,
+ "srcs": `["c.java"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
},
})
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
new file mode 100644
index 0000000..dc763e7
--- /dev/null
+++ b/bp2build/java_plugin_conversion_test.go
@@ -0,0 +1,110 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func runJavaPluginTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "java_plugin"
+ (&tc).moduleTypeUnderTestFactory = java.PluginFactory
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
+ }, tc)
+}
+
+func TestJavaPlugin(t *testing.T) {
+ runJavaPluginTestCase(t, bp2buildTestCase{
+ description: "java_plugin with srcs, libs, static_libs",
+ blueprint: `java_plugin {
+ name: "java-plug-1",
+ srcs: ["a.java", "b.java"],
+ libs: ["java-lib-1"],
+ static_libs: ["java-lib-2"],
+ bazel_module: { bp2build_available: true },
+ java_version: "7",
+}
+
+java_library {
+ name: "java-lib-1",
+ srcs: ["b.java"],
+ bazel_module: { bp2build_available: false },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["c.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ "deps": `[
+ ":java-lib-1",
+ ":java-lib-2",
+ ]`,
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ]`,
+ "javacopts": `["-source 1.7 -target 1.7"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaPluginNoSrcs(t *testing.T) {
+ runJavaPluginTestCase(t, bp2buildTestCase{
+ description: "java_plugin without srcs converts (static) libs to deps",
+ blueprint: `java_plugin {
+ name: "java-plug-1",
+ libs: ["java-lib-1"],
+ static_libs: ["java-lib-2"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-lib-1",
+ srcs: ["b.java"],
+ bazel_module: { bp2build_available: false },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["c.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ "deps": `[
+ ":java-lib-1",
+ ":java-lib-2",
+ ]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
new file mode 100644
index 0000000..c6feeb8
--- /dev/null
+++ b/bp2build/java_proto_conversion_test.go
@@ -0,0 +1,124 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func runJavaProtoTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "java_library_static"
+ (&tc).moduleTypeUnderTestFactory = java.LibraryFactory
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestJavaProto(t *testing.T) {
+ testCases := []struct {
+ protoType string
+ javaLibraryType string
+ javaLibraryNameExtension string
+ }{
+ {
+ protoType: "nano",
+ javaLibraryType: "java_nano_proto_library",
+ javaLibraryNameExtension: "java_proto_nano",
+ },
+ {
+ protoType: "micro",
+ javaLibraryType: "java_micro_proto_library",
+ javaLibraryNameExtension: "java_proto_micro",
+ },
+ {
+ protoType: "lite",
+ javaLibraryType: "java_lite_proto_library",
+ javaLibraryNameExtension: "java_proto_lite",
+ },
+ {
+ protoType: "stream",
+ javaLibraryType: "java_stream_proto_library",
+ javaLibraryNameExtension: "java_proto_stream",
+ },
+ {
+ protoType: "full",
+ javaLibraryType: "java_proto_library",
+ javaLibraryNameExtension: "java_proto",
+ },
+ }
+
+ bp := `java_library_static {
+ name: "java-protos",
+ proto: {
+ type: "%s",
+ },
+ srcs: ["a.proto"],
+}`
+
+ protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+ "srcs": `["a.proto"]`,
+ })
+
+ for _, tc := range testCases {
+ javaLibraryName := fmt.Sprintf("java-protos_%s", tc.javaLibraryNameExtension)
+
+ runJavaProtoTestCase(t, bp2buildTestCase{
+ description: fmt.Sprintf("java_proto %s", tc.protoType),
+ blueprint: fmt.Sprintf(bp, tc.protoType),
+ expectedBazelTargets: []string{
+ protoLibrary,
+ makeBazelTarget(
+ tc.javaLibraryType,
+ javaLibraryName,
+ attrNameToString{
+ "deps": `[":java-protos_proto"]`,
+ }),
+ makeBazelTarget("java_library", "java-protos", attrNameToString{
+ "exports": fmt.Sprintf(`[":%s"]`, javaLibraryName),
+ }),
+ },
+ })
+ }
+}
+
+func TestJavaProtoDefault(t *testing.T) {
+ runJavaProtoTestCase(t, bp2buildTestCase{
+ description: "java_library proto default",
+ blueprint: `java_library_static {
+ name: "java-protos",
+ srcs: ["a.proto"],
+ java_version: "7",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+ "srcs": `["a.proto"]`,
+ }),
+ makeBazelTarget(
+ "java_lite_proto_library",
+ "java-protos_java_proto_lite",
+ attrNameToString{
+ "deps": `[":java-protos_proto"]`,
+ }),
+ makeBazelTarget("java_library", "java-protos", attrNameToString{
+ "exports": `[":java-protos_java_proto_lite"]`,
+ "javacopts": `["-source 1.7 -target 1.7"]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 8a0b1c9..04fac44 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -43,6 +43,8 @@
// Counts of total modules by module type.
totalModuleTypeCount map[string]uint64
+
+ Events []*bp2build_metrics_proto.Event
}
// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics
@@ -55,6 +57,7 @@
ConvertedModules: metrics.convertedModules,
ConvertedModuleTypeCount: metrics.convertedModuleTypeCount,
TotalModuleTypeCount: metrics.totalModuleTypeCount,
+ Events: metrics.Events,
}
}
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index 3a5d5bb..2e4b221 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -45,11 +45,11 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+ makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `"version/tz_version"`,
- "sub_dir": `"tz"`,
+ "dir": `"etc/tz"`,
})}})
}
@@ -75,7 +75,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+ makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `select({
@@ -83,7 +83,7 @@
"//build/bazel/platforms/arch:arm64": "arm64",
"//conditions:default": "version/tz_version",
})`,
- "sub_dir": `"tz"`,
+ "dir": `"etc/tz"`,
})}})
}
@@ -114,7 +114,7 @@
}
`,
expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+ makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `select({
@@ -125,6 +125,59 @@
"//build/bazel/platforms/os_arch:linux_bionic_arm64": "darwin_or_arm64",
"//conditions:default": "version/tz_version",
})`,
- "sub_dir": `"tz"`,
+ "dir": `"etc/tz"`,
+ })}})
+}
+
+func runPrebuiltUsrShareTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "prebuilt_usr_share"
+ (&tc).moduleTypeUnderTestFactory = etc.PrebuiltUserShareFactory
+ runBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
+}
+
+func registerPrebuiltUsrShareModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestPrebuiltUsrShareSimple(t *testing.T) {
+ runPrebuiltUsrShareTestCase(t, bp2buildTestCase{
+ description: "prebuilt_usr_share - simple example",
+ filesystem: map[string]string{},
+ blueprint: `
+prebuilt_usr_share {
+ name: "apex_tz_version",
+ src: "version/tz_version",
+ filename: "tz_version",
+ sub_dir: "tz",
+ installable: false,
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ "filename": `"tz_version"`,
+ "installable": `False`,
+ "src": `"version/tz_version"`,
+ "dir": `"usr/share/tz"`,
+ })}})
+}
+
+func TestPrebuiltEtcNoSubdir(t *testing.T) {
+ runPrebuiltEtcTestCase(t, bp2buildTestCase{
+ description: "prebuilt_etc - no subdir",
+ filesystem: map[string]string{},
+ blueprint: `
+prebuilt_etc {
+ name: "apex_tz_version",
+ src: "version/tz_version",
+ filename: "tz_version",
+ installable: false,
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ "filename": `"tz_version"`,
+ "installable": `False`,
+ "src": `"version/tz_version"`,
+ "dir": `"etc"`,
})}})
}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 40c8ba1..dfa11d1 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -51,6 +51,10 @@
"b/c.py",
"b/d.py",
]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
},
})
@@ -80,6 +84,10 @@
makeBazelTarget("py_binary", "foo", attrNameToString{
"python_version": `"PY2"`,
"srcs": `["a.py"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
},
})
@@ -109,6 +117,10 @@
// python_version is PY3 by default.
makeBazelTarget("py_binary", "foo", attrNameToString{
"srcs": `["a.py"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
},
})
@@ -141,6 +153,10 @@
"//build/bazel/platforms/arch:x86": ["x86.py"],
"//conditions:default": [],
})`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
}),
},
})
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
index 6b26105..f51f106 100644
--- a/bp2build/python_library_conversion_test.go
+++ b/bp2build/python_library_conversion_test.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "strings"
"testing"
"android/soong/android"
@@ -11,21 +12,73 @@
// TODO(alexmarquez): Should be lifted into a generic Bp2Build file
type PythonLibBp2Build func(ctx android.TopDownMutatorContext)
-func runPythonLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+type pythonLibBp2BuildTestCase struct {
+ description string
+ filesystem map[string]string
+ blueprint string
+ expectedBazelTargets []testBazelTarget
+ dir string
+ expectedError error
+}
+
+func convertPythonLibTestCaseToBp2build_Host(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+ for i := range tc.expectedBazelTargets {
+ tc.expectedBazelTargets[i].attrs["target_compatible_with"] = `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`
+ }
+
+ return convertPythonLibTestCaseToBp2build(tc)
+}
+
+func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+ var bp2BuildTargets []string
+ for _, t := range tc.expectedBazelTargets {
+ bp2BuildTargets = append(bp2BuildTargets, makeBazelTarget(t.typ, t.name, t.attrs))
+ }
+ // Copy the filesystem so that we can change stuff in it later without it
+ // affecting the original pythonLibBp2BuildTestCase
+ filesystemCopy := make(map[string]string)
+ for k, v := range tc.filesystem {
+ filesystemCopy[k] = v
+ }
+ return bp2buildTestCase{
+ description: tc.description,
+ filesystem: filesystemCopy,
+ blueprint: tc.blueprint,
+ expectedBazelTargets: bp2BuildTargets,
+ dir: tc.dir,
+ expectedErr: tc.expectedError,
+ }
+}
+
+func runPythonLibraryTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
t.Helper()
- testCase := tc
+ testCase := convertPythonLibTestCaseToBp2build(tc)
testCase.description = fmt.Sprintf(testCase.description, "python_library")
testCase.blueprint = fmt.Sprintf(testCase.blueprint, "python_library")
+ for name, contents := range testCase.filesystem {
+ if strings.HasSuffix(name, "Android.bp") {
+ testCase.filesystem[name] = fmt.Sprintf(contents, "python_library")
+ }
+ }
testCase.moduleTypeUnderTest = "python_library"
testCase.moduleTypeUnderTestFactory = python.PythonLibraryFactory
+
runBp2BuildTestCaseSimple(t, testCase)
}
-func runPythonLibraryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+func runPythonLibraryHostTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
t.Helper()
- testCase := tc
+ testCase := convertPythonLibTestCaseToBp2build_Host(tc)
testCase.description = fmt.Sprintf(testCase.description, "python_library_host")
testCase.blueprint = fmt.Sprintf(testCase.blueprint, "python_library_host")
+ for name, contents := range testCase.filesystem {
+ if strings.HasSuffix(name, "Android.bp") {
+ testCase.filesystem[name] = fmt.Sprintf(contents, "python_library_host")
+ }
+ }
testCase.moduleTypeUnderTest = "python_library_host"
testCase.moduleTypeUnderTestFactory = python.PythonLibraryHostFactory
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
@@ -34,14 +87,14 @@
testCase)
}
-func runPythonLibraryTestCases(t *testing.T, tc bp2buildTestCase) {
+func runPythonLibraryTestCases(t *testing.T, tc pythonLibBp2BuildTestCase) {
t.Helper()
runPythonLibraryTestCase(t, tc)
runPythonLibraryHostTestCase(t, tc)
}
func TestSimplePythonLib(t *testing.T) {
- testCases := []bp2buildTestCase{
+ testCases := []pythonLibBp2BuildTestCase{
{
description: "simple %s converts to a native py_library",
filesystem: map[string]string{
@@ -64,17 +117,22 @@
srcs: ["b/e.py"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_library", "foo", attrNameToString{
- "data": `["files/data.txt"]`,
- "deps": `[":bar"]`,
- "srcs": `[
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "data": `["files/data.txt"]`,
+ "deps": `[":bar"]`,
+ "srcs": `[
"a.py",
"b/c.py",
"b/d.py",
]`,
- "srcs_version": `"PY3"`,
- }),
+ "srcs_version": `"PY3"`,
+ "imports": `["."]`,
+ },
+ },
},
},
{
@@ -93,11 +151,16 @@
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_library", "foo", attrNameToString{
- "srcs": `["a.py"]`,
- "srcs_version": `"PY2"`,
- }),
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ "srcs_version": `"PY2"`,
+ "imports": `["."]`,
+ },
+ },
},
},
{
@@ -116,11 +179,16 @@
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_library", "foo", attrNameToString{
- "srcs": `["a.py"]`,
- "srcs_version": `"PY3"`,
- }),
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ "srcs_version": `"PY3"`,
+ "imports": `["."]`,
+ },
+ },
},
},
{
@@ -139,13 +207,60 @@
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- // srcs_version is PY2ANDPY3 by default.
- makeBazelTarget("py_library", "foo", attrNameToString{
- "srcs": `["a.py"]`,
- }),
+ expectedBazelTargets: []testBazelTarget{
+ {
+ // srcs_version is PY2ANDPY3 by default.
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ "imports": `["."]`,
+ },
+ },
},
},
+ {
+ description: "%s: pkg_path in a subdirectory of the same name converts correctly",
+ dir: "mylib/subpackage",
+ filesystem: map[string]string{
+ "mylib/subpackage/a.py": "",
+ "mylib/subpackage/Android.bp": `%s {
+ name: "foo",
+ srcs: ["a.py"],
+ pkg_path: "mylib/subpackage",
+
+ bazel_module: { bp2build_available: true },
+ }`,
+ },
+ blueprint: `%s {name: "bar"}`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ // srcs_version is PY2ANDPY3 by default.
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ "imports": `["../.."]`,
+ "srcs_version": `"PY3"`,
+ },
+ },
+ },
+ },
+ {
+ description: "%s: pkg_path in a subdirectory of a different name fails",
+ dir: "mylib/subpackage",
+ filesystem: map[string]string{
+ "mylib/subpackage/a.py": "",
+ "mylib/subpackage/Android.bp": `%s {
+ name: "foo",
+ srcs: ["a.py"],
+ pkg_path: "mylib/subpackage2",
+ bazel_module: { bp2build_available: true },
+ }`,
+ },
+ blueprint: `%s {name: "bar"}`,
+ expectedError: fmt.Errorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in."),
+ },
}
for _, tc := range testCases {
@@ -156,7 +271,7 @@
}
func TestPythonArchVariance(t *testing.T) {
- runPythonLibraryTestCases(t, bp2buildTestCase{
+ runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
description: "test %s arch variants",
filesystem: map[string]string{
"dir/arm.py": "",
@@ -173,15 +288,63 @@
},
},
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_library", "foo", attrNameToString{
- "srcs": `select({
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `select({
"//build/bazel/platforms/arch:arm": ["arm.py"],
"//build/bazel/platforms/arch:x86": ["x86.py"],
"//conditions:default": [],
})`,
- "srcs_version": `"PY3"`,
- }),
+ "srcs_version": `"PY3"`,
+ "imports": `["."]`,
+ },
+ },
+ },
+ })
+}
+
+func TestPythonLibraryWithProtobufs(t *testing.T) {
+ runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
+ description: "test %s protobuf",
+ filesystem: map[string]string{
+ "dir/mylib.py": "",
+ "dir/myproto.proto": "",
+ },
+ blueprint: `%s {
+ name: "foo",
+ srcs: [
+ "dir/mylib.py",
+ "dir/myproto.proto",
+ ],
+ }`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "proto_library",
+ name: "foo_proto",
+ attrs: attrNameToString{
+ "srcs": `["dir/myproto.proto"]`,
+ },
+ },
+ {
+ typ: "py_proto_library",
+ name: "foo_py_proto",
+ attrs: attrNameToString{
+ "deps": `[":foo_proto"]`,
+ },
+ },
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["dir/mylib.py"]`,
+ "srcs_version": `"PY3"`,
+ "imports": `["."]`,
+ "deps": `[":foo_py_proto"]`,
+ },
+ },
},
})
}
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index b1e1fb2..8460cae 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -49,6 +49,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
feature1: {
conditions_default: {
@@ -94,6 +95,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
feature1: {
conditions_default: {
@@ -141,6 +143,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
board: {
soc_a: {
@@ -200,6 +203,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
feature1: {
conditions_default: {
@@ -268,6 +272,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
board: {
soc_a: {
@@ -356,6 +361,7 @@
name: "lib",
defaults: ["foo_defaults_2"],
bazel_module: { bp2build_available: true },
+ host_supported: true,
}
`
@@ -429,12 +435,14 @@
name: "lib",
defaults: ["foo_defaults", "bar_defaults"],
bazel_module: { bp2build_available: true },
+ host_supported: true,
}
cc_library_static {
name: "lib2",
defaults: ["bar_defaults", "foo_defaults"],
bazel_module: { bp2build_available: true },
+ host_supported: true,
}
`
@@ -550,6 +558,7 @@
name: "lib",
defaults: ["foo_defaults", "qux_defaults"],
bazel_module: { bp2build_available: true },
+ host_supported: true,
}
`
@@ -615,6 +624,7 @@
library_linking_strategy_cc_defaults {
name: "library_linking_strategy_merged_defaults",
defaults: ["library_linking_strategy_lib_a_defaults"],
+ host_supported: true,
soong_config_variables: {
library_linking_strategy: {
prefer_static: {
@@ -714,6 +724,7 @@
cc_binary {
name: "library_linking_strategy_sample_binary",
+ host_supported: true,
srcs: ["library_linking_strategy.cc"],
defaults: ["library_linking_strategy_sample_defaults"],
}`
@@ -800,6 +811,7 @@
cc_binary {
name: "alphabet_binary",
+ host_supported: true,
srcs: ["main.cc"],
defaults: ["alphabet_sample_cc_defaults"],
}`
@@ -861,6 +873,7 @@
cc_binary {
name: "alphabet_binary",
srcs: ["main.cc"],
+ host_supported: true,
defaults: ["alphabet_sample_cc_defaults"],
enabled: false,
arch: {
@@ -958,6 +971,7 @@
alphabet_cc_defaults {
name: "alphabet_sample_cc_defaults",
+ host_supported: true,
soong_config_variables: {
special_build: {
enabled: true,
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 15a6335..818d7ae 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -90,6 +90,26 @@
}
}
+func isDir(path string, fi os.FileInfo) bool {
+ if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
+ return fi.IsDir()
+ }
+
+ fi2, statErr := os.Stat(path)
+ if statErr == nil {
+ return fi2.IsDir()
+ }
+
+ // Check if this is a dangling symlink. If so, treat it like a file, not a dir.
+ _, lstatErr := os.Lstat(path)
+ if lstatErr != nil {
+ fmt.Fprintf(os.Stderr, "Cannot stat or lstat '%s': %s\n%s\n", path, statErr, lstatErr)
+ os.Exit(1)
+ }
+
+ return false
+}
+
// Recursively plants a symlink forest at forestDir. The symlink tree will
// contain every file in buildFilesDir and srcDir excluding the files in
// exclude. Collects every directory encountered during the traversal of srcDir
@@ -145,8 +165,18 @@
continue
}
+ sDir := false
+ bDir := false
+ if sExists {
+ sDir = isDir(shared.JoinPath(topdir, srcChild), srcChildEntry)
+ }
+
+ if bExists {
+ bDir = isDir(shared.JoinPath(topdir, buildFilesChild), buildFilesChildEntry)
+ }
+
if !sExists {
- if buildFilesChildEntry.IsDir() && excludeChild != nil {
+ if bDir && excludeChild != nil {
// Not in the source tree, but we have to exclude something from under
// this subtree, so descend
plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
@@ -155,7 +185,7 @@
symlinkIntoForest(topdir, forestChild, buildFilesChild)
}
} else if !bExists {
- if srcChildEntry.IsDir() && excludeChild != nil {
+ if sDir && excludeChild != nil {
// Not in the build file tree, but we have to exclude something from
// under this subtree, so descend
plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
@@ -163,10 +193,10 @@
// Not in the build file tree, symlink source tree, carry on
symlinkIntoForest(topdir, forestChild, srcChild)
}
- } else if srcChildEntry.IsDir() && buildFilesChildEntry.IsDir() {
+ } else if sDir && bDir {
// Both are directories. Descend.
plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
- } else if !srcChildEntry.IsDir() && !buildFilesChildEntry.IsDir() {
+ } else if !sDir && !bDir {
// Neither is a directory. Prioritize BUILD files generated by bp2build
// over any BUILD file imported into external/.
fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 53b60fa..580bac4 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -25,14 +25,17 @@
"testing"
"android/soong/android"
+ "android/soong/android/allowlists"
"android/soong/bazel"
)
var (
// A default configuration for tests to not have to specify bp2build_available on top level targets.
- bp2buildConfig = android.Bp2BuildConfig{
- android.BP2BUILD_TOPLEVEL: android.Bp2BuildDefaultTrueRecursively,
- }
+ bp2buildConfig = android.NewBp2BuildAllowlist().SetDefaultConfig(
+ allowlists.Bp2BuildConfig{
+ android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
+ },
+ )
buildDir string
)
@@ -81,8 +84,9 @@
expectedBazelTargets []string
filesystem map[string]string
dir string
- expectedErr error
- unconvertedDepsMode unconvertedDepsMode
+ // An error with a string contained within the string of the expected error
+ expectedErr error
+ unconvertedDepsMode unconvertedDepsMode
}
func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
@@ -115,8 +119,8 @@
return
}
- errs := append(parseErrs, resolveDepsErrs...)
- if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
+ parseAndResolveErrs := append(parseErrs, resolveDepsErrs...)
+ if tc.expectedErr != nil && checkError(t, parseAndResolveErrs, tc.expectedErr) {
return
}
@@ -131,7 +135,7 @@
if checkError(t, errs, tc.expectedErr) {
return
} else {
- t.Errorf("Expected error: %q, got: %q", tc.expectedErr, errs)
+ t.Errorf("Expected error: %q, got: %q and %q", tc.expectedErr, errs, parseAndResolveErrs)
}
} else {
android.FailIfErrored(t, errs)
@@ -209,12 +213,36 @@
return module
}
-func customModuleFactory() android.Module {
+func customModuleFactoryHostAndDevice() android.Module {
m := customModuleFactoryBase()
android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibBoth)
return m
}
+func customModuleFactoryDeviceSupported() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth)
+ return m
+}
+
+func customModuleFactoryHostSupported() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidArchModule(m, android.HostSupported, android.MultilibBoth)
+ return m
+}
+
+func customModuleFactoryHostAndDeviceDefault() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibBoth)
+ return m
+}
+
+func customModuleFactoryNeitherHostNorDeviceSupported() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidArchModule(m, android.NeitherHostNorDeviceSupported, android.MultilibBoth)
+ return m
+}
+
type testProps struct {
Test_prop struct {
Test_string_prop string
@@ -351,7 +379,7 @@
}
func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
- ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
ctx.RegisterForBazelConversion()
}
@@ -365,7 +393,29 @@
type attrNameToString map[string]string
-func makeBazelTarget(typ, name string, attrs attrNameToString) string {
+func (a attrNameToString) clone() attrNameToString {
+ newAttrs := make(attrNameToString, len(a))
+ for k, v := range a {
+ newAttrs[k] = v
+ }
+ return newAttrs
+}
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that can be host or
+// device specific, or independent of host/device.
+func makeBazelTargetHostOrDevice(typ, name string, attrs attrNameToString, hod android.HostOrDeviceSupported) string {
+ if _, ok := attrs["target_compatible_with"]; !ok {
+ switch hod {
+ case android.HostSupported:
+ attrs["target_compatible_with"] = `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`
+ case android.DeviceSupported:
+ attrs["target_compatible_with"] = `["//build/bazel/platforms/os:android"]`
+ }
+ }
+
attrStrings := make([]string, 0, len(attrs)+1)
attrStrings = append(attrStrings, fmt.Sprintf(` name = "%s",`, name))
for _, k := range android.SortedStringKeys(attrs) {
@@ -375,3 +425,16 @@
%s
)`, typ, strings.Join(attrStrings, "\n"))
}
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that does not add a
+// target_compatible_with. This is useful for module types like filegroup and genrule that arch not
+// arch variant
+func makeBazelTargetNoRestrictions(typ, name string, attrs attrNameToString) string {
+ return makeBazelTargetHostOrDevice(typ, name, attrs, android.HostAndDeviceDefault)
+}
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that is device specific
+// as this is the most common default in Soong.
+func makeBazelTarget(typ, name string, attrs attrNameToString) string {
+ return makeBazelTargetHostOrDevice(typ, name, attrs, android.DeviceSupported)
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index a4999e5..14b2d84 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -20,7 +20,6 @@
"strings"
"android/soong/android"
- _ "android/soong/cc/config"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -74,7 +73,10 @@
Include_dirs []string
Sub_dir string
// If set to true, generate BTF debug info for maps & programs
- Btf *bool
+ Btf *bool
+ Vendor *bool
+
+ VendorInternal bool `blueprint:"mutated"`
}
type bpf struct {
@@ -85,6 +87,41 @@
objs android.Paths
}
+var _ android.ImageInterface = (*bpf)(nil)
+
+func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return !proptools.Bool(bpf.properties.Vendor)
+}
+
+func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+ if proptools.Bool(bpf.properties.Vendor) {
+ return []string{"vendor"}
+ }
+ return nil
+}
+
+func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+ bpf.properties.VendorInternal = variation == "vendor"
+}
+
func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
cflags := []string{
"-nostdlibinc",
@@ -132,8 +169,8 @@
if proptools.Bool(bpf.properties.Btf) {
objStripped := android.ObjPathWithExt(ctx, "", src, "o")
ctx.Build(pctx, android.BuildParams{
- Rule: stripRule,
- Input: obj,
+ Rule: stripRule,
+ Input: obj,
Output: objStripped,
Args: map[string]string{
"stripCmd": "${config.ClangBin}/llvm-strip",
@@ -154,7 +191,12 @@
fmt.Fprintln(w)
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w)
- localModulePath := "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+ var localModulePath string
+ if bpf.properties.VendorInternal {
+ localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/bpf"
+ } else {
+ localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+ }
if len(bpf.properties.Sub_dir) > 0 {
localModulePath += "/" + bpf.properties.Sub_dir
}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 4f7d88c..94b28dc 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -449,6 +449,7 @@
}
hasInstrumentationFor := hasNonEmptyLiteralStringProperty(mod, "instrumentation_for")
+ hasTestSuites := hasNonEmptyLiteralListProperty(mod, "test_suites")
tags, _ := getLiteralListPropertyValue(mod, "tags")
var hasTestsTag bool
@@ -458,7 +459,7 @@
}
}
- isTest := hasInstrumentationFor || hasTestsTag
+ isTest := hasInstrumentationFor || hasTestsTag || hasTestSuites
if isTest {
switch mod.Type {
@@ -470,13 +471,7 @@
mod.Type = "java_test"
case "java_library_host":
mod.Type = "java_test_host"
- }
- }
-
- // when a cc_binary module has a nonempty test_suites field, modify the type to cc_test
- if mod.Type == "cc_binary" {
- hasTestSuites := hasNonEmptyLiteralListProperty(mod, "test_suites")
- if hasTestSuites {
+ case "cc_binary":
mod.Type = "cc_test"
}
}
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 17b3c24..672e852 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -1436,6 +1436,38 @@
}
`,
},
+ {
+ name: "android_app with android_test",
+ in: `
+ android_app {
+ name: "foo",
+ srcs: ["srcs"],
+ test_suites: ["test_suite1"],
+ }
+ `,
+ out: `
+ android_test {
+ name: "foo",
+ srcs: ["srcs"],
+ test_suites: ["test_suite1"],
+ }
+ `,
+ },
+ {
+ name: "android_app without test_suites",
+ in: `
+ android_app {
+ name: "foo",
+ srcs: ["srcs"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+ srcs: ["srcs"],
+ }
+ `,
+ },
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
diff --git a/build_kzip.bash b/build_kzip.bash
index aff2d6d..6219021 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -36,7 +36,7 @@
declare -r out="${OUT_DIR:-out}"
# Build extraction files for C++ and Java. Build `merge_zips` which we use later.
-build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
+build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java xref_rust
# Build extraction file for Go the files in build/{blueprint,soong} directories.
declare -r abspath_out=$(realpath "${out}")
@@ -44,7 +44,7 @@
declare -r go_root=$(realpath prebuilts/go/linux-x86)
declare -r source_root=$PWD
-# TODO(asmundak): Until b/182183061 is fixed, default corpus has to be specified
+# TODO(asmundak): Until b/182183061 is fixed, default corpus has to be specified
# in the rules file. Generate this file on the fly with corpus value set from the
# environment variable.
for dir in blueprint soong; do
diff --git a/build_test.bash b/build_test.bash
index b6d00e2..8b91e2c 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -25,7 +25,10 @@
# Products that are broken or otherwise don't work with multiproduct_kati
SKIPPED_PRODUCTS=(
+ # These products are for soong-only builds, and will fail the kati stage.
+ linux_bionic
mainline_sdk
+ ndk
)
# To track how long we took to startup. %N isn't supported on Darwin, but
diff --git a/cc/Android.bp b/cc/Android.bp
index 9103a48..ce94467 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -15,6 +15,7 @@
"soong-etc",
"soong-fuzz",
"soong-genrule",
+ "soong-multitree",
"soong-snapshot",
"soong-tradefed",
],
@@ -65,6 +66,7 @@
"library.go",
"library_headers.go",
"library_sdk_member.go",
+ "library_stub.go",
"native_bridge_sdk_trait.go",
"object.go",
"test.go",
@@ -89,17 +91,20 @@
],
testSrcs: [
"afdo_test.go",
+ "binary_test.go",
"cc_test.go",
"compiler_test.go",
"gen_test.go",
"genrule_test.go",
"library_headers_test.go",
+ "library_stub_test.go",
"library_test.go",
"object_test.go",
"prebuilt_test.go",
"proto_test.go",
"sanitize_test.go",
"test_data_test.go",
+ "tidy_test.go",
"vendor_public_library_test.go",
"vendor_snapshot_test.go",
],
diff --git a/cc/OWNERS b/cc/OWNERS
index a438b15..ffbf14a 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -1,4 +1,4 @@
per-file ndk_*.go = danalbert@google.com
-per-file tidy.go = srhines@google.com, chh@google.com
+per-file tidy*.go = srhines@google.com, chh@google.com
per-file afdo.go,afdo_test.go,lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
per-file coverage.go = pirama@google.com, srhines@google.com, allenhair@google.com
diff --git a/cc/afdo.go b/cc/afdo.go
index d7cce77..66e9732 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -32,7 +32,7 @@
var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
-const afdoCFlagsFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
+const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s"
func getAfdoProfileProjects(config android.DeviceConfig) []string {
return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
@@ -45,6 +45,8 @@
}
type AfdoProperties struct {
+ // Afdo allows developers self-service enroll for
+ // automatic feedback-directed optimization using profile data.
Afdo bool
AfdoTarget *string `blueprint:"mutated"`
diff --git a/cc/androidmk.go b/cc/androidmk.go
index b56d689..ff5ba45 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -108,6 +108,9 @@
if len(c.Properties.AndroidMkHeaderLibs) > 0 {
entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
}
+ if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
+ entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
+ }
entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
if c.UseVndk() {
entries.SetBool("LOCAL_USE_VNDK", true)
@@ -328,6 +331,14 @@
})
}
+func (test *testDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ if len(test.InstallerProperties.Test_suites) > 0 {
+ entries.AddCompatibilityTestSuites(test.InstallerProperties.Test_suites...)
+ }
+ })
+}
+
func (binary *binaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, binary.baseInstaller)
@@ -376,14 +387,13 @@
func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, test.binaryDecorator)
+ ctx.subAndroidMk(entries, test.testDecorator)
+
entries.Class = "NATIVE_TESTS"
if Bool(test.Properties.Test_per_src) {
entries.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
}
entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- if len(test.Properties.Test_suites) > 0 {
- entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
- }
if test.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
}
@@ -396,6 +406,9 @@
}
entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
+ if len(test.Properties.Data_bins) > 0 {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+ }
})
AndroidMkWriteTestData(test.data, entries)
@@ -439,6 +452,7 @@
func (test *testLibrary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, test.libraryDecorator)
+ ctx.subAndroidMk(entries, test.testDecorator)
}
func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -554,10 +568,6 @@
func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
entries.SubName = c.baseProperties.Androidmk_suffix
-
- entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
- })
}
func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/binary.go b/cc/binary.go
index ee3de3f..b2f2482 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -17,6 +17,7 @@
import (
"path/filepath"
+ "android/soong/bazel/cquery"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -70,13 +71,13 @@
// cc_binary produces a binary that is runnable on a device.
func BinaryFactory() android.Module {
module, _ := newBinary(android.HostAndDeviceSupported, true)
+ module.bazelHandler = &ccBinaryBazelHandler{module: module}
return module.Init()
}
// cc_binary_host produces a binary that is runnable on a host.
func BinaryHostFactory() android.Module {
module, _ := newBinary(android.HostSupported, true)
- module.bazelable = true
return module.Init()
}
@@ -182,7 +183,7 @@
}
}
- if !binary.static() && inList("libc", deps.StaticLibs) && !ctx.BazelConversionMode() {
+ if !binary.static() && inList("libc", deps.StaticLibs) {
ctx.ModuleErrorf("statically linking libc to dynamic executable, please remove libc\n" +
"from static libs or set static_executable: true")
}
@@ -220,18 +221,18 @@
func (binary *binaryDecorator) linkerInit(ctx BaseModuleContext) {
binary.baseLinker.linkerInit(ctx)
- if !ctx.toolchain().Bionic() && !ctx.toolchain().Musl() {
- if ctx.Os() == android.Linux {
- // Unless explicitly specified otherwise, host static binaries are built with -static
- // if HostStaticBinaries is true for the product configuration.
- if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
- binary.Properties.Static_executable = BoolPtr(true)
- }
- } else {
- // Static executables are not supported on Darwin or Windows
- binary.Properties.Static_executable = nil
+ if ctx.Os().Linux() && ctx.Host() {
+ // Unless explicitly specified otherwise, host static binaries are built with -static
+ // if HostStaticBinaries is true for the product configuration.
+ if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
+ binary.Properties.Static_executable = BoolPtr(true)
}
}
+
+ if ctx.Darwin() || ctx.Windows() {
+ // Static executables are not supported on Darwin or Windows
+ binary.Properties.Static_executable = nil
+ }
}
func (binary *binaryDecorator) static() bool {
@@ -441,6 +442,16 @@
// Need to determine symlinks early since some targets (ie APEX) need this
// information but will not call 'install'
+ binary.setSymlinkList(ctx)
+
+ return ret
+}
+
+func (binary *binaryDecorator) unstrippedOutputFilePath() android.Path {
+ return binary.unstrippedOutputFile
+}
+
+func (binary *binaryDecorator) setSymlinkList(ctx ModuleContext) {
for _, symlink := range binary.Properties.Symlinks {
binary.symlinks = append(binary.symlinks,
symlink+String(binary.Properties.Suffix)+ctx.toolchain().ExecutableSuffix())
@@ -457,12 +468,6 @@
binary.preferredArchSymlink = symlinkName
}
}
-
- return ret
-}
-
-func (binary *binaryDecorator) unstrippedOutputFilePath() android.Path {
- return binary.unstrippedOutputFile
}
func (binary *binaryDecorator) symlinkList() []string {
@@ -557,6 +562,35 @@
})
}
+type ccBinaryBazelHandler struct {
+ module *Module
+}
+
+var _ BazelHandler = (*ccBinaryBazelHandler)(nil)
+
+func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+}
+
+func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ if len(filePaths) != 1 {
+ ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
+ return
+ }
+ outputFilePath := android.PathForBazelOut(ctx, filePaths[0])
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ // TODO(b/220164721): We need to decide if we should return the stripped as the unstripped.
+ handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
+}
+
func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
baseAttrs := bp2BuildParseBaseProps(ctx, m)
binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
@@ -589,6 +623,7 @@
Linkopts: baseAttrs.linkopts,
Link_crt: baseAttrs.linkCrt,
Use_libcrt: baseAttrs.useLibcrt,
+ Use_version_lib: baseAttrs.useVersionLib,
Rtti: baseAttrs.rtti,
Stl: baseAttrs.stl,
Cpp_std: baseAttrs.cppStd,
@@ -604,21 +639,16 @@
},
Features: baseAttrs.features,
+
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
- var enabledProperty bazel.BoolAttribute
- if typ == "cc_binary_host" {
- falseVal := false
- enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, android.Android.Name, &falseVal)
- }
-
- ctx.CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties{
+ ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
Rule_class: "cc_binary",
- Bzl_load_location: "//build/bazel/rules:cc_binary.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
},
android.CommonAttributes{Name: m.Name()},
- attrs,
- enabledProperty)
+ attrs)
}
// binaryAttributes contains Bazel attributes corresponding to a cc binary
@@ -644,8 +674,9 @@
Linkopts bazel.StringListAttribute
Additional_linker_inputs bazel.LabelListAttribute
- Link_crt bazel.BoolAttribute
- Use_libcrt bazel.BoolAttribute
+ Link_crt bazel.BoolAttribute
+ Use_libcrt bazel.BoolAttribute
+ Use_version_lib bazel.BoolAttribute
Rtti bazel.BoolAttribute
Stl *string
@@ -654,4 +685,6 @@
Strip stripAttributes
Features bazel.StringListAttribute
+
+ sdkAttributes
}
diff --git a/cc/binary_test.go b/cc/binary_test.go
new file mode 100644
index 0000000..cba5974
--- /dev/null
+++ b/cc/binary_test.go
@@ -0,0 +1,71 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestCcBinaryWithBazel(t *testing.T) {
+ bp := `
+cc_binary {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//foo/bar:bar": []string{"foo"},
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+
+ binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+ producer := binMod.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+ expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+ android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
+
+func TestBinaryLinkerScripts(t *testing.T) {
+ result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+ cc_binary {
+ name: "foo",
+ srcs: ["foo.cc"],
+ linker_scripts: ["foo.ld", "bar.ld"],
+ }`)
+
+ binFoo := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("ld")
+
+ android.AssertStringListContains(t, "missing dependency on linker_scripts",
+ binFoo.Implicits.Strings(), "foo.ld")
+ android.AssertStringListContains(t, "missing dependency on linker_scripts",
+ binFoo.Implicits.Strings(), "bar.ld")
+ android.AssertStringDoesContain(t, "missing flag for linker_scripts",
+ binFoo.Args["ldFlags"], "-Wl,--script,foo.ld")
+ android.AssertStringDoesContain(t, "missing flag for linker_scripts",
+ binFoo.Args["ldFlags"], "-Wl,--script,bar.ld")
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index c5eab06..70dcf40 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -16,7 +16,6 @@
import (
"fmt"
"path/filepath"
- "regexp"
"strings"
"android/soong/android"
@@ -30,16 +29,12 @@
const (
cSrcPartition = "c"
asSrcPartition = "as"
+ lSrcPartition = "l"
+ llSrcPartition = "ll"
cppSrcPartition = "cpp"
protoSrcPartition = "proto"
)
-var (
- // ignoring case, checks for proto or protos as an independent word in the name, whether at the
- // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
- filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
-)
-
// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
// properties which apply to either the shared or static version of a cc_library module.
type staticOrSharedAttributes struct {
@@ -59,48 +54,42 @@
System_dynamic_deps bazel.LabelListAttribute
Enabled bazel.BoolAttribute
+
+ sdkAttributes
}
+// groupSrcsByExtension partitions `srcs` into groups based on file extension.
func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
- // Check that a module is a filegroup type
- isFilegroup := func(m blueprint.Module) bool {
- return ctx.OtherModuleType(m) == "filegroup"
- }
-
// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
// macro.
addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
m, exists := ctx.ModuleFromName(label.OriginalModuleName)
labelStr := label.Label
- if !exists || !isFilegroup(m) {
+ if !exists || !android.IsFilegroup(ctx, m) {
return labelStr, false
}
return labelStr + suffix, true
}
}
- isProtoFilegroup := func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
- m, exists := ctx.ModuleFromName(label.OriginalModuleName)
- labelStr := label.Label
- if !exists || !isFilegroup(m) {
- return labelStr, false
- }
- likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
- return labelStr, likelyProtos
- }
-
// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
- partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
- protoSrcPartition: bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup},
+ labels := bazel.LabelPartitions{
+ protoSrcPartition: android.ProtoSrcLabelPartition,
cSrcPartition: bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+ // TODO(http://b/231968910): If there is ever a filegroup target that
+ // contains .l or .ll files we will need to find a way to add a
+ // LabelMapper for these that identifies these filegroups and
+ // converts them appropriately
+ lSrcPartition: bazel.LabelPartition{Extensions: []string{".l"}},
+ llSrcPartition: bazel.LabelPartition{Extensions: []string{".ll"}},
// C++ is the "catch-all" group, and comprises generated sources because we don't
// know the language of these sources until the genrule is executed.
cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
- })
+ }
- return partitioned
+ return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
}
// bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
@@ -160,6 +149,7 @@
}
}
+// Parses properties common to static and shared libraries. Also used for prebuilt libraries.
func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
attrs := staticOrSharedAttributes{}
@@ -185,21 +175,17 @@
attrs.System_dynamic_deps.ForceSpecifyEmptyList = true
if isStatic {
- for axis, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
- for config, props := range configToProps {
- if staticOrSharedProps, ok := props.(*StaticProperties); ok {
- setAttrs(axis, config, staticOrSharedProps.Static)
- }
+ bp2BuildPropParseHelper(ctx, module, &StaticProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if staticOrSharedProps, ok := props.(*StaticProperties); ok {
+ setAttrs(axis, config, staticOrSharedProps.Static)
}
- }
+ })
} else {
- for axis, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
- for config, props := range configToProps {
- if staticOrSharedProps, ok := props.(*SharedProperties); ok {
- setAttrs(axis, config, staticOrSharedProps.Shared)
- }
+ bp2BuildPropParseHelper(ctx, module, &SharedProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if staticOrSharedProps, ok := props.(*SharedProperties); ok {
+ setAttrs(axis, config, staticOrSharedProps.Shared)
}
- }
+ })
}
partitionedSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
@@ -217,30 +203,72 @@
// Convenience struct to hold all attributes parsed from prebuilt properties.
type prebuiltAttributes struct {
- Src bazel.LabelAttribute
+ Src bazel.LabelAttribute
+ Enabled bazel.BoolAttribute
}
// NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
-func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
+func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) prebuiltAttributes {
+ manySourceFileError := func(axis bazel.ConfigurationAxis, config string) {
+ ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most one source file for %s %s\n", axis, config)
+ }
var srcLabelAttribute bazel.LabelAttribute
- for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
- for config, props := range configToProps {
- if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
- if len(prebuiltLinkerProperties.Srcs) > 1 {
- ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for %s %s\n", axis, config)
- continue
- } else if len(prebuiltLinkerProperties.Srcs) == 0 {
- continue
- }
- src := android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0])
- srcLabelAttribute.SetSelectValue(axis, config, src)
- }
+ parseSrcs := func(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, srcs []string) {
+ if len(srcs) > 1 {
+ manySourceFileError(axis, config)
+ return
+ } else if len(srcs) == 0 {
+ return
}
+ if srcLabelAttribute.SelectValue(axis, config) != nil {
+ manySourceFileError(axis, config)
+ return
+ }
+
+ src := android.BazelLabelForModuleSrcSingle(ctx, srcs[0])
+ srcLabelAttribute.SetSelectValue(axis, config, src)
+ }
+
+ bp2BuildPropParseHelper(ctx, module, &prebuiltLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
+ parseSrcs(ctx, axis, config, prebuiltLinkerProperties.Srcs)
+ }
+ })
+
+ var enabledLabelAttribute bazel.BoolAttribute
+ parseAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
+ if props.Enabled != nil {
+ enabledLabelAttribute.SetSelectValue(axis, config, props.Enabled)
+ }
+ parseSrcs(ctx, axis, config, props.Srcs)
+ }
+
+ if isStatic {
+ bp2BuildPropParseHelper(ctx, module, &StaticProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if staticProperties, ok := props.(*StaticProperties); ok {
+ parseAttrs(axis, config, staticProperties.Static)
+ }
+ })
+ } else {
+ bp2BuildPropParseHelper(ctx, module, &SharedProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if sharedProperties, ok := props.(*SharedProperties); ok {
+ parseAttrs(axis, config, sharedProperties.Shared)
+ }
+ })
}
return prebuiltAttributes{
- Src: srcLabelAttribute,
+ Src: srcLabelAttribute,
+ Enabled: enabledLabelAttribute,
+ }
+}
+
+func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
+ for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
+ for config, props := range configToProps {
+ parseFunc(axis, config, props)
+ }
}
}
@@ -265,6 +293,11 @@
cppFlags bazel.StringListAttribute
srcs bazel.LabelListAttribute
+ // Lex sources and options
+ lSrcs bazel.LabelListAttribute
+ llSrcs bazel.LabelListAttribute
+ lexopts bazel.StringListAttribute
+
hdrs bazel.LabelListAttribute
rtti bazel.BoolAttribute
@@ -335,21 +368,18 @@
}
func (ca *compilerAttributes) convertStlProps(ctx android.ArchVariantContext, module *Module) {
- stlPropsByArch := module.GetArchVariantProperties(ctx, &StlProperties{})
- for _, configToProps := range stlPropsByArch {
- for _, props := range configToProps {
- if stlProps, ok := props.(*StlProperties); ok {
- if stlProps.Stl == nil {
- continue
- }
- if ca.stl == nil {
- ca.stl = stlProps.Stl
- } else if ca.stl != stlProps.Stl {
- ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
- }
+ bp2BuildPropParseHelper(ctx, module, &StlProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if stlProps, ok := props.(*StlProperties); ok {
+ if stlProps.Stl == nil {
+ return
+ }
+ if ca.stl == nil {
+ ca.stl = stlProps.Stl
+ } else if ca.stl != stlProps.Stl {
+ ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
}
}
- }
+ })
}
func (ca *compilerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
@@ -390,6 +420,8 @@
ca.srcs = partitionedSrcs[cppSrcPartition]
ca.cSrcs = partitionedSrcs[cSrcPartition]
ca.asSrcs = partitionedSrcs[asSrcPartition]
+ ca.lSrcs = partitionedSrcs[lSrcPartition]
+ ca.llSrcs = partitionedSrcs[llSrcPartition]
ca.absoluteIncludes.DeduplicateAxesFromBase()
ca.localIncludes.DeduplicateAxesFromBase()
@@ -412,32 +444,33 @@
return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
}
-func bp2buildResolveCppStdValue(c_std *string, cpp_std *string, gnu_extensions *bool) (*string, *string) {
- var cStdVal, cppStdVal string
+func bp2buildStdVal(std *string, prefix string, useGnu bool) *string {
+ defaultVal := prefix + "_std_default"
// If c{,pp}std properties are not specified, don't generate them in the BUILD file.
// Defaults are handled by the toolchain definition.
// However, if gnu_extensions is false, then the default gnu-to-c version must be specified.
- if cpp_std != nil {
- cppStdVal = parseCppStd(cpp_std)
- } else if gnu_extensions != nil && !*gnu_extensions {
- cppStdVal = "c++17"
- }
- if c_std != nil {
- cStdVal = parseCStd(c_std)
- } else if gnu_extensions != nil && !*gnu_extensions {
- cStdVal = "c99"
+ stdVal := proptools.StringDefault(std, defaultVal)
+ if stdVal == "experimental" || stdVal == defaultVal {
+ if stdVal == "experimental" {
+ stdVal = prefix + "_std_experimental"
+ }
+ if !useGnu {
+ stdVal += "_no_gnu"
+ }
+ } else if !useGnu {
+ stdVal = gnuToCReplacer.Replace(stdVal)
}
- cStdVal, cppStdVal = maybeReplaceGnuToC(gnu_extensions, cStdVal, cppStdVal)
- var c_std_prop, cpp_std_prop *string
- if cStdVal != "" {
- c_std_prop = &cStdVal
+ if stdVal == defaultVal {
+ return nil
}
- if cppStdVal != "" {
- cpp_std_prop = &cppStdVal
- }
+ return &stdVal
+}
- return c_std_prop, cpp_std_prop
+func bp2buildResolveCppStdValue(c_std *string, cpp_std *string, gnu_extensions *bool) (*string, *string) {
+ useGnu := useGnuExtensions(gnu_extensions)
+
+ return bp2buildStdVal(c_std, "c", useGnu), bp2buildStdVal(cpp_std, "cpp", useGnu)
}
// packageFromLabel extracts package from a fully-qualified or relative Label and whether the label
@@ -498,7 +531,9 @@
var allHdrs []string
if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
allHdrs = baseCompilerProps.Generated_headers
-
+ if baseCompilerProps.Lex != nil {
+ compilerAttrs.lexopts.SetSelectValue(axis, config, baseCompilerProps.Lex.Flags)
+ }
(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
}
@@ -553,6 +588,10 @@
(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
+ convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
+ (&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
+ (&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName)
+
return baseAttributes{
compilerAttrs,
linkerAttrs,
@@ -560,6 +599,18 @@
}
}
+func bp2BuildParseSdkAttributes(module *Module) sdkAttributes {
+ return sdkAttributes{
+ Sdk_version: module.Properties.Sdk_version,
+ Min_sdk_version: module.Properties.Min_sdk_version,
+ }
+}
+
+type sdkAttributes struct {
+ Sdk_version *string
+ Min_sdk_version *string
+}
+
// Convenience struct to hold all attributes parsed from linker properties.
type linkerAttributes struct {
deps bazel.LabelListAttribute
@@ -592,9 +643,12 @@
// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
var axisFeatures []string
+ wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
+ la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
// Excludes to parallel Soong:
// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
- staticLibs := android.FirstUniqueStrings(props.Static_libs)
+ staticLibs := android.FirstUniqueStrings(android.RemoveListFromList(props.Static_libs, wholeStaticLibs))
+
staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, staticLibs, props.Exclude_static_libs, props.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
headerLibs := android.FirstUniqueStrings(props.Header_libs)
@@ -606,9 +660,6 @@
(&hDeps.implementation).Append(staticDeps.implementation)
la.implementationDeps.SetSelectValue(axis, config, hDeps.implementation)
- wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
- la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
-
systemSharedLibs := props.System_shared_libs
// systemSharedLibs distinguishes between nil/empty list behavior:
// nil -> use default values
@@ -644,7 +695,7 @@
var linkerFlags []string
if len(props.Ldflags) > 0 {
- linkerFlags = append(linkerFlags, props.Ldflags...)
+ linkerFlags = append(linkerFlags, proptools.NinjaEscapeList(props.Ldflags)...)
// binaries remove static flag if -shared is in the linker flags
if isBinary && android.InList("-shared", linkerFlags) {
axisFeatures = append(axisFeatures, "-static_flag")
@@ -655,6 +706,13 @@
la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
}
+
+ if props.Dynamic_list != nil {
+ label := android.BazelLabelForModuleSrcSingle(ctx, *props.Dynamic_list)
+ la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
+ linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--dynamic-list,$(location %s)", label.Label))
+ }
+
la.linkopts.SetSelectValue(axis, config, linkerFlags)
la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
@@ -677,17 +735,15 @@
}
func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
- for axis, configToProps := range module.GetArchVariantProperties(ctx, &StripProperties{}) {
- for config, props := range configToProps {
- if stripProperties, ok := props.(*StripProperties); ok {
- la.stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
- la.stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
- la.stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
- la.stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
- la.stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
- }
+ bp2BuildPropParseHelper(ctx, module, &StripProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if stripProperties, ok := props.(*StripProperties); ok {
+ la.stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
+ la.stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
+ la.stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
+ la.stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
+ la.stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
}
- }
+ })
}
func (la *linkerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
@@ -801,40 +857,23 @@
SystemIncludes bazel.StringListAttribute
}
-func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module, existingIncludes BazelIncludes) BazelIncludes {
- libraryDecorator := module.linker.(*libraryDecorator)
- return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator, &existingIncludes)
-}
-
-// Bp2buildParseExportedIncludesForPrebuiltLibrary returns a BazelIncludes with Bazel-ified values
-// to export includes from the underlying module's properties.
-func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
- prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
- libraryDecorator := prebuiltLibraryLinker.libraryDecorator
- return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator, nil)
-}
-
-// bp2BuildParseExportedIncludes creates a string list attribute contains the
-// exported included directories of a module.
-func bp2BuildParseExportedIncludesHelper(ctx android.BazelConversionPathContext, module *Module, libraryDecorator *libraryDecorator, includes *BazelIncludes) BazelIncludes {
+func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module, includes *BazelIncludes) BazelIncludes {
var exported BazelIncludes
if includes != nil {
exported = *includes
} else {
exported = BazelIncludes{}
}
- for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
- for config, props := range configToProps {
- if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
- if len(flagExporterProperties.Export_include_dirs) > 0 {
- exported.Includes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.Includes.SelectValue(axis, config), flagExporterProperties.Export_include_dirs...)))
- }
- if len(flagExporterProperties.Export_system_include_dirs) > 0 {
- exported.SystemIncludes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.SystemIncludes.SelectValue(axis, config), flagExporterProperties.Export_system_include_dirs...)))
- }
+ bp2BuildPropParseHelper(ctx, module, &FlagExporterProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+ if len(flagExporterProperties.Export_include_dirs) > 0 {
+ exported.Includes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.Includes.SelectValue(axis, config), flagExporterProperties.Export_include_dirs...)))
+ }
+ if len(flagExporterProperties.Export_system_include_dirs) > 0 {
+ exported.SystemIncludes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.SystemIncludes.SelectValue(axis, config), flagExporterProperties.Export_system_include_dirs...)))
}
}
- }
+ })
exported.AbsoluteIncludes.DeduplicateAxesFromBase()
exported.Includes.DeduplicateAxesFromBase()
exported.SystemIncludes.DeduplicateAxesFromBase()
@@ -844,10 +883,8 @@
func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
label := android.BazelModuleLabel(ctx, m)
- if aModule, ok := m.(android.Module); ok {
- if ctx.OtherModuleType(aModule) == "cc_library" && !android.GenerateCcLibraryStaticOnly(m.Name()) {
- label += "_bp2build_cc_library_static"
- }
+ if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+ label += "_bp2build_cc_library_static"
}
return label
}
@@ -904,22 +941,19 @@
func bp2buildBinaryLinkerProps(ctx android.BazelConversionPathContext, m *Module) binaryLinkerAttrs {
attrs := binaryLinkerAttrs{}
- archVariantProps := m.GetArchVariantProperties(ctx, &BinaryLinkerProperties{})
- for axis, configToProps := range archVariantProps {
- for _, p := range configToProps {
- props := p.(*BinaryLinkerProperties)
- staticExecutable := props.Static_executable
- if axis == bazel.NoConfigAxis {
- if linkBinaryShared := !proptools.Bool(staticExecutable); !linkBinaryShared {
- attrs.Linkshared = &linkBinaryShared
- }
- } else if staticExecutable != nil {
- // TODO(b/202876379): Static_executable is arch-variant; however, linkshared is a
- // nonconfigurable attribute. Only 4 AOSP modules use this feature, defer handling
- ctx.ModuleErrorf("bp2build cannot migrate a module with arch/target-specific static_executable values")
+ bp2BuildPropParseHelper(ctx, m, &BinaryLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ linkerProps := props.(*BinaryLinkerProperties)
+ staticExecutable := linkerProps.Static_executable
+ if axis == bazel.NoConfigAxis {
+ if linkBinaryShared := !proptools.Bool(staticExecutable); !linkBinaryShared {
+ attrs.Linkshared = &linkBinaryShared
}
+ } else if staticExecutable != nil {
+ // TODO(b/202876379): Static_executable is arch-variant; however, linkshared is a
+ // nonconfigurable attribute. Only 4 AOSP modules use this feature, defer handling
+ ctx.ModuleErrorf("bp2build cannot migrate a module with arch/target-specific static_executable values")
}
- }
+ })
return attrs
}
diff --git a/cc/builder.go b/cc/builder.go
index a5e5406..72f7d12 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -202,27 +202,22 @@
},
"clangBin", "format")
- // Rule for invoking clang-tidy (a clang-based linter).
+ // Rules for invoking clang-tidy (a clang-based linter).
clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- // Pick bash because some machines with old /bin/sh cannot handle arrays.
- // All $cFlags and $tidyFlags should have single quotes escaped.
- // Assume no single quotes in other parameters like $in, $out, $ccCmd.
- Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
- "rm -f $$TIDYF $${TIDYF}.d && " +
- "${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
- "-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
- "$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
- "-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
- CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
+ Command: "CLANG_CMD=$clangCmd TIDY_FILE=$out " +
+ "$tidyVars$reTemplate${config.ClangBin}/clang-tidy.sh $in $tidyFlags -- $cFlags",
+ CommandDeps: []string{"${config.ClangBin}/clang-tidy.sh", "$ccCmd", "$tidyCmd"},
},
&remoteexec.REParams{
Labels: map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
ExecStrategy: "${config.REClangTidyExecStrategy}",
Inputs: []string{"$in"},
- EnvironmentVariables: []string{"TIDY_TIMEOUT"},
+ OutputFiles: []string{"${out}", "${out}.d"},
+ ToolchainInputs: []string{"$ccCmd", "$tidyCmd"},
+ EnvironmentVariables: []string{"CLANG_CMD", "TIDY_FILE", "TIDY_TIMEOUT"},
// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
// under parallel compilation and RBE. So we assume no OutputFiles here.
// The clang-tidy fix option is best run locally in single thread.
@@ -230,7 +225,7 @@
// (1) New timestamps trigger clang and clang-tidy compilations again.
// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
- }, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
+ }, []string{"cFlags", "ccCmd", "clangCmd", "tidyCmd", "tidyFlags", "tidyVars"}, []string{})
_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -449,28 +444,26 @@
}
}
-func escapeSingleQuotes(s string) string {
- // Replace single quotes to work when embedded in a single quoted string for bash.
- // Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
- return strings.Replace(s, `'`, `'\''`, -1)
-}
-
// Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
+func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs android.Paths,
flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
objFiles := make(android.Paths, len(srcFiles))
var tidyFiles android.Paths
- noTidySrcsMap := make(map[android.Path]bool)
+ noTidySrcsMap := make(map[string]bool)
var tidyVars string
if flags.tidy {
tidyFiles = make(android.Paths, 0, len(srcFiles))
for _, path := range noTidySrcs {
- noTidySrcsMap[path] = true
+ noTidySrcsMap[path.String()] = true
}
tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
if len(tidyTimeout) > 0 {
- tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+ tidyVars += "TIDY_TIMEOUT=" + tidyTimeout + " "
+ // add timeoutTidySrcs into noTidySrcsMap if TIDY_TIMEOUT is set
+ for _, path := range timeoutTidySrcs {
+ noTidySrcsMap[path.String()] = true
+ }
}
}
var coverageFiles android.Paths
@@ -629,6 +622,7 @@
continue
}
+ // ccCmd is "clang" or "clang++"
ccDesc := ccCmd
ccCmd = "${config.ClangBin}/" + ccCmd
@@ -672,26 +666,33 @@
}
// Even with tidy, some src file could be skipped by noTidySrcsMap.
- if tidy && !noTidySrcsMap[srcFile] {
+ if tidy && !noTidySrcsMap[srcFile.String()] {
tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
tidyFiles = append(tidyFiles, tidyFile)
+ tidyCmd := "${config.ClangBin}/clang-tidy"
rule := clangTidy
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
rule = clangTidyRE
}
+ sharedCFlags := shareFlags("cFlags", moduleFlags)
+ srcRelPath := srcFile.Rel()
+
+ // Add the .tidy rule
ctx.Build(pctx, android.BuildParams{
Rule: rule,
- Description: "clang-tidy " + srcFile.Rel(),
+ Description: "clang-tidy " + srcRelPath,
Output: tidyFile,
Input: srcFile,
Implicits: cFlagsDeps,
OrderOnly: pathDeps,
Args: map[string]string{
+ "cFlags": sharedCFlags,
"ccCmd": ccCmd,
- "cFlags": shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
- "tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+ "clangCmd": ccDesc,
+ "tidyCmd": tidyCmd,
+ "tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
"tidyVars": tidyVars, // short and not shared
},
})
@@ -917,9 +918,10 @@
return outputFile
}
-// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.ldump).
+// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
func sourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
- baseName, exportedHeaderFlags string, checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
+ baseName, exportedHeaderFlags string, diffFlags []string,
+ checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
@@ -950,6 +952,8 @@
if isVndkExt {
extraFlags = append(extraFlags, "-allow-extensions")
}
+ // TODO(b/232891473): Simplify the above logic with diffFlags.
+ extraFlags = append(extraFlags, diffFlags...)
ctx.Build(pctx, android.BuildParams{
Rule: sAbiDiff,
diff --git a/cc/cc.go b/cc/cc.go
index 31babc2..da8a807 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -48,7 +48,6 @@
ctx.BottomUp("vndk", VndkMutator).Parallel()
ctx.BottomUp("link", LinkageMutator).Parallel()
ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
- ctx.BottomUp("version_selector", versionSelectorMutator).Parallel()
ctx.BottomUp("version", versionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel()
ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel()
@@ -505,6 +504,7 @@
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
+ isAfdoCompile() bool
isPgoCompile() bool
isNDKStubLibrary() bool
useClangLld(actx ModuleContext) bool
@@ -745,6 +745,7 @@
runtimeDepTag = installDependencyTag{name: "runtime lib"}
testPerSrcDepTag = dependencyTag{name: "test_per_src"}
stubImplDepTag = dependencyTag{name: "stub_impl"}
+ JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"}
)
func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -771,6 +772,19 @@
return ok && ccDepTag == testPerSrcDepTag
}
+// bazelHandler is the interface for a helper object related to deferring to Bazel for
+// processing a cc module (during Bazel mixed builds). Individual module types should define
+// their own bazel handler if they support being handled by Bazel.
+type BazelHandler interface {
+ // QueueBazelCall invokes request-queueing functions on the BazelContext
+ //so that these requests are handled when Bazel's cquery is invoked.
+ QueueBazelCall(ctx android.BaseModuleContext, label string)
+
+ // ProcessBazelQueryResponse uses information retrieved from Bazel to set properties
+ // on the current module with given label.
+ ProcessBazelQueryResponse(ctx android.ModuleContext, label string)
+}
+
// Module contains the properties and members used by all C/C++ module types, and implements
// the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces
// to construct the output file. Behavior can be customized with a Customizer, or "decorator",
@@ -810,7 +824,7 @@
compiler compiler
linker linker
installer installer
- bazelHandler android.BazelHandler
+ bazelHandler BazelHandler
features []feature
stl *stl
@@ -1217,6 +1231,17 @@
return c.VendorProperties.IsVendorPublicLibrary
}
+func (c *Module) IsVndkPrebuiltLibrary() bool {
+ if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
+ return true
+ }
+ return false
+}
+
+func (c *Module) SdkAndPlatformVariantVisibleToMake() bool {
+ return c.Properties.SdkAndPlatformVariantVisibleToMake
+}
+
func (c *Module) HasLlndkStubs() bool {
lib := moduleLibraryInterface(c)
return lib != nil && lib.hasLLNDKStubs()
@@ -1259,6 +1284,13 @@
return false
}
+func (c *Module) isAfdoCompile() bool {
+ if afdo := c.afdo; afdo != nil {
+ return afdo.Properties.AfdoTarget != nil
+ }
+ return false
+}
+
func (c *Module) isPgoCompile() bool {
if pgo := c.pgo; pgo != nil {
return pgo.Properties.PgoCompile
@@ -1375,7 +1407,7 @@
}
func InstallToBootstrap(name string, config android.Config) bool {
- if name == "libclang_rt.hwasan-aarch64-android" {
+ if name == "libclang_rt.hwasan" {
return true
}
return isBionic(name)
@@ -1536,6 +1568,10 @@
return ctx.mod.IsVndk()
}
+func (ctx *moduleContextImpl) isAfdoCompile() bool {
+ return ctx.mod.isAfdoCompile()
+}
+
func (ctx *moduleContextImpl) isPgoCompile() bool {
return ctx.mod.isPgoCompile()
}
@@ -1687,7 +1723,7 @@
return nil
}
-func (c *Module) getNameSuffixWithVndkVersion(ctx android.ModuleContext) string {
+func getNameSuffixWithVndkVersion(ctx android.ModuleContext, c LinkableInterface) string {
// Returns the name suffix for product and vendor variants. If the VNDK version is not
// "current", it will append the VNDK version to the name suffix.
var vndkVersion string
@@ -1707,19 +1743,19 @@
if vndkVersion == "current" {
vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
}
- if c.Properties.VndkVersion != vndkVersion && c.Properties.VndkVersion != "" {
+ if c.VndkVersion() != vndkVersion && c.VndkVersion() != "" {
// add version suffix only if the module is using different vndk version than the
// version in product or vendor partition.
- nameSuffix += "." + c.Properties.VndkVersion
+ nameSuffix += "." + c.VndkVersion()
}
return nameSuffix
}
-func (c *Module) setSubnameProperty(actx android.ModuleContext) {
- c.Properties.SubName = ""
+func GetSubnameProperty(actx android.ModuleContext, c LinkableInterface) string {
+ var subName = ""
if c.Target().NativeBridge == android.NativeBridgeEnabled {
- c.Properties.SubName += NativeBridgeSuffix
+ subName += NativeBridgeSuffix
}
llndk := c.IsLlndk()
@@ -1727,51 +1763,74 @@
// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
// added for product variant only when we have vendor and product variants with core
// variant. The suffix is not added for vendor-only or product-only module.
- c.Properties.SubName += c.getNameSuffixWithVndkVersion(actx)
+ subName += getNameSuffixWithVndkVersion(actx, c)
} else if c.IsVendorPublicLibrary() {
- c.Properties.SubName += vendorPublicLibrarySuffix
- } else if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
+ subName += vendorPublicLibrarySuffix
+ } else if c.IsVndkPrebuiltLibrary() {
// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
- c.Properties.SubName += VendorSuffix
+ subName += VendorSuffix
} else if c.InRamdisk() && !c.OnlyInRamdisk() {
- c.Properties.SubName += RamdiskSuffix
+ subName += RamdiskSuffix
} else if c.InVendorRamdisk() && !c.OnlyInVendorRamdisk() {
- c.Properties.SubName += VendorRamdiskSuffix
+ subName += VendorRamdiskSuffix
} else if c.InRecovery() && !c.OnlyInRecovery() {
- c.Properties.SubName += RecoverySuffix
- } else if c.IsSdkVariant() && (c.Properties.SdkAndPlatformVariantVisibleToMake || c.SplitPerApiLevel()) {
- c.Properties.SubName += sdkSuffix
+ subName += RecoverySuffix
+ } else if c.IsSdkVariant() && (c.SdkAndPlatformVariantVisibleToMake() || c.SplitPerApiLevel()) {
+ subName += sdkSuffix
if c.SplitPerApiLevel() {
- c.Properties.SubName += "." + c.SdkVersion()
+ subName += "." + c.SdkVersion()
}
}
+
+ return subName
}
-// Returns true if Bazel was successfully used for the analysis of this module.
-func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
- var bazelModuleLabel string
- if actx.ModuleType() == "cc_library" && c.static() {
+var _ android.MixedBuildBuildable = (*Module)(nil)
+
+func (c *Module) getBazelModuleLabel(ctx android.BaseModuleContext) string {
+ if c.typ() == fullLibrary && c.static() {
// cc_library is a special case in bp2build; two targets are generated -- one for each
// of the shared and static variants. The shared variant keeps the module name, but the
// static variant uses a different suffixed name.
- bazelModuleLabel = bazelLabelForStaticModule(actx, c)
- } else {
- bazelModuleLabel = c.GetBazelLabel(actx, c)
+ return bazelLabelForStaticModule(ctx, c)
}
- bazelActionsUsed := false
- // Mixed builds mode is disabled for modules outside of device OS.
- // TODO(b/200841190): Support non-device OS in mixed builds.
- if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil {
- bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel)
+ return c.GetBazelLabel(ctx, c)
+}
+
+func (c *Module) QueueBazelCall(ctx android.BaseModuleContext) {
+ c.bazelHandler.QueueBazelCall(ctx, c.getBazelModuleLabel(ctx))
+}
+
+func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ return c.bazelHandler != nil
+}
+
+func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+ bazelModuleLabel := c.getBazelModuleLabel(ctx)
+
+ c.bazelHandler.ProcessBazelQueryResponse(ctx, bazelModuleLabel)
+
+ c.Properties.SubName = GetSubnameProperty(ctx, c)
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ c.hideApexVariantFromMake = true
}
- return bazelActionsUsed
+
+ c.makeLinkType = GetMakeLinkType(ctx, c)
+
+ mctx := &moduleContext{
+ ModuleContext: ctx,
+ moduleContextImpl: moduleContextImpl{
+ mod: c,
+ },
+ }
+ mctx.ctx = mctx
+
+ c.maybeInstall(mctx, apexInfo)
}
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
- // TODO(cparsons): Any logic in this method occurring prior to querying Bazel should be
- // requested from Bazel instead.
-
// Handle the case of a test module split by `test_per_src` mutator.
//
// The `test_per_src` mutator adds an extra variation named "", depending on all the other
@@ -1782,7 +1841,7 @@
return
}
- c.setSubnameProperty(actx)
+ c.Properties.SubName = GetSubnameProperty(actx, c)
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !apexInfo.IsForPlatform() {
c.hideApexVariantFromMake = true
@@ -1798,11 +1857,6 @@
}
ctx.ctx = ctx
- if c.maybeGenerateBazelActions(actx) {
- c.maybeInstall(ctx, apexInfo)
- return
- }
-
deps := c.depsToPaths(ctx)
if ctx.Failed() {
return
@@ -2005,12 +2059,6 @@
deps.HeaderLibs = android.LastUniqueStrings(deps.HeaderLibs)
deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
- // In Bazel conversion mode, we dependency and build validations will occur in Bazel, so there is
- // no need to do so in Soong.
- if ctx.BazelConversionMode() {
- return deps
- }
-
for _, lib := range deps.ReexportSharedLibHeaders {
if !inList(lib, deps.SharedLibs) {
ctx.PropertyErrorf("export_shared_lib_headers", "Shared library not in shared_libs: '%s'", lib)
@@ -2090,7 +2138,7 @@
variations = append([]blueprint.Variation(nil), variations...)
- if version != "" && CanBeOrLinkAgainstVersionVariants(mod) {
+ if version != "" && canBeOrLinkAgainstVersionVariants(mod) {
// Version is explicitly specified. i.e. libFoo#30
variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
if tag, ok := depTag.(libraryDependencyTag); ok {
@@ -2794,6 +2842,8 @@
// dependency.
depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
}
+ depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
+ staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
} else {
switch libDepTag.Order {
case earlyLibraryDependency:
@@ -3464,19 +3514,33 @@
return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
}
+// Overrides android.ApexModuleBase.UniqueApexVariations
+func (c *Module) UniqueApexVariations() bool {
+ // When a vendor APEX needs a VNDK lib in it (use_vndk_as_stable: false), it should be a unique
+ // APEX variation. Otherwise, another vendor APEX with use_vndk_as_stable:true may use a wrong
+ // variation of the VNDK lib because APEX variations are merged/grouped.
+ return c.UseVndk() && c.IsVndk()
+}
+
var _ snapshot.RelativeInstallPath = (*Module)(nil)
-// ConvertWithBp2build converts Module to Bazel for bp2build.
-func (c *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- prebuilt := c.IsPrebuilt()
+type moduleType int
+
+const (
+ unknownType moduleType = iota
+ binary
+ object
+ fullLibrary
+ staticLibrary
+ sharedLibrary
+ headerLibrary
+)
+
+func (c *Module) typ() moduleType {
if c.Binary() {
- if !prebuilt {
- binaryBp2build(ctx, c, ctx.ModuleType())
- }
+ return binary
} else if c.Object() {
- if !prebuilt {
- objectBp2Build(ctx, c)
- }
+ return object
} else if c.CcLibrary() {
static := false
shared := false
@@ -3487,25 +3551,49 @@
static = library.MutatedProperties.BuildStatic
shared = library.MutatedProperties.BuildShared
}
-
if static && shared {
- if !prebuilt {
- libraryBp2Build(ctx, c)
- }
+ return fullLibrary
} else if !static && !shared {
- libraryHeadersBp2Build(ctx, c)
+ return headerLibrary
} else if static {
- if prebuilt {
- prebuiltLibraryStaticBp2Build(ctx, c)
- } else {
- sharedOrStaticLibraryBp2Build(ctx, c, true)
- }
- } else if shared {
- if prebuilt {
- prebuiltLibrarySharedBp2Build(ctx, c)
- } else {
- sharedOrStaticLibraryBp2Build(ctx, c, false)
- }
+ return staticLibrary
+ }
+ return sharedLibrary
+ }
+ return unknownType
+}
+
+// ConvertWithBp2build converts Module to Bazel for bp2build.
+func (c *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ prebuilt := c.IsPrebuilt()
+ switch c.typ() {
+ case binary:
+ if !prebuilt {
+ binaryBp2build(ctx, c, ctx.ModuleType())
+ }
+ case object:
+ if !prebuilt {
+ objectBp2Build(ctx, c)
+ }
+ case fullLibrary:
+ if !prebuilt {
+ libraryBp2Build(ctx, c)
+ } else {
+ prebuiltLibraryBp2Build(ctx, c)
+ }
+ case headerLibrary:
+ libraryHeadersBp2Build(ctx, c)
+ case staticLibrary:
+ if prebuilt {
+ prebuiltLibraryStaticBp2Build(ctx, c, false)
+ } else {
+ sharedOrStaticLibraryBp2Build(ctx, c, true)
+ }
+ case sharedLibrary:
+ if prebuilt {
+ prebuiltLibrarySharedBp2Build(ctx, c)
+ } else {
+ sharedOrStaticLibraryBp2Build(ctx, c, false)
}
}
}
@@ -3543,7 +3631,8 @@
&SharedProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
- &TestProperties{},
+ &TestLinkerProperties{},
+ &TestInstallerProperties{},
&TestBinaryProperties{},
&BenchmarkProperties{},
&fuzz.FuzzProperties{},
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 31d91b6..fb24624 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -779,6 +779,68 @@
}
}
+func TestTestBinaryTestSuites(t *testing.T) {
+ bp := `
+ cc_test {
+ name: "main_test",
+ srcs: ["main_test.cpp"],
+ test_suites: [
+ "suite_1",
+ "suite_2",
+ ],
+ gtest: false,
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
+ module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
+ if len(compatEntries) != 2 {
+ t.Errorf("expected two elements in LOCAL_COMPATIBILITY_SUITE. got %d", len(compatEntries))
+ }
+ if compatEntries[0] != "suite_1" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_1`,"+
+ " but was '%s'", compatEntries[0])
+ }
+ if compatEntries[1] != "suite_2" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_2`,"+
+ " but was '%s'", compatEntries[1])
+ }
+}
+
+func TestTestLibraryTestSuites(t *testing.T) {
+ bp := `
+ cc_test_library {
+ name: "main_test_lib",
+ srcs: ["main_test_lib.cpp"],
+ test_suites: [
+ "suite_1",
+ "suite_2",
+ ],
+ gtest: false,
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
+ module := ctx.ModuleForTests("main_test_lib", "android_arm_armv7-a-neon_shared").Module()
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
+ if len(compatEntries) != 2 {
+ t.Errorf("expected two elements in LOCAL_COMPATIBILITY_SUITE. got %d", len(compatEntries))
+ }
+ if compatEntries[0] != "suite_1" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_1`,"+
+ " but was '%s'", compatEntries[0])
+ }
+ if compatEntries[1] != "suite_2" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_2`,"+
+ " but was '%s'", compatEntries[1])
+ }
+}
+
func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
ctx := testCcNoVndk(t, `
cc_library {
@@ -2944,13 +3006,13 @@
// Check the shared version of lib2.
variant := "android_arm64_armv8-a_shared"
module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
- checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins"}, module)
// Check the static version of lib2.
variant = "android_arm64_armv8-a_static"
module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
// libc++_static is linked additionally.
- checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
}
var compilerFlagsTestCases = []struct {
@@ -3943,7 +4005,6 @@
"${config.ArmGenericCflags}",
"-target",
"armv7a-linux-androideabi20",
- "-B${config.ArmGccRoot}/arm-linux-androideabi/bin",
}
expectedIncludes := []string{
@@ -3980,8 +4041,8 @@
conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
cppOnly := []string{"-fPIC", "${config.CommonGlobalCppflags}", "${config.DeviceGlobalCppflags}", "${config.ArmCppflags}"}
- cflags := []string{"-Wall", "-Werror", "-std=candcpp"}
- cstd := []string{"-std=gnu99", "-std=conly"}
+ cflags := []string{"-Werror", "-std=candcpp"}
+ cstd := []string{"-std=gnu11", "-std=conly"}
cppstd := []string{"-std=gnu++17", "-std=cpp", "-fno-rtti"}
lastIncludes := []string{
diff --git a/cc/check.go b/cc/check.go
index a357a97..3d290a9 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -87,6 +87,8 @@
ctx.PropertyErrorf(prop, "Bad flag: `%s` is not allowed", flag)
} else if strings.HasPrefix(flag, "-Wl,--version-script") {
ctx.PropertyErrorf(prop, "Bad flag: `%s`, use version_script instead", flag)
+ } else if flag == "-T" || strings.HasPrefix(flag, "--script") {
+ ctx.PropertyErrorf(prop, "Bad flag: `%s`, use linker_scripts instead", flag)
} else if flag == "--coverage" {
ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
} else if strings.Contains(flag, " ") {
diff --git a/cc/compiler.go b/cc/compiler.go
index 9dbf2d1..c7e9c9a 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -42,6 +42,9 @@
// list of source files that should not be compiled with clang-tidy.
Tidy_disabled_srcs []string `android:"path,arch_variant"`
+ // list of source files that should not be compiled by clang-tidy when TIDY_TIMEOUT is set.
+ Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
// list of source files that should not be used to build the C/C++ module.
// This is most useful in the arch/multilib variants to remove non-common files
Exclude_srcs []string `android:"path,arch_variant"`
@@ -292,8 +295,12 @@
getNamedMapForConfig(ctx.Config(), key).Store(module, true)
}
+func useGnuExtensions(gnuExtensions *bool) bool {
+ return proptools.BoolDefault(gnuExtensions, true)
+}
+
func maybeReplaceGnuToC(gnuExtensions *bool, cStd string, cppStd string) (string, string) {
- if gnuExtensions != nil && *gnuExtensions == false {
+ if !useGnuExtensions(gnuExtensions) {
cStd = gnuToCReplacer.Replace(cStd)
cppStd = gnuToCReplacer.Replace(cppStd)
}
@@ -403,6 +410,10 @@
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RECOVERY__")
}
+ if ctx.inRecovery() || ctx.inRamdisk() || ctx.inVendorRamdisk() {
+ flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RAMDISK__")
+ }
+
if ctx.apexVariationName() != "" {
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
if ctx.Device() {
@@ -450,11 +461,9 @@
}
}
- gccPrefix := "-B" + config.ToolPath(tc)
-
- flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
- flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
- flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
+ flags.Global.CFlags = append(flags.Global.CFlags, target)
+ flags.Global.AsFlags = append(flags.Global.AsFlags, target)
+ flags.Global.LdFlags = append(flags.Global.LdFlags, target)
hod := "Host"
if ctx.Os().Class == android.Device {
@@ -584,10 +593,9 @@
addToModuleList(ctx, modulesUsingWnoErrorKey, module)
} else if !inList("-Werror", flags.Local.CFlags) && !inList("-Werror", flags.Local.CppFlags) {
if warningsAreAllowed(ctx.ModuleDir()) {
- addToModuleList(ctx, modulesAddedWallKey, module)
- flags.Local.CFlags = append([]string{"-Wall"}, flags.Local.CFlags...)
+ addToModuleList(ctx, modulesWarningsAllowedKey, module)
} else {
- flags.Local.CFlags = append([]string{"-Wall", "-Werror"}, flags.Local.CFlags...)
+ flags.Local.CFlags = append([]string{"-Werror"}, flags.Local.CFlags...)
}
}
}
@@ -665,6 +673,7 @@
// Compile files listed in c.Properties.Srcs into objects
objs := compileObjs(ctx, buildFlags, "", srcs,
android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
pathDeps, compiler.cFlagsDeps)
if ctx.Failed() {
@@ -676,9 +685,9 @@
// Compile a list of source files into objects a specified subdirectory
func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
- srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+ srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
- return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
+ return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps)
}
// Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 7b7ee28..1a21c13 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -8,9 +8,9 @@
deps: [
"soong-android",
"soong-remoteexec",
+ "soong-starlark-format",
],
srcs: [
- "bp2build.go",
"clang.go",
"global.go",
"tidy.go",
@@ -32,7 +32,6 @@
"arm64_linux_host.go",
],
testSrcs: [
- "bp2build_test.go",
"tidy_test.go",
],
}
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 2d6bcb8..66087e6 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -96,25 +96,28 @@
pctx.SourcePathVariable("Arm64GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
- exportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
- exportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
+ exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
+ exportedVars.ExportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
- exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
- exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
+ exportedVars.ExportStringListStaticVariable("Arm64Cflags", arm64Cflags)
+ exportedVars.ExportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
- exportedStringListDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflags)
- exportedStringListDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflags)
+ exportedVars.ExportVariableReferenceDict("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar)
+ exportedVars.ExportVariableReferenceDict("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar)
+ exportedVars.ExportVariableReferenceDict("Arm64CpuVariantLdflags", arm64CpuVariantLdflags)
- pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
- pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
- pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
- pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
+ exportedVars.ExportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"])
+ exportedVars.ExportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
+ exportedVars.ExportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
+ exportedVars.ExportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
- pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
- pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
- pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
- pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
- pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
+ exportedVars.ExportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
+ exportedVars.ExportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
+ exportedVars.ExportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"])
+ exportedVars.ExportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"])
+ exportedVars.ExportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"])
+
+ exportedVars.ExportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"})
}
var (
@@ -126,7 +129,6 @@
}
arm64CpuVariantCflagsVar = map[string]string{
- "": "",
"cortex-a53": "${config.Arm64CortexA53Cflags}",
"cortex-a55": "${config.Arm64CortexA55Cflags}",
"cortex-a72": "${config.Arm64CortexA53Cflags}",
@@ -138,6 +140,15 @@
"exynos-m1": "${config.Arm64ExynosM1Cflags}",
"exynos-m2": "${config.Arm64ExynosM2Cflags}",
}
+
+ arm64CpuVariantLdflags = map[string]string{
+ "cortex-a53": "${config.Arm64FixCortexA53Ldflags}",
+ "cortex-a72": "${config.Arm64FixCortexA53Ldflags}",
+ "cortex-a73": "${config.Arm64FixCortexA53Ldflags}",
+ "kryo": "${config.Arm64FixCortexA53Ldflags}",
+ "exynos-m1": "${config.Arm64FixCortexA53Ldflags}",
+ "exynos-m2": "${config.Arm64FixCortexA53Ldflags}",
+ }
)
type toolchainArm64 struct {
@@ -212,12 +223,7 @@
toolchainCflags = append(toolchainCflags,
variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant))
- var extraLdflags string
- switch arch.CpuVariant {
- case "cortex-a53", "cortex-a72", "cortex-a73", "kryo", "exynos-m1", "exynos-m2":
- extraLdflags = "-Wl,--fix-cortex-a53-843419"
- }
-
+ extraLdflags := variantOrDefault(arm64CpuVariantLdflags, arch.CpuVariant)
return &toolchainArm64{
ldflags: strings.Join([]string{
"${config.Arm64Ldflags}",
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 853d818..5c7f926 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -41,7 +41,6 @@
"-Wl,-z,relro",
"-Wl,-z,now",
"-Wl,--build-id=md5",
- "-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--hash-style=gnu",
"-Wl,--no-undefined-version",
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 0fe5e68..d702c61 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -39,6 +39,10 @@
armLldflags = armLdflags
+ armFixCortexA8LdFlags = []string{"-Wl,--fix-cortex-a8"}
+
+ armNoFixCortexA8LdFlags = []string{"-Wl,--no-fix-cortex-a8"}
+
armArmCflags = []string{
"-fstrict-aliasing",
}
@@ -174,38 +178,41 @@
pctx.SourcePathVariable("ArmGccRoot", "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
// Just exported. Not created as a Ninja static variable.
- exportedStringVars.Set("ArmClangTriple", clangTriple)
+ exportedVars.ExportString("ArmClangTriple", clangTriple)
- exportStringListStaticVariable("ArmLdflags", armLdflags)
- exportStringListStaticVariable("ArmLldflags", armLldflags)
+ exportedVars.ExportStringListStaticVariable("ArmLdflags", armLdflags)
+ exportedVars.ExportStringListStaticVariable("ArmLldflags", armLldflags)
+
+ exportedVars.ExportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
+ exportedVars.ExportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
// Clang cflags
- exportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
- exportStringListStaticVariable("ArmCflags", armCflags)
- exportStringListStaticVariable("ArmCppflags", armCppflags)
+ exportedVars.ExportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
+ exportedVars.ExportStringListStaticVariable("ArmCflags", armCflags)
+ exportedVars.ExportStringListStaticVariable("ArmCppflags", armCppflags)
// Clang ARM vs. Thumb instruction set cflags
- exportStringListStaticVariable("ArmArmCflags", armArmCflags)
- exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
+ exportedVars.ExportStringListStaticVariable("ArmArmCflags", armArmCflags)
+ exportedVars.ExportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
- exportedStringListDictVars.Set("ArmArchVariantCflags", armArchVariantCflags)
- exportedStringListDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflags)
+ exportedVars.ExportVariableReferenceDict("ArmArchVariantCflags", armArchVariantCflagsVar)
+ exportedVars.ExportVariableReferenceDict("ArmCpuVariantCflags", armCpuVariantCflagsVar)
// Clang arch variant cflags
- exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
- exportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
- exportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"])
- exportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"])
+ exportedVars.ExportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
+ exportedVars.ExportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
+ exportedVars.ExportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"])
+ exportedVars.ExportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"])
// Clang cpu variant cflags
- exportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""])
- exportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
- exportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
- exportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
- exportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
- exportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
- exportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
- exportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"])
+ exportedVars.ExportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""])
+ exportedVars.ExportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
+ exportedVars.ExportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
+ exportedVars.ExportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
+ exportedVars.ExportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
+ exportedVars.ExportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
+ exportedVars.ExportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
+ exportedVars.ExportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"])
}
var (
@@ -324,12 +331,12 @@
switch arch.CpuVariant {
case "cortex-a8", "":
// Generic ARM might be a Cortex A8 -- better safe than sorry
- fixCortexA8 = "-Wl,--fix-cortex-a8"
+ fixCortexA8 = "${config.ArmFixCortexA8LdFlags}"
default:
- fixCortexA8 = "-Wl,--no-fix-cortex-a8"
+ fixCortexA8 = "${config.ArmNoFixCortexA8LdFlags}"
}
case "armv7-a":
- fixCortexA8 = "-Wl,--fix-cortex-a8"
+ fixCortexA8 = "${config.ArmFixCortexA8LdFlags}"
case "armv8-a", "armv8-2a":
// Nothing extra for armv8-a/armv8-2a
default:
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
deleted file mode 100644
index 982b436..0000000
--- a/cc/config/bp2build.go
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
- "fmt"
- "reflect"
- "regexp"
- "sort"
- "strings"
-
- "android/soong/android"
-
- "github.com/google/blueprint"
-)
-
-const (
- bazelIndent = 4
-)
-
-type bazelVarExporter interface {
- asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
-}
-
-// Helpers for exporting cc configuration information to Bazel.
-var (
- // Maps containing toolchain variables that are independent of the
- // environment variables of the build.
- exportedStringListVars = exportedStringListVariables{}
- exportedStringVars = exportedStringVariables{}
- exportedStringListDictVars = exportedStringListDictVariables{}
-
- /// Maps containing variables that are dependent on the build config.
- exportedConfigDependingVars = exportedConfigDependingVariables{}
-)
-
-type exportedConfigDependingVariables map[string]interface{}
-
-func (m exportedConfigDependingVariables) Set(k string, v interface{}) {
- m[k] = v
-}
-
-// Ensure that string s has no invalid characters to be generated into the bzl file.
-func validateCharacters(s string) string {
- for _, c := range []string{`\n`, `"`, `\`} {
- if strings.Contains(s, c) {
- panic(fmt.Errorf("%s contains illegal character %s", s, c))
- }
- }
- return s
-}
-
-type bazelConstant struct {
- variableName string
- internalDefinition string
-}
-
-type exportedStringVariables map[string]string
-
-func (m exportedStringVariables) Set(k string, v string) {
- m[k] = v
-}
-
-func bazelIndention(level int) string {
- return strings.Repeat(" ", level*bazelIndent)
-}
-
-func printBazelList(items []string, indentLevel int) string {
- list := make([]string, 0, len(items)+2)
- list = append(list, "[")
- innerIndent := bazelIndention(indentLevel + 1)
- for _, item := range items {
- list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
- }
- list = append(list, bazelIndention(indentLevel)+"]")
- return strings.Join(list, "\n")
-}
-
-func (m exportedStringVariables) asBazel(config android.Config,
- stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
- ret := make([]bazelConstant, 0, len(m))
- for k, variableValue := range m {
- expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
- if err != nil {
- panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
- }
- if len(expandedVar) > 1 {
- panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
- }
- ret = append(ret, bazelConstant{
- variableName: k,
- internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
- })
- }
- return ret
-}
-
-// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
-func exportStringStaticVariable(name string, value string) {
- pctx.StaticVariable(name, value)
- exportedStringVars.Set(name, value)
-}
-
-type exportedStringListVariables map[string][]string
-
-func (m exportedStringListVariables) Set(k string, v []string) {
- m[k] = v
-}
-
-func (m exportedStringListVariables) asBazel(config android.Config,
- stringScope exportedStringVariables, stringListScope exportedStringListVariables,
- exportedVars exportedConfigDependingVariables) []bazelConstant {
- ret := make([]bazelConstant, 0, len(m))
- // For each exported variable, recursively expand elements in the variableValue
- // list to ensure that interpolated variables are expanded according to their values
- // in the variable scope.
- for k, variableValue := range m {
- var expandedVars []string
- for _, v := range variableValue {
- expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
- if err != nil {
- panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
- }
- expandedVars = append(expandedVars, expandedVar...)
- }
- // Assign the list as a bzl-private variable; this variable will be exported
- // out through a constants struct later.
- ret = append(ret, bazelConstant{
- variableName: k,
- internalDefinition: printBazelList(expandedVars, 0),
- })
- }
- return ret
-}
-
-// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
-func exportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
- exportedConfigDependingVars.Set(name, method)
- return pctx.VariableConfigMethod(name, method)
-}
-
-// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
-func exportSourcePathVariable(name string, value string) {
- pctx.SourcePathVariable(name, value)
- exportedStringVars.Set(name, value)
-}
-
-// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
-func exportStringListStaticVariable(name string, value []string) {
- pctx.StaticVariable(name, strings.Join(value, " "))
- exportedStringListVars.Set(name, value)
-}
-
-func ExportStringList(name string, value []string) {
- exportedStringListVars.Set(name, value)
-}
-
-type exportedStringListDictVariables map[string]map[string][]string
-
-func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
- m[k] = v
-}
-
-func printBazelStringListDict(dict map[string][]string) string {
- bazelDict := make([]string, 0, len(dict)+2)
- bazelDict = append(bazelDict, "{")
- for k, v := range dict {
- bazelDict = append(bazelDict,
- fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
- }
- bazelDict = append(bazelDict, "}")
- return strings.Join(bazelDict, "\n")
-}
-
-// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
-func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
- _ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
- ret := make([]bazelConstant, 0, len(m))
- for k, dict := range m {
- ret = append(ret, bazelConstant{
- variableName: k,
- internalDefinition: printBazelStringListDict(dict),
- })
- }
- return ret
-}
-
-// BazelCcToolchainVars generates bzl file content containing variables for
-// Bazel's cc_toolchain configuration.
-func BazelCcToolchainVars(config android.Config) string {
- return bazelToolchainVars(
- config,
- exportedStringListDictVars,
- exportedStringListVars,
- exportedStringVars)
-}
-
-func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
- ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
-
- results := []bazelConstant{}
- for _, v := range vars {
- results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
- }
-
- sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
-
- definitions := make([]string, 0, len(results))
- constants := make([]string, 0, len(results))
- for _, b := range results {
- definitions = append(definitions,
- fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
- constants = append(constants,
- fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
- }
-
- // Build the exported constants struct.
- ret += strings.Join(definitions, "\n\n")
- ret += "\n\n"
- ret += "constants = struct(\n"
- ret += strings.Join(constants, "\n")
- ret += "\n)"
-
- return ret
-}
-
-// expandVar recursively expand interpolated variables in the exportedVars scope.
-//
-// We're using a string slice to track the seen variables to avoid
-// stackoverflow errors with infinite recursion. it's simpler to use a
-// string slice than to handle a pass-by-referenced map, which would make it
-// quite complex to track depth-first interpolations. It's also unlikely the
-// interpolation stacks are deep (n > 1).
-func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
- stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
- // e.g. "${ExternalCflags}"
- r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
-
- // Internal recursive function.
- var expandVarInternal func(string, map[string]bool) (string, error)
- expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
- var ret string
- remainingString := toExpand
- for len(remainingString) > 0 {
- matches := r.FindStringSubmatch(remainingString)
- if len(matches) == 0 {
- return ret + remainingString, nil
- }
- if len(matches) != 2 {
- panic(fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", remainingString, len(matches)-1))
- }
- matchIndex := strings.Index(remainingString, matches[0])
- ret += remainingString[:matchIndex]
- remainingString = remainingString[matchIndex+len(matches[0]):]
-
- // Index 1 of FindStringSubmatch contains the subexpression match
- // (variable name) of the capture group.
- variable := matches[1]
- // toExpand contains a variable.
- if _, ok := seenVars[variable]; ok {
- return ret, fmt.Errorf(
- "Unbounded recursive interpolation of variable: %s", variable)
- }
- // A map is passed-by-reference. Create a new map for
- // this scope to prevent variables seen in one depth-first expansion
- // to be also treated as "seen" in other depth-first traversals.
- newSeenVars := map[string]bool{}
- for k := range seenVars {
- newSeenVars[k] = true
- }
- newSeenVars[variable] = true
- if unexpandedVars, ok := stringListScope[variable]; ok {
- expandedVars := []string{}
- for _, unexpandedVar := range unexpandedVars {
- expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
- if err != nil {
- return ret, err
- }
- expandedVars = append(expandedVars, expandedVar)
- }
- ret += strings.Join(expandedVars, " ")
- } else if unexpandedVar, ok := stringScope[variable]; ok {
- expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
- if err != nil {
- return ret, err
- }
- ret += expandedVar
- } else if unevaluatedVar, ok := exportedVars[variable]; ok {
- evalFunc := reflect.ValueOf(unevaluatedVar)
- validateVariableMethod(variable, evalFunc)
- evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
- evaluatedValue := evaluatedResult[0].Interface().(string)
- expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
- if err != nil {
- return ret, err
- }
- ret += expandedVar
- } else {
- return "", fmt.Errorf("Unbound config variable %s", variable)
- }
- }
- return ret, nil
- }
- var ret []string
- for _, v := range strings.Split(toExpand, " ") {
- val, err := expandVarInternal(v, map[string]bool{})
- if err != nil {
- return ret, err
- }
- ret = append(ret, val)
- }
-
- return ret, nil
-}
-
-func validateVariableMethod(name string, methodValue reflect.Value) {
- methodType := methodValue.Type()
- if methodType.Kind() != reflect.Func {
- panic(fmt.Errorf("method given for variable %s is not a function",
- name))
- }
- if n := methodType.NumIn(); n != 1 {
- panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
- name, n))
- }
- if n := methodType.NumOut(); n != 1 {
- panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
- name, n))
- }
- if kind := methodType.Out(0).Kind(); kind != reflect.String {
- panic(fmt.Errorf("method for variable %s does not return a string",
- name))
- }
-}
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
deleted file mode 100644
index 3118df1..0000000
--- a/cc/config/bp2build_test.go
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
- "testing"
-
- "android/soong/android"
-)
-
-func TestExpandVars(t *testing.T) {
- android_arm64_config := android.TestConfig("out", nil, "", nil)
- android_arm64_config.BuildOS = android.Android
- android_arm64_config.BuildArch = android.Arm64
-
- testCases := []struct {
- description string
- config android.Config
- stringScope exportedStringVariables
- stringListScope exportedStringListVariables
- configVars exportedConfigDependingVariables
- toExpand string
- expectedValues []string
- }{
- {
- description: "no expansion for non-interpolated value",
- toExpand: "foo",
- expectedValues: []string{"foo"},
- },
- {
- description: "single level expansion for string var",
- stringScope: exportedStringVariables{
- "foo": "bar",
- },
- toExpand: "${foo}",
- expectedValues: []string{"bar"},
- },
- {
- description: "single level expansion string list var",
- stringListScope: exportedStringListVariables{
- "foo": []string{"bar"},
- },
- toExpand: "${foo}",
- expectedValues: []string{"bar"},
- },
- {
- description: "mixed level expansion for string list var",
- stringScope: exportedStringVariables{
- "foo": "${bar}",
- "qux": "hello",
- },
- stringListScope: exportedStringListVariables{
- "bar": []string{"baz", "${qux}"},
- },
- toExpand: "${foo}",
- expectedValues: []string{"baz hello"},
- },
- {
- description: "double level expansion",
- stringListScope: exportedStringListVariables{
- "foo": []string{"${bar}"},
- "bar": []string{"baz"},
- },
- toExpand: "${foo}",
- expectedValues: []string{"baz"},
- },
- {
- description: "double level expansion with a literal",
- stringListScope: exportedStringListVariables{
- "a": []string{"${b}", "c"},
- "b": []string{"d"},
- },
- toExpand: "${a}",
- expectedValues: []string{"d c"},
- },
- {
- description: "double level expansion, with two variables in a string",
- stringListScope: exportedStringListVariables{
- "a": []string{"${b} ${c}"},
- "b": []string{"d"},
- "c": []string{"e"},
- },
- toExpand: "${a}",
- expectedValues: []string{"d e"},
- },
- {
- description: "triple level expansion with two variables in a string",
- stringListScope: exportedStringListVariables{
- "a": []string{"${b} ${c}"},
- "b": []string{"${c}", "${d}"},
- "c": []string{"${d}"},
- "d": []string{"foo"},
- },
- toExpand: "${a}",
- expectedValues: []string{"foo foo foo"},
- },
- {
- description: "expansion with config depending vars",
- configVars: exportedConfigDependingVariables{
- "a": func(c android.Config) string { return c.BuildOS.String() },
- "b": func(c android.Config) string { return c.BuildArch.String() },
- },
- config: android_arm64_config,
- toExpand: "${a}-${b}",
- expectedValues: []string{"android-arm64"},
- },
- {
- description: "double level multi type expansion",
- stringListScope: exportedStringListVariables{
- "platform": []string{"${os}-${arch}"},
- "const": []string{"const"},
- },
- configVars: exportedConfigDependingVariables{
- "os": func(c android.Config) string { return c.BuildOS.String() },
- "arch": func(c android.Config) string { return c.BuildArch.String() },
- "foo": func(c android.Config) string { return "foo" },
- },
- config: android_arm64_config,
- toExpand: "${const}/${platform}/${foo}",
- expectedValues: []string{"const/android-arm64/foo"},
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
- output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
- if len(output) != len(testCase.expectedValues) {
- t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
- }
- for i, actual := range output {
- expectedValue := testCase.expectedValues[i]
- if actual != expectedValue {
- t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
- }
- }
- })
- }
-}
-
-func TestBazelToolchainVars(t *testing.T) {
- testCases := []struct {
- name string
- config android.Config
- vars []bazelVarExporter
- expectedOut string
- }{
- {
- name: "exports strings",
- vars: []bazelVarExporter{
- exportedStringVariables{
- "a": "b",
- "c": "d",
- },
- },
- expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = "b"
-
-_c = "d"
-
-constants = struct(
- a = _a,
- c = _c,
-)`,
- },
- {
- name: "exports string lists",
- vars: []bazelVarExporter{
- exportedStringListVariables{
- "a": []string{"b1", "b2"},
- "c": []string{"d1", "d2"},
- },
- },
- expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = [
- "b1",
- "b2",
-]
-
-_c = [
- "d1",
- "d2",
-]
-
-constants = struct(
- a = _a,
- c = _c,
-)`,
- },
- {
- name: "exports string lists dicts",
- vars: []bazelVarExporter{
- exportedStringListDictVariables{
- "a": map[string][]string{"b1": []string{"b2"}},
- "c": map[string][]string{"d1": []string{"d2"}},
- },
- },
- expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = {
- "b1": [
- "b2",
- ],
-}
-
-_c = {
- "d1": [
- "d2",
- ],
-}
-
-constants = struct(
- a = _a,
- c = _c,
-)`,
- },
- {
- name: "sorts across types",
- vars: []bazelVarExporter{
- exportedStringVariables{
- "b": "b-val",
- "d": "d-val",
- },
- exportedStringListVariables{
- "c": []string{"c-val"},
- "e": []string{"e-val"},
- },
- exportedStringListDictVariables{
- "a": map[string][]string{"a1": []string{"a2"}},
- "f": map[string][]string{"f1": []string{"f2"}},
- },
- },
- expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = {
- "a1": [
- "a2",
- ],
-}
-
-_b = "b-val"
-
-_c = [
- "c-val",
-]
-
-_d = "d-val"
-
-_e = [
- "e-val",
-]
-
-_f = {
- "f1": [
- "f2",
- ],
-}
-
-constants = struct(
- a = _a,
- b = _b,
- c = _c,
- d = _d,
- e = _e,
- f = _f,
-)`,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- out := bazelToolchainVars(tc.config, tc.vars...)
- if out != tc.expectedOut {
- t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
- }
- })
- }
-}
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 206bec1..5e3f7c7 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -258,8 +258,12 @@
return darwinAvailableLibraries
}
-func (t *toolchainDarwin) ToolPath() string {
- return "${config.MacToolPath}"
+func (t *toolchainDarwin) ToolchainCflags() string {
+ return "-B${config.MacToolPath}"
+}
+
+func (t *toolchainDarwin) ToolchainLdflags() string {
+ return "-B${config.MacToolPath}"
}
var toolchainDarwinArmSingleton Toolchain = &toolchainDarwinArm{}
diff --git a/cc/config/global.go b/cc/config/global.go
index 5acc7f5..b09598a 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -23,6 +23,9 @@
)
var (
+ pctx = android.NewPackageContext("android/soong/cc/config")
+ exportedVars = android.NewExportedVariables(pctx)
+
// Flags used by lots of devices. Putting them in package static variables
// will save bytes in build.ninja so they aren't repeated for every file
commonGlobalCflags = []string{
@@ -95,6 +98,9 @@
// Nested and array designated initialization is nice to have.
"-Wno-c99-designator",
+ // Many old files still have GNU designator syntax.
+ "-Wno-gnu-designator",
+
// Warnings from clang-12
"-Wno-gnu-folding-constant",
@@ -104,6 +110,9 @@
// This macro allows the bionic versioning.h to indirectly determine whether the
// option -Wunguarded-availability is on or not.
"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
+
+ // Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
+ "-ffp-contract=off",
}
commonGlobalConlyflags = []string{}
@@ -139,7 +148,6 @@
"-Wl,-z,relro",
"-Wl,-z,now",
"-Wl,--build-id=md5",
- "-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--no-undefined-version",
// TODO: Eventually we should link against a libunwind.a with hidden symbols, and then these
@@ -183,7 +191,6 @@
"-Werror=int-in-bool-context",
"-Werror=int-to-pointer-cast",
"-Werror=pointer-to-int-cast",
- "-Werror=string-compare",
"-Werror=xor-used-as-pow",
// http://b/161386391 for -Wno-void-pointer-to-enum-cast
"-Wno-void-pointer-to-enum-cast",
@@ -209,7 +216,6 @@
// http://b/145211066
"-Wno-implicit-int-float-conversion",
// New warnings to be fixed after clang-r377782.
- "-Wno-int-in-bool-context", // http://b/148287349
"-Wno-sizeof-array-div", // http://b/148815709
"-Wno-tautological-overlap-compare", // http://b/148815696
// New warnings to be fixed after clang-r383902.
@@ -218,10 +224,6 @@
"-Wno-misleading-indentation", // http://b/153746954
"-Wno-zero-as-null-pointer-constant", // http://b/68236239
"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
- "-Wno-deprecated-enum-enum-conversion", // http://b/153746563
- "-Wno-string-compare", // http://b/153764102
- "-Wno-enum-enum-conversion", // http://b/154138986
- "-Wno-enum-float-conversion", // http://b/154255917
"-Wno-pessimizing-move", // http://b/154270751
// New warnings to be fixed after clang-r399163
"-Wno-non-c-typedef-for-linkage", // http://b/161304145
@@ -236,6 +238,8 @@
// http://b/197240255
"-Wno-unused-but-set-variable",
"-Wno-unused-but-set-parameter",
+ // http://b/215753485
+ "-Wno-bitwise-instead-of-logical",
}
// Extra cflags for external third-party projects to disable warnings that
@@ -273,15 +277,15 @@
"-w",
}
- CStdVersion = "gnu99"
+ CStdVersion = "gnu11"
CppStdVersion = "gnu++17"
- ExperimentalCStdVersion = "gnu11"
+ ExperimentalCStdVersion = "gnu17"
ExperimentalCppStdVersion = "gnu++2a"
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r437112b"
- ClangDefaultShortVersion = "14.0.1"
+ ClangDefaultVersion = "clang-r450784e"
+ ClangDefaultShortVersion = "14.0.7"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -293,20 +297,28 @@
WarningAllowedOldProjects = []string{}
)
-var pctx = android.NewPackageContext("android/soong/cc/config")
+// BazelCcToolchainVars generates bzl file content containing variables for
+// Bazel's cc_toolchain configuration.
+func BazelCcToolchainVars(config android.Config) string {
+ return android.BazelToolchainVars(config, exportedVars)
+}
+
+func ExportStringList(name string, value []string) {
+ exportedVars.ExportStringList(name, value)
+}
func init() {
if runtime.GOOS == "linux" {
commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
}
- exportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
- exportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
- exportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
- exportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
- exportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags)
- exportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags)
- exportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
+ exportedVars.ExportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
+ exportedVars.ExportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
+ exportedVars.ExportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
+ exportedVars.ExportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
+ exportedVars.ExportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags)
+ exportedVars.ExportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags)
+ exportedVars.ExportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
// Export the static default CommonGlobalCflags to Bazel.
// TODO(187086342): handle cflags that are set in VariableFuncs.
@@ -317,7 +329,7 @@
"-ftrivial-auto-var-init=zero",
"-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang",
}...)
- exportedStringListVars.Set("CommonGlobalCflags", bazelCommonGlobalCflags)
+ exportedVars.ExportStringList("CommonGlobalCflags", bazelCommonGlobalCflags)
pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string {
flags := commonGlobalCflags
@@ -346,17 +358,22 @@
// Export the static default DeviceGlobalCflags to Bazel.
// TODO(187086342): handle cflags that are set in VariableFuncs.
- exportedStringListVars.Set("DeviceGlobalCflags", deviceGlobalCflags)
+ exportedVars.ExportStringList("DeviceGlobalCflags", deviceGlobalCflags)
pctx.VariableFunc("DeviceGlobalCflags", func(ctx android.PackageVarContext) string {
return strings.Join(deviceGlobalCflags, " ")
})
- exportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
- exportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
- exportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
- exportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
- exportStringListStaticVariable("ExternalCflags", extraExternalCflags)
+ exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
+ exportedVars.ExportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+ exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
+ exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
+ exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
+
+ exportedVars.ExportString("CStdVersion", CStdVersion)
+ exportedVars.ExportString("CppStdVersion", CppStdVersion)
+ exportedVars.ExportString("ExperimentalCStdVersion", ExperimentalCStdVersion)
+ exportedVars.ExportString("ExperimentalCppStdVersion", ExperimentalCppStdVersion)
// Everything in these lists is a crime against abstraction and dependency tracking.
// Do not add anything to this list.
@@ -371,11 +388,11 @@
"frameworks/native/opengl/include",
"frameworks/av/include",
}
- exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes)
+ exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes)
pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
- exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
- exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
+ exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+ exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase)
pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
@@ -415,7 +432,7 @@
pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
}
-var HostPrebuiltTag = exportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+var HostPrebuiltTag = exportedVars.ExportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
func ClangPath(ctx android.PathContext, file string) android.SourcePath {
type clangToolKey string
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index fdc246c..826197a 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -35,17 +35,26 @@
"bugprone-*",
"cert-*",
"clang-diagnostic-unused-command-line-argument",
- "google-*",
+ // Select only google-* checks that do not have thousands of warnings.
+ // Add more such checks when we clean up source code.
+ // "google-build-using-namespace",
+ // "google-default-arguments",
+ // "google-explicit-constructor",
+ // "google-global-names-in-headers",
+ // "google-runtime-int",
+ "google-build-explicit-make-pair",
+ "google-build-namespaces",
+ "google-runtime-operator",
+ "google-upgrade-*",
"misc-*",
"performance-*",
"portability-*",
"-bugprone-easily-swappable-parameters",
"-bugprone-narrowing-conversions",
- "-google-readability*",
- "-google-runtime-references",
"-misc-no-recursion",
"-misc-non-private-member-variables-in-classes",
"-misc-unused-parameters",
+ "-performance-no-int-to-ptr",
// the following groups are excluded by -*
// -altera-*
// -cppcoreguidelines-*
@@ -62,8 +71,9 @@
}, ",")
// clang-analyzer-* checks are too slow to be in the default for WITH_TIDY=1.
// nightly builds add CLANG_ANALYZER_CHECKS=1 to run those checks.
+ // The insecureAPI.DeprecatedOrUnsafeBufferHandling warning does not apply to Android.
if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
- checks += ",clang-analyzer-*"
+ checks += ",clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling"
}
return checks
})
@@ -77,13 +87,10 @@
return strings.Join([]string{
"-*",
"clang-diagnostic-unused-command-line-argument",
- "google*",
- "-google-build-using-namespace",
- "-google-default-arguments",
- "-google-explicit-constructor",
- "-google-readability*",
- "-google-runtime-int",
- "-google-runtime-references",
+ "google-build-explicit-make-pair",
+ "google-build-namespaces",
+ "google-runtime-operator",
+ "google-upgrade-*",
}, ",")
})
@@ -121,6 +128,7 @@
{"hardware/qcom", tidyExternalVendor},
{"vendor/", tidyExternalVendor},
{"vendor/google", tidyDefault},
+ {"vendor/google_arc/libs/org.chromium.arc.mojom", tidyExternalVendor},
{"vendor/google_devices", tidyExternalVendor},
}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 6320dbb..253bb06 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "path/filepath"
"android/soong/android"
)
@@ -37,19 +36,10 @@
Arch() android.Arch
}
-type conversionContext interface {
- BazelConversionMode() bool
-}
-
func FindToolchainWithContext(ctx toolchainContext) Toolchain {
t, err := findToolchain(ctx.Os(), ctx.Arch())
if err != nil {
- if c, ok := ctx.(conversionContext); ok && c.BazelConversionMode() {
- // TODO(b/179123288): determine conversion for toolchain
- return &toolchainX86_64{}
- } else {
- panic(err)
- }
+ panic(err)
}
return t
}
@@ -77,7 +67,6 @@
GccTriple() string
// GccVersion should return a real value, not a ninja reference
GccVersion() string
- ToolPath() string
IncludeFlags() string
@@ -198,10 +187,6 @@
return false
}
-func (t toolchainBase) ToolPath() string {
- return ""
-}
-
type toolchain64Bit struct {
toolchainBase
}
@@ -233,14 +218,7 @@
}
func LibclangRuntimeLibrary(t Toolchain, library string) string {
- arch := t.LibclangRuntimeLibraryArch()
- if arch == "" {
- return ""
- }
- if !t.Bionic() {
- return "libclang_rt." + library + "-" + arch
- }
- return "libclang_rt." + library + "-" + arch + "-android"
+ return "libclang_rt." + library
}
func BuiltinsRuntimeLibrary(t Toolchain) string {
@@ -283,11 +261,4 @@
return LibclangRuntimeLibrary(t, "fuzzer")
}
-func ToolPath(t Toolchain) string {
- if p := t.ToolPath(); p != "" {
- return p
- }
- return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
-}
-
var inList = android.InList
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00f07ff..aebda0b 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -15,6 +15,7 @@
package config
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -36,10 +37,10 @@
"": []string{
"-march=x86-64",
},
+
"broadwell": []string{
"-march=broadwell",
},
-
"haswell": []string{
"-march=core-avx2",
},
@@ -77,14 +78,6 @@
"popcnt": []string{"-mpopcnt"},
"aes_ni": []string{"-maes"},
}
-
- x86_64DefaultArchVariantFeatures = []string{
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- }
)
const (
@@ -92,34 +85,32 @@
)
func init() {
- android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64, x86_64DefaultArchVariantFeatures...)
- exportedStringListVars.Set("X86_64DefaultArchVariantFeatures", x86_64DefaultArchVariantFeatures)
pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
pctx.SourcePathVariable("X86_64GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
- exportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
- exportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
+ exportedVars.ExportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
+ exportedVars.ExportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
- exportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
- exportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
+ exportedVars.ExportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
+ exportedVars.ExportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
// Clang cflags
- exportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
- exportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
+ exportedVars.ExportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
+ exportedVars.ExportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
// Yasm flags
- exportStringListStaticVariable("X86_64YasmFlags", []string{
+ exportedVars.ExportStringListStaticVariable("X86_64YasmFlags", []string{
"-f elf64",
"-m amd64",
})
// Extended cflags
- exportedStringListDictVars.Set("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
- exportedStringListDictVars.Set("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
+ exportedVars.ExportStringListDict("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
+ exportedVars.ExportStringListDict("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
// Architecture variant cflags
for variant, cflags := range x86_64ArchVariantCflags {
@@ -190,6 +181,11 @@
}
func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+ // Error now rather than having a confusing Ninja error
+ if _, ok := x86_64ArchVariantCflags[arch.ArchVariant]; !ok {
+ panic(fmt.Sprintf("Unknown x86_64 architecture version: %q", arch.ArchVariant))
+ }
+
toolchainCflags := []string{
"${config.X86_64ToolchainCflags}",
"${config.X86_64" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 29f0593..421b083 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -15,6 +15,7 @@
package config
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -97,25 +98,25 @@
pctx.SourcePathVariable("X86GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
- exportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
- exportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
+ exportedVars.ExportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
+ exportedVars.ExportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
- exportStringListStaticVariable("X86Ldflags", x86Ldflags)
- exportStringListStaticVariable("X86Lldflags", x86Ldflags)
+ exportedVars.ExportStringListStaticVariable("X86Ldflags", x86Ldflags)
+ exportedVars.ExportStringListStaticVariable("X86Lldflags", x86Ldflags)
// Clang cflags
- exportStringListStaticVariable("X86Cflags", x86Cflags)
- exportStringListStaticVariable("X86Cppflags", x86Cppflags)
+ exportedVars.ExportStringListStaticVariable("X86Cflags", x86Cflags)
+ exportedVars.ExportStringListStaticVariable("X86Cppflags", x86Cppflags)
// Yasm flags
- exportStringListStaticVariable("X86YasmFlags", []string{
+ exportedVars.ExportStringListStaticVariable("X86YasmFlags", []string{
"-f elf32",
"-m x86",
})
// Extended cflags
- exportedStringListDictVars.Set("X86ArchVariantCflags", x86ArchVariantCflags)
- exportedStringListDictVars.Set("X86ArchFeatureCflags", x86ArchFeatureCflags)
+ exportedVars.ExportStringListDict("X86ArchVariantCflags", x86ArchVariantCflags)
+ exportedVars.ExportStringListDict("X86ArchFeatureCflags", x86ArchFeatureCflags)
// Architecture variant cflags
for variant, cflags := range x86ArchVariantCflags {
@@ -186,6 +187,11 @@
}
func x86ToolchainFactory(arch android.Arch) Toolchain {
+ // Error now rather than having a confusing Ninja error
+ if _, ok := x86ArchVariantCflags[arch.ArchVariant]; !ok {
+ panic(fmt.Sprintf("Unknown x86 architecture version: %q", arch.ArchVariant))
+ }
+
toolchainCflags := []string{
"${config.X86ToolchainCflags}",
"${config.X86" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 4b7ba6a..976cc25 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -47,7 +47,6 @@
"-Wl,-z,relro",
"-Wl,-z,now",
"-Wl,--build-id=md5",
- "-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--hash-style=gnu",
"-Wl,--no-undefined-version",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 43333fa..4e8fd77 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -47,6 +47,7 @@
"-D_LIBCPP_HAS_MUSL_LIBC",
"-DANDROID_HOST_MUSL",
"-nostdlibinc",
+ "--sysroot /dev/null",
}
linuxLdflags = []string{
@@ -64,7 +65,7 @@
linuxMuslLdflags = []string{
"-nostdlib",
- "-lgcc", "-lgcc_eh",
+ "--sysroot /dev/null",
}
// Extended cflags
@@ -120,40 +121,40 @@
)
func init() {
- exportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
- exportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+ exportedVars.ExportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
+ exportedVars.ExportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
// Most places use the full GCC version. A few only use up to the first two numbers.
if p := strings.Split(linuxGccVersion, "."); len(p) > 2 {
- exportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
+ exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
} else {
- exportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
+ exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
}
- exportSourcePathVariable("LinuxGccRoot",
+ exportedVars.ExportSourcePathVariable("LinuxGccRoot",
"prebuilts/gcc/linux-x86/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
- exportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
+ exportedVars.ExportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
- exportStringListStaticVariable("LinuxCflags", linuxCflags)
- exportStringListStaticVariable("LinuxLdflags", linuxLdflags)
- exportStringListStaticVariable("LinuxLldflags", linuxLdflags)
- exportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
- exportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
- exportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
- exportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
- exportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
- exportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxCflags", linuxCflags)
+ exportedVars.ExportStringListStaticVariable("LinuxLdflags", linuxLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxLldflags", linuxLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
+ exportedVars.ExportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
+ exportedVars.ExportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
- exportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
- exportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
- exportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
- exportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
- exportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
- exportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags)
+ exportedVars.ExportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
+ exportedVars.ExportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
+ exportedVars.ExportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
+ exportedVars.ExportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
+ exportedVars.ExportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
+ exportedVars.ExportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags)
// Yasm flags
- exportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
- exportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
+ exportedVars.ExportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
+ exportedVars.ExportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
}
type toolchainLinux struct {
@@ -194,10 +195,6 @@
return ""
}
-func (t *toolchainLinuxX86) ClangTriple() string {
- return "i686-linux-gnu"
-}
-
func (t *toolchainLinuxX86) Cflags() string {
return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
}
@@ -206,10 +203,6 @@
return ""
}
-func (t *toolchainLinuxX8664) ClangTriple() string {
- return "x86_64-linux-gnu"
-}
-
func (t *toolchainLinuxX8664) Cflags() string {
return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
}
@@ -283,6 +276,10 @@
toolchainGlibc
}
+func (t *toolchainLinuxGlibcX86) ClangTriple() string {
+ return "i686-linux-gnu"
+}
+
func (t *toolchainLinuxGlibcX86) Cflags() string {
return t.toolchainLinuxX86.Cflags() + " " + t.toolchainGlibc.Cflags()
}
@@ -295,6 +292,10 @@
return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainGlibc.Lldflags()
}
+func (t *toolchainLinuxGlibcX8664) ClangTriple() string {
+ return "x86_64-linux-gnu"
+}
+
func (t *toolchainLinuxGlibcX8664) Cflags() string {
return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainGlibc.Cflags()
}
@@ -356,6 +357,10 @@
toolchainMusl
}
+func (t *toolchainLinuxMuslX86) ClangTriple() string {
+ return "i686-linux-musl"
+}
+
func (t *toolchainLinuxMuslX86) Cflags() string {
return t.toolchainLinuxX86.Cflags() + " " + t.toolchainMusl.Cflags()
}
@@ -368,6 +373,10 @@
return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainMusl.Lldflags()
}
+func (t *toolchainLinuxMuslX8664) ClangTriple() string {
+ return "x86_64-linux-musl"
+}
+
func (t *toolchainLinuxMuslX8664) Cflags() string {
return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainMusl.Cflags()
}
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 9daf40f..2c83211 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -15,6 +15,7 @@
package config
import (
+ "path/filepath"
"strings"
"android/soong/android"
@@ -180,6 +181,14 @@
return "${config.WindowsGccTriple}"
}
+func (t *toolchainWindows) ToolchainCflags() string {
+ return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
+func (t *toolchainWindows) ToolchainLdflags() string {
+ return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
func (t *toolchainWindows) GccVersion() string {
return windowsGccVersion
}
diff --git a/cc/coverage.go b/cc/coverage.go
index f2b5425..d0902ea 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -77,6 +77,10 @@
return deps
}
+func EnableContinuousCoverage(ctx android.BaseModuleContext) bool {
+ return ctx.DeviceConfig().ClangCoverageContinuousMode()
+}
+
func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled()
@@ -101,6 +105,9 @@
// Override -Wframe-larger-than. We can expect frame size increase after
// coverage instrumentation.
flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
+ if EnableContinuousCoverage(ctx) {
+ flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
+ }
}
}
@@ -152,6 +159,9 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
} else if clangCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
+ if EnableContinuousCoverage(ctx) {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
+ }
coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
diff --git a/cc/gen.go b/cc/gen.go
index 8f62363..08b49c9 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -18,6 +18,7 @@
"path/filepath"
"strings"
+ "android/soong/bazel"
"github.com/google/blueprint"
"android/soong/android"
@@ -169,6 +170,41 @@
})
}
+type LexAttrs struct {
+ Srcs bazel.LabelListAttribute
+ Lexopts bazel.StringListAttribute
+}
+
+type LexNames struct {
+ cSrcName bazel.LabelAttribute
+ srcName bazel.LabelAttribute
+}
+
+func bp2BuildLex(ctx android.Bp2buildMutatorContext, moduleName string, ca compilerAttributes) LexNames {
+ names := LexNames{}
+ if !ca.lSrcs.IsEmpty() {
+ names.cSrcName = createLexTargetModule(ctx, moduleName+"_genlex_l", ca.lSrcs, ca.lexopts)
+ }
+ if !ca.llSrcs.IsEmpty() {
+ names.srcName = createLexTargetModule(ctx, moduleName+"_genlex_ll", ca.llSrcs, ca.lexopts)
+ }
+ return names
+}
+
+func createLexTargetModule(ctx android.Bp2buildMutatorContext, name string, srcs bazel.LabelListAttribute, opts bazel.StringListAttribute) bazel.LabelAttribute {
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "genlex",
+ Bzl_load_location: "//build/bazel/rules/cc:flex.bzl",
+ },
+ android.CommonAttributes{Name: name},
+ &LexAttrs{
+ Srcs: srcs,
+ Lexopts: opts,
+ })
+ return bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + name}}
+}
+
func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Paths) {
headerFile := android.PathForModuleGen(ctx, "sysprop", "include", syspropFile.Rel()+".h")
publicHeaderFile := android.PathForModuleGen(ctx, "sysprop/public", "include", syspropFile.Rel()+".h")
diff --git a/cc/installer.go b/cc/installer.go
index 2522610..e2c0e7b 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -31,7 +31,7 @@
Install_in_root *bool `android:"arch_variant"`
// Install output directly in {partition}/xbin
- Install_in_xbin *bool `android:"arch_vvariant"`
+ Install_in_xbin *bool `android:"arch_variant"`
}
type installLocation int
diff --git a/cc/libbuildversion/tests/Android.bp b/cc/libbuildversion/tests/Android.bp
index 0e97fed..c616a33 100644
--- a/cc/libbuildversion/tests/Android.bp
+++ b/cc/libbuildversion/tests/Android.bp
@@ -35,6 +35,16 @@
dir: "host/",
},
},
+ linux_musl_x86: {
+ dist: {
+ dir: "host32/",
+ },
+ },
+ linux_musl_x86_64: {
+ dist: {
+ dir: "host/",
+ },
+ },
linux_glibc_x86: {
dist: {
dir: "host32/",
diff --git a/cc/library.go b/cc/library.go
index cefbf6c..0fa01d7 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,7 +32,7 @@
"github.com/google/blueprint/pathtools"
)
-// LibraryProperties is a collection of properties shared by cc library rules.
+// LibraryProperties is a collection of properties shared by cc library rules/cc.
type LibraryProperties struct {
// local file name to pass to the linker as -unexported_symbols_list
Unexported_symbols_list *string `android:"path,arch_variant"`
@@ -110,6 +110,9 @@
// Run checks on all APIs (in addition to the ones referred by
// one of exported ELF symbols.)
Check_all_apis *bool
+
+ // Extra flags passed to header-abi-diff
+ Diff_flags []string
}
// Inject boringssl hash into the shared library. This is only intended for use by external/boringssl.
@@ -145,6 +148,8 @@
Tidy_disabled_srcs []string `android:"path,arch_variant"`
+ Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
Sanitized Sanitized `android:"arch_variant"`
Cflags []string `android:"arch_variant"`
@@ -287,7 +292,7 @@
baseAttributes := bp2BuildParseBaseProps(ctx, m)
compilerAttrs := baseAttributes.compilerAttributes
linkerAttrs := baseAttributes.linkerAttributes
- exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, compilerAttrs.includes)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, &compilerAttrs.includes)
srcs := compilerAttrs.srcs
@@ -314,6 +319,7 @@
Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
Whole_archive_deps: *linkerAttrs.wholeArchiveDeps.Clone().Append(staticAttrs.Whole_archive_deps),
System_dynamic_deps: *linkerAttrs.systemDynamicDeps.Clone().Append(staticAttrs.System_dynamic_deps),
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
sharedCommonAttrs := staticOrSharedAttributes{
@@ -329,6 +335,7 @@
Implementation_dynamic_deps: *linkerAttrs.implementationDynamicDeps.Clone().Append(sharedAttrs.Implementation_dynamic_deps),
Whole_archive_deps: *linkerAttrs.wholeArchiveDeps.Clone().Append(sharedAttrs.Whole_archive_deps),
System_dynamic_deps: *linkerAttrs.systemDynamicDeps.Clone().Append(sharedAttrs.System_dynamic_deps),
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
staticTargetAttrs := &bazelCcLibraryStaticAttributes{
@@ -348,6 +355,7 @@
Stl: compilerAttrs.stl,
Cpp_std: compilerAttrs.cppStd,
C_std: compilerAttrs.cStd,
+ Use_version_lib: linkerAttrs.useVersionLib,
Features: linkerAttrs.features,
}
@@ -370,6 +378,7 @@
Stl: compilerAttrs.stl,
Cpp_std: compilerAttrs.cppStd,
C_std: compilerAttrs.cStd,
+ Use_version_lib: linkerAttrs.useVersionLib,
Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
@@ -386,13 +395,28 @@
Stubs_versions: compilerAttrs.stubsVersions,
}
+ for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
+ for config, props := range configToProps {
+ if props, ok := props.(*LibraryProperties); ok {
+ if props.Inject_bssl_hash != nil {
+ // This is an edge case applies only to libcrypto
+ if m.Name() == "libcrypto" {
+ sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, config, props.Inject_bssl_hash)
+ } else {
+ ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
+ }
+ }
+ }
+ }
+ }
+
staticProps := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_static",
- Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_static.bzl",
}
sharedProps := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_shared",
- Bzl_load_location: "//build/bazel/rules:cc_library_shared.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
}
ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
@@ -571,7 +595,8 @@
stripper Stripper
// For whole_static_libs
- objects Objects
+ objects Objects
+ wholeStaticLibsFromPrebuilts android.Paths
// Uses the module's name if empty, but can be overridden. Does not include
// shlib suffix.
@@ -620,18 +645,18 @@
}
type ccLibraryBazelHandler struct {
- android.BazelHandler
-
module *Module
}
+var _ BazelHandler = (*ccLibraryBazelHandler)(nil)
+
// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong
// provider from a Bazel shared library's CcInfo provider.
-func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
+func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) {
rootStaticArchives := ccInfo.RootStaticArchives
if len(rootStaticArchives) != 1 {
ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
- return false
+ return
}
outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
@@ -657,17 +682,17 @@
Build(),
})
- return true
+ return
}
// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong
// provider from a Bazel shared library's CcInfo provider.
-func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
+func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) {
rootDynamicLibraries := ccInfo.RootDynamicLibraries
if len(rootDynamicLibraries) != 1 {
ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
- return false
+ return
}
outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
@@ -687,30 +712,27 @@
// TODO(b/190524881): Include transitive static libraries in this provider to support
// static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering.
})
- return true
}
-func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *ccLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+}
+
+func (handler *ccLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
- return false
- }
- if !ok {
- return ok
+ return
}
if handler.module.static() {
- if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok {
- return false
- }
+ handler.generateStaticBazelBuildActions(ctx, label, ccInfo)
} else if handler.module.Shared() {
- if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok {
- return false
- }
+ handler.generateSharedBazelBuildActions(ctx, label, ccInfo)
} else {
- return false
+ ctx.ModuleErrorf("Unhandled bazel case for %s (neither shared nor static!)", ctx.ModuleName())
}
handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo)
@@ -724,7 +746,6 @@
// implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
}
- return ok
}
func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
@@ -757,9 +778,10 @@
if dir == "external/eigen" {
// Only these two directories contains exported headers.
for _, subdir := range []string{"Eigen", "unsupported/Eigen"} {
- glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
+ globDir := "external/eigen/" + subdir + "/**/*"
+ glob, err := ctx.GlobWithDeps(globDir, nil)
if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
+ ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
return nil
}
for _, header := range glob {
@@ -775,9 +797,10 @@
}
continue
}
- glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+ globDir := dir + "/**/*"
+ glob, err := ctx.GlobWithDeps(globDir, nil)
if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
+ ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
return nil
}
isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
@@ -1061,11 +1084,13 @@
srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
} else if library.shared() {
srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
}
@@ -1326,6 +1351,7 @@
library.objects = deps.WholeStaticLibObjs.Copy()
library.objects = library.objects.Append(objs)
+ library.wholeStaticLibsFromPrebuilts = android.CopyOfPaths(deps.WholeStaticLibsFromPrebuilts)
fileName := ctx.ModuleName() + staticLibraryExtension
outputFile := android.PathForModuleOut(ctx, fileName)
@@ -1351,9 +1377,10 @@
if library.static() {
ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: outputFile,
- ReuseObjects: library.reuseObjects,
- Objects: library.objects,
+ StaticLibrary: outputFile,
+ ReuseObjects: library.reuseObjects,
+ Objects: library.objects,
+ WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
Direct(outputFile).
@@ -1610,6 +1637,7 @@
if refAbiDumpFile != nil {
library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
refAbiDumpFile, fileName, exportedHeaderFlags,
+ library.Properties.Header_abi_checker.Diff_flags,
Bool(library.Properties.Header_abi_checker.Check_all_apis),
ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
}
@@ -2316,7 +2344,7 @@
}
}
-func CanBeOrLinkAgainstVersionVariants(module interface {
+func canBeOrLinkAgainstVersionVariants(module interface {
Host() bool
InRamdisk() bool
InVendorRamdisk() bool
@@ -2324,15 +2352,14 @@
return !module.Host() && !module.InRamdisk() && !module.InVendorRamdisk()
}
-func CanBeVersionVariant(module interface {
+func canBeVersionVariant(module interface {
Host() bool
InRamdisk() bool
InVendorRamdisk() bool
- InRecovery() bool
CcLibraryInterface() bool
Shared() bool
}) bool {
- return CanBeOrLinkAgainstVersionVariants(module) &&
+ return canBeOrLinkAgainstVersionVariants(module) &&
module.CcLibraryInterface() && module.Shared()
}
@@ -2343,37 +2370,41 @@
return nil
}
-// versionSelector normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions.
-func versionSelectorMutator(mctx android.BottomUpMutatorContext) {
- if library := moduleLibraryInterface(mctx.Module()); library != nil && CanBeVersionVariant(mctx.Module().(*Module)) {
- if library.buildShared() {
- versions := library.stubsVersions(mctx)
- if len(versions) > 0 {
- normalizeVersions(mctx, versions)
- if mctx.Failed() {
- return
- }
- // Set the versions on the pre-mutated module so they can be read by any llndk modules that
- // depend on the implementation library and haven't been mutated yet.
- library.setAllStubsVersions(versions)
- }
- }
+// setStubsVersions normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions.
+func setStubsVersions(mctx android.BottomUpMutatorContext, library libraryInterface, module *Module) {
+ if !library.buildShared() || !canBeVersionVariant(module) {
+ return
}
+ versions := library.stubsVersions(mctx)
+ if len(versions) <= 0 {
+ return
+ }
+ normalizeVersions(mctx, versions)
+ if mctx.Failed() {
+ return
+ }
+ // Set the versions on the pre-mutated module so they can be read by any llndk modules that
+ // depend on the implementation library and haven't been mutated yet.
+ library.setAllStubsVersions(versions)
}
// versionMutator splits a module into the mandatory non-stubs variant
// (which is unnamed) and zero or more stubs variants.
func versionMutator(mctx android.BottomUpMutatorContext) {
- if library := moduleLibraryInterface(mctx.Module()); library != nil && CanBeVersionVariant(mctx.Module().(*Module)) {
+ if mctx.Os() != android.Android {
+ return
+ }
+
+ m, ok := mctx.Module().(*Module)
+ if library := moduleLibraryInterface(mctx.Module()); library != nil && canBeVersionVariant(m) {
+ setStubsVersions(mctx, library, m)
+
createVersionVariations(mctx, library.allStubsVersions())
return
}
- if m, ok := mctx.Module().(*Module); ok {
+ if ok {
if m.SplitPerApiLevel() && m.IsSdkVariant() {
- if mctx.Os() != android.Android {
- return
- }
createPerApiVersionVariations(mctx, m.MinSdkVersion())
}
}
@@ -2405,7 +2436,6 @@
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
BuiltTool("bssl_inject_hash").
- Flag("-sha256").
FlagWithInput("-in-object ", outputFile).
FlagWithOutput("-o ", hashedOutputfile)
rule.Build("injectCryptoHash", "inject crypto hash")
@@ -2419,7 +2449,7 @@
compilerAttrs := baseAttributes.compilerAttributes
linkerAttrs := baseAttributes.linkerAttributes
- exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, compilerAttrs.includes)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &compilerAttrs.includes)
// Append shared/static{} stanza properties. These won't be specified on
// cc_library_* itself, but may be specified in cc_defaults that this module
@@ -2457,6 +2487,7 @@
Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+ sdkAttributes: bp2BuildParseSdkAttributes(module),
}
var attrs interface{}
@@ -2535,7 +2566,7 @@
}
props := bazel.BazelTargetModuleProperties{
Rule_class: modType,
- Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+ Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -2600,4 +2631,5 @@
Stubs_symbol_file *string
Stubs_versions bazel.StringListAttribute
+ Inject_bssl_hash bazel.BoolAttribute
}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 064e2b8..7232290 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
)
func init() {
@@ -47,28 +48,30 @@
ctx.RegisterModuleType("cc_prebuilt_library_headers", prebuiltLibraryHeaderFactory)
}
-type libraryHeaderBazelHander struct {
- android.BazelHandler
-
+type libraryHeaderBazelHandler struct {
module *Module
library *libraryDecorator
}
-func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+var _ BazelHandler = (*libraryHeaderBazelHandler)(nil)
+
+func (handler *libraryHeaderBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+}
+
+func (h *libraryHeaderBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
- ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
- return false
- }
- if !ok {
- return false
+ ctx.ModuleErrorf(err.Error())
+ return
}
outputPaths := ccInfo.OutputFiles
if len(outputPaths) != 1 {
ctx.ModuleErrorf("expected exactly one output file for %q, but got %q", label, outputPaths)
- return false
+ return
}
outputPath := android.PathForBazelOut(ctx, outputPaths[0])
@@ -83,8 +86,6 @@
// validation will fail. For now, set this to an empty list.
// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
h.library.collectedSnapshotHeaders = android.Paths{}
-
- return true
}
// cc_library_headers contains a set of c/c++ headers which are imported by
@@ -96,7 +97,7 @@
library.HeaderOnly()
module.sdkMemberTypes = []android.SdkMemberType{headersLibrarySdkMemberType}
module.bazelable = true
- module.bazelHandler = &libraryHeaderBazelHander{module: module, library: library}
+ module.bazelHandler = &libraryHeaderBazelHandler{module: module, library: library}
return module.Init()
}
@@ -117,11 +118,12 @@
Deps bazel.LabelListAttribute
Implementation_deps bazel.LabelListAttribute
System_dynamic_deps bazel.LabelListAttribute
+ sdkAttributes
}
func libraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
baseAttributes := bp2BuildParseBaseProps(ctx, module)
- exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, baseAttributes.includes)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &baseAttributes.includes)
linkerAttrs := baseAttributes.linkerAttributes
attrs := &bazelCcLibraryHeadersAttributes{
@@ -132,11 +134,12 @@
Deps: linkerAttrs.deps,
System_dynamic_deps: linkerAttrs.systemDynamicDeps,
Hdrs: baseAttributes.hdrs,
+ sdkAttributes: bp2BuildParseSdkAttributes(module),
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_headers",
- Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 8988de2..1bcbdc5 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -27,32 +27,33 @@
var sharedLibrarySdkMemberType = &librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
- PropertyName: "native_shared_libs",
- SupportsSdk: true,
- HostOsDependent: true,
+ PropertyName: "native_shared_libs",
+ SupportsSdk: true,
+ HostOsDependent: true,
+ SupportedLinkageNames: []string{"shared"},
},
prebuiltModuleType: "cc_prebuilt_library_shared",
- linkTypes: []string{"shared"},
}
var staticLibrarySdkMemberType = &librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
- PropertyName: "native_static_libs",
- SupportsSdk: true,
- HostOsDependent: true,
+ PropertyName: "native_static_libs",
+ SupportsSdk: true,
+ HostOsDependent: true,
+ SupportedLinkageNames: []string{"static"},
},
prebuiltModuleType: "cc_prebuilt_library_static",
- linkTypes: []string{"static"},
}
var staticAndSharedLibrarySdkMemberType = &librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
- PropertyName: "native_libs",
- SupportsSdk: true,
- HostOsDependent: true,
+ PropertyName: "native_libs",
+ OverridesPropertyNames: map[string]bool{"native_shared_libs": true, "native_static_libs": true},
+ SupportsSdk: true,
+ HostOsDependent: true,
+ SupportedLinkageNames: []string{"static", "shared"},
},
prebuiltModuleType: "cc_prebuilt_library",
- linkTypes: []string{"static", "shared"},
}
func init() {
@@ -69,9 +70,6 @@
noOutputFiles bool // True if there are no srcs files.
- // The set of link types supported. A set of "static", "shared", or nil to
- // skip link type variations.
- linkTypes []string
}
func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
@@ -165,12 +163,12 @@
// Add any additional dependencies needed.
variations = append(variations, dependency.imageVariations...)
- if mt.linkTypes == nil {
+ if mt.SupportedLinkageNames == nil {
// No link types are supported so add a dependency directly.
ctx.AddFarVariationDependencies(variations, dependencyTag, name)
} else {
// Otherwise, add a dependency on each supported link type in turn.
- for _, linkType := range mt.linkTypes {
+ for _, linkType := range mt.SupportedLinkageNames {
libVariations := append(variations,
blueprint.Variation{Mutator: "link", Variation: linkType})
// If this is for the device and a shared link type then add a dependency onto the
diff --git a/cc/library_stub.go b/cc/library_stub.go
new file mode 100644
index 0000000..4d0148d
--- /dev/null
+++ b/cc/library_stub.go
@@ -0,0 +1,163 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "android/soong/android"
+ "android/soong/multitree"
+)
+
+func init() {
+ RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
+ // cc_api_stub_library shares a lot of ndk_library, and this will be refactored later
+ ctx.RegisterModuleType("cc_api_stub_library", CcApiStubLibraryFactory)
+ ctx.RegisterModuleType("cc_api_contribution", CcApiContributionFactory)
+}
+
+func CcApiStubLibraryFactory() android.Module {
+ module, decorator := NewLibrary(android.DeviceSupported)
+ apiStubDecorator := &apiStubDecorator{
+ libraryDecorator: decorator,
+ }
+ apiStubDecorator.BuildOnlyShared()
+
+ module.compiler = apiStubDecorator
+ module.linker = apiStubDecorator
+ module.installer = nil
+ module.library = apiStubDecorator
+ module.Properties.HideFromMake = true // TODO: remove
+
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+ module.AddProperties(&module.Properties,
+ &apiStubDecorator.properties,
+ &apiStubDecorator.MutatedProperties,
+ &apiStubDecorator.apiStubLibraryProperties)
+ return module
+}
+
+type apiStubLiraryProperties struct {
+ Imported_includes []string `android:"path"`
+}
+
+type apiStubDecorator struct {
+ *libraryDecorator
+ properties libraryProperties
+ apiStubLibraryProperties apiStubLiraryProperties
+}
+
+func (compiler *apiStubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
+ firstVersion := String(compiler.properties.First_version)
+ return ndkLibraryVersions(ctx, android.ApiLevelOrPanic(ctx, firstVersion))
+}
+
+func (decorator *apiStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+ if decorator.stubsVersion() == "" {
+ decorator.setStubsVersion("current")
+ } // TODO: fix
+ symbolFile := String(decorator.properties.Symbol_file)
+ nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
+ android.ApiLevelOrPanic(ctx, decorator.stubsVersion()),
+ "")
+ return compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+}
+
+func (decorator *apiStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
+ decorator.reexportDirs(android.PathsForModuleSrc(ctx, decorator.apiStubLibraryProperties.Imported_includes)...)
+ return decorator.libraryDecorator.link(ctx, flags, deps, objects)
+}
+
+func init() {
+ pctx.HostBinToolVariable("gen_api_surface_build_files", "gen_api_surface_build_files")
+}
+
+type CcApiContribution struct {
+ android.ModuleBase
+ properties ccApiContributionProperties
+}
+
+type ccApiContributionProperties struct {
+ Symbol_file *string `android:"path"`
+ First_version *string
+ Export_include_dir *string
+}
+
+func CcApiContributionFactory() android.Module {
+ module := &CcApiContribution{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
+
+// Do some simple validations
+// Majority of the build rules will be created in the ctx of the api surface this module contributes to
+func (contrib *CcApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if contrib.properties.Symbol_file == nil {
+ ctx.PropertyErrorf("symbol_file", "%v does not have symbol file", ctx.ModuleName())
+ }
+ if contrib.properties.First_version == nil {
+ ctx.PropertyErrorf("first_version", "%v does not have first_version for stub variants", ctx.ModuleName())
+ }
+}
+
+// Path is out/soong/.export/ but will be different in final multi-tree layout
+func outPathApiSurface(ctx android.ModuleContext, myModuleName string, pathComponent string) android.OutputPath {
+ return android.PathForOutput(ctx, ".export", ctx.ModuleName(), myModuleName, pathComponent)
+}
+
+func (contrib *CcApiContribution) CopyFilesWithTag(apiSurfaceContext android.ModuleContext) map[string]android.Paths {
+ // copy map.txt for now
+ // hardlinks cannot be created since nsjail creates a different mountpoint for out/
+ myDir := apiSurfaceContext.OtherModuleDir(contrib)
+ genMapTxt := outPathApiSurface(apiSurfaceContext, contrib.Name(), String(contrib.properties.Symbol_file))
+ apiSurfaceContext.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Description: "import map.txt file",
+ Input: android.PathForSource(apiSurfaceContext, myDir, String(contrib.properties.Symbol_file)),
+ Output: genMapTxt,
+ })
+
+ outputs := make(map[string]android.Paths)
+ outputs["map"] = []android.Path{genMapTxt}
+
+ if contrib.properties.Export_include_dir != nil {
+ includeDir := android.PathForSource(apiSurfaceContext, myDir, String(contrib.properties.Export_include_dir))
+ outputs["export_include_dir"] = []android.Path{includeDir}
+ }
+ return outputs
+}
+
+var _ multitree.ApiContribution = (*CcApiContribution)(nil)
+
+/*
+func (contrib *CcApiContribution) GenerateBuildFiles(apiSurfaceContext android.ModuleContext) android.Paths {
+ genAndroidBp := outPathApiSurface(apiSurfaceContext, contrib.Name(), "Android.bp")
+
+ // generate Android.bp
+ apiSurfaceContext.Build(pctx, android.BuildParams{
+ Rule: genApiSurfaceBuildFiles,
+ Description: "generate API surface build files",
+ Outputs: []android.WritablePath{genAndroidBp},
+ Args: map[string]string{
+ "name": contrib.Name() + "." + apiSurfaceContext.ModuleName(), //e.g. liblog.ndk
+ "symbol_file": String(contrib.properties.Symbol_file),
+ "first_version": String(contrib.properties.First_version),
+ },
+ })
+ return []android.Path{genAndroidBp}
+}
+*/
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
new file mode 100644
index 0000000..15b56d2
--- /dev/null
+++ b/cc/library_stub_test.go
@@ -0,0 +1,108 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ _ "fmt"
+ _ "sort"
+
+ "testing"
+
+ "android/soong/android"
+ "android/soong/multitree"
+)
+
+func TestCcApiStubLibraryOutputFiles(t *testing.T) {
+ bp := `
+ cc_api_stub_library {
+ name: "foo",
+ symbol_file: "foo.map.txt",
+ first_version: "29",
+ }
+ `
+ result := prepareForCcTest.RunTestWithBp(t, bp)
+ outputs := result.ModuleForTests("foo", "android_arm64_armv8-a_shared").AllOutputs()
+ expected_file_suffixes := []string{".c", "stub.map", ".o", ".so"}
+ for _, expected_file_suffix := range expected_file_suffixes {
+ android.AssertBoolEquals(t, expected_file_suffix+" file not found in output", true, android.SuffixInList(outputs, expected_file_suffix))
+ }
+}
+
+func TestCcApiStubLibraryVariants(t *testing.T) {
+ bp := `
+ cc_api_stub_library {
+ name: "foo",
+ symbol_file: "foo.map.txt",
+ first_version: "29",
+ }
+ `
+ result := prepareForCcTest.RunTestWithBp(t, bp)
+ variants := result.ModuleVariantsForTests("foo")
+ expected_variants := []string{"29", "30", "S", "Tiramisu"} //TODO: make this test deterministic by using fixtures
+ for _, expected_variant := range expected_variants {
+ android.AssertBoolEquals(t, expected_variant+" variant not found in foo", true, android.SubstringInList(variants, expected_variant))
+ }
+}
+
+func TestCcLibraryUsesCcApiStubLibrary(t *testing.T) {
+ bp := `
+ cc_api_stub_library {
+ name: "foo",
+ symbol_file: "foo.map.txt",
+ first_version: "29",
+ }
+ cc_library {
+ name: "foo_user",
+ shared_libs: [
+ "foo#29",
+ ],
+ }
+
+ `
+ prepareForCcTest.RunTestWithBp(t, bp)
+}
+
+func TestApiSurfaceOutputs(t *testing.T) {
+ bp := `
+ api_surface {
+ name: "mysdk",
+ contributions: [
+ "foo",
+ ],
+ }
+
+ cc_api_contribution {
+ name: "foo",
+ symbol_file: "foo.map.txt",
+ first_version: "29",
+ }
+ `
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ multitree.PrepareForTestWithApiSurface,
+ ).RunTestWithBp(t, bp)
+ mysdk := result.ModuleForTests("mysdk", "")
+
+ actual_surface_inputs := mysdk.Rule("phony").BuildParams.Inputs.Strings()
+ expected_file_suffixes := []string{"mysdk/foo/foo.map.txt"}
+ for _, expected_file_suffix := range expected_file_suffixes {
+ android.AssertBoolEquals(t, expected_file_suffix+" file not found in input", true, android.SuffixInList(actual_surface_inputs, expected_file_suffix))
+ }
+
+ // check args/inputs to rule
+ /*api_surface_gen_rule_args := result.ModuleForTests("mysdk", "").Rule("genApiSurfaceBuildFiles").Args
+ android.AssertStringEquals(t, "name", "foo.mysdk", api_surface_gen_rule_args["name"])
+ android.AssertStringEquals(t, "symbol_file", "foo.map.txt", api_surface_gen_rule_args["symbol_file"])*/
+}
diff --git a/cc/library_test.go b/cc/library_test.go
index d220e19..6d5eda2 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -379,3 +379,77 @@
gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
}
+
+func TestWholeStaticLibPrebuilts(t *testing.T) {
+ result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+ cc_prebuilt_library_static {
+ name: "libprebuilt",
+ srcs: ["foo.a"],
+ }
+
+ cc_library_static {
+ name: "libdirect",
+ whole_static_libs: ["libprebuilt"],
+ }
+
+ cc_library_static {
+ name: "libtransitive",
+ whole_static_libs: ["libdirect"],
+ }
+
+ cc_library_static {
+ name: "libdirect_with_srcs",
+ srcs: ["bar.c"],
+ whole_static_libs: ["libprebuilt"],
+ }
+
+ cc_library_static {
+ name: "libtransitive_with_srcs",
+ srcs: ["baz.c"],
+ whole_static_libs: ["libdirect_with_srcs"],
+ }
+ `)
+
+ libdirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
+ libtransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+ libdirectWithSrcs := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+ libtransitiveWithSrcs := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+ barObj := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+ bazObj := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libdirect.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libdirect.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libtransitive.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libtransitive.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libdirectWithSrcs.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libdirectWithSrcs.Args["arLibs"], "foo.a")
+ android.AssertStringListContains(t, "missing dependency on bar.o",
+ libdirectWithSrcs.Inputs.Strings(), barObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for bar.o",
+ libdirectWithSrcs.Args["arObjs"], barObj.Output.String())
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libtransitiveWithSrcs.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libtransitiveWithSrcs.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on bar.o",
+ libtransitiveWithSrcs.Inputs.Strings(), barObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for bar.o",
+ libtransitiveWithSrcs.Args["arObjs"], barObj.Output.String())
+
+ android.AssertStringListContains(t, "missing dependency on baz.o",
+ libtransitiveWithSrcs.Inputs.Strings(), bazObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for baz.o",
+ libtransitiveWithSrcs.Args["arObjs"], bazObj.Output.String())
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index 02d7047..04eab39 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -22,16 +22,16 @@
// than left undefined.
IsSanitizerExplicitlyDisabled(t SanitizerType) bool
- // SanitizeDep returns the value of the SanitizeDep flag, which is set if a module is a dependency of a
- // sanitized module.
- SanitizeDep() bool
+ // SanitizeDep returns true if the module is statically linked into another that is sanitized
+ // with the given sanitizer.
+ SanitizeDep(t SanitizerType) bool
+
+ // SetSanitizeDep marks a module as a static dependency of another module to be sanitized.
+ SetSanitizeDep(t SanitizerType)
// SetSanitizer enables or disables the specified sanitizer type if it's supported, otherwise this should panic.
SetSanitizer(t SanitizerType, b bool)
- // SetSanitizerDep returns true if the module is statically linked.
- SetSanitizeDep(b bool)
-
// StaticallyLinked returns true if the module is statically linked.
StaticallyLinked() bool
@@ -176,10 +176,14 @@
IsVndk() bool
IsVndkExt() bool
IsVndkPrivate() bool
+ IsVendorPublicLibrary() bool
+ IsVndkPrebuiltLibrary() bool
HasVendorVariant() bool
HasProductVariant() bool
HasNonSystemVariants() bool
+ ProductSpecific() bool
InProduct() bool
+ SdkAndPlatformVariantVisibleToMake() bool
// SubName returns the modules SubName, used for image and NDK/SDK variations.
SubName() string
@@ -352,6 +356,11 @@
Objects Objects
ReuseObjects Objects
+ // A static library may contain prebuilt static libraries included with whole_static_libs
+ // that won't appear in Objects. They are transitively available in
+ // WholeStaticLibsFromPrebuilts.
+ WholeStaticLibsFromPrebuilts android.Paths
+
// This isn't the actual transitive DepSet, shared library dependencies have been
// converted into static library analogues. It is only used to order the static
// library dependencies that were specified for the current module.
diff --git a/cc/linker.go b/cc/linker.go
index bea65d4..4e9404c 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -227,6 +227,9 @@
// local file name to pass to the linker as --dynamic-list
Dynamic_list *string `android:"path,arch_variant"`
+ // local files to pass to the linker as --script
+ Linker_scripts []string `android:"path,arch_variant"`
+
// list of static libs that should not be used to build this module
Exclude_static_libs []string `android:"arch_variant"`
@@ -386,9 +389,7 @@
}
deps.SystemSharedLibs = linker.Properties.System_shared_libs
- // In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may
- // inaccuarately appear unset, which can cause issues with circular dependencies.
- if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() {
+ if deps.SystemSharedLibs == nil {
// Provide a default system_shared_libs if it is unspecified. Note: If an
// empty list [] is specified, it implies that the module declines the
// default system_shared_libs.
@@ -602,6 +603,17 @@
flags.LdFlagsDeps = append(flags.LdFlagsDeps, dynamicList.Path())
}
}
+
+ linkerScriptPaths := android.PathsForModuleSrc(ctx, linker.Properties.Linker_scripts)
+ if len(linkerScriptPaths) > 0 && (ctx.Darwin() || ctx.Windows()) {
+ ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files")
+ } else {
+ for _, linkerScriptPath := range linkerScriptPaths {
+ flags.Local.LdFlags = append(flags.Local.LdFlags,
+ "-Wl,--script,"+linkerScriptPath.String())
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath)
+ }
+ }
}
return flags
diff --git a/cc/lto.go b/cc/lto.go
index 6d55579..2c274bd 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -123,7 +123,7 @@
// If the module does not have a profile, be conservative and limit cross TU inline
// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
- if !ctx.isPgoCompile() {
+ if !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
flags.Local.LdFlags = append(flags.Local.LdFlags,
"-Wl,-plugin-opt,-import-instr-limit=5")
}
diff --git a/cc/makevars.go b/cc/makevars.go
index b7aaaad..8154436 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -25,7 +25,7 @@
)
var (
- modulesAddedWallKey = android.NewOnceKey("ModulesAddedWall")
+ modulesWarningsAllowedKey = android.NewOnceKey("ModulesWarningsAllowed")
modulesUsingWnoErrorKey = android.NewOnceKey("ModulesUsingWnoError")
modulesMissingProfileFileKey = android.NewOnceKey("ModulesMissingProfileFile")
)
@@ -119,7 +119,7 @@
ctx.Strict("LSDUMP_PATHS", strings.Join(lsdumpPaths, " "))
ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
- ctx.Strict("SOONG_MODULES_ADDED_WALL", makeStringOfKeys(ctx, modulesAddedWallKey))
+ ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeStringOfKeys(ctx, modulesWarningsAllowedKey))
ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoErrorKey))
ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFileKey))
@@ -227,7 +227,6 @@
}
clangPrefix := secondPrefix + "CLANG_" + typePrefix
- clangExtras := "-B" + config.ToolPath(toolchain)
ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
@@ -235,7 +234,6 @@
"${config.CommonGlobalCflags}",
fmt.Sprintf("${config.%sGlobalCflags}", hod),
toolchain.ToolchainCflags(),
- clangExtras,
productExtraCflags,
}, " "))
ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
@@ -248,25 +246,52 @@
toolchain.Ldflags(),
toolchain.ToolchainLdflags(),
productExtraLdflags,
- clangExtras,
}, " "))
ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
fmt.Sprintf("${config.%sGlobalLldflags}", hod),
toolchain.Lldflags(),
toolchain.ToolchainLdflags(),
productExtraLdflags,
- clangExtras,
}, " "))
if target.Os.Class == android.Device {
- ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"SCUDO_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoMinimalRuntimeLibrary(toolchain), ".so"))
+ sanitizerVariables := map[string]string{
+ "ADDRESS_SANITIZER_RUNTIME_LIBRARY": config.AddressSanitizerRuntimeLibrary(toolchain),
+ "HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
+ "HWADDRESS_SANITIZER_STATIC_LIBRARY": config.HWAddressSanitizerStaticLibrary(toolchain),
+ "UBSAN_RUNTIME_LIBRARY": config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
+ "UBSAN_MINIMAL_RUNTIME_LIBRARY": config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
+ "TSAN_RUNTIME_LIBRARY": config.ThreadSanitizerRuntimeLibrary(toolchain),
+ "SCUDO_RUNTIME_LIBRARY": config.ScudoRuntimeLibrary(toolchain),
+ "SCUDO_MINIMAL_RUNTIME_LIBRARY": config.ScudoMinimalRuntimeLibrary(toolchain),
+ }
+
+ for variable, value := range sanitizerVariables {
+ ctx.Strict(secondPrefix+variable, value)
+ }
+
+ sanitizerLibs := android.SortedStringValues(sanitizerVariables)
+ var sanitizerLibStems []string
+ ctx.VisitAllModules(func(m android.Module) {
+ if !m.Enabled() {
+ return
+ }
+
+ ccModule, _ := m.(*Module)
+ if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+ return
+ }
+
+ if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
+ m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+ outputFile := ccModule.outputFile
+ if outputFile.Valid() {
+ sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+ }
+ }
+ })
+ sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+ ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
}
// This is used by external/gentoo/...
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 927fa2e..3456c32 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -46,7 +46,7 @@
if m, ok := module.(*Module); ok {
if installer, ok := m.installer.(*stubDecorator); ok {
- if canDumpAbi() {
+ if canDumpAbi(ctx.Config()) {
depPaths = append(depPaths, installer.abiDumpPath)
}
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 7efe134..0879257 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -53,9 +53,9 @@
abitidy = pctx.AndroidStaticRule("abitidy",
blueprint.RuleParams{
- Command: "$abitidy --all -i $in -o $out",
+ Command: "$abitidy --all $flags -i $in -o $out",
CommandDeps: []string{"$abitidy"},
- })
+ }, "flags")
abidiff = pctx.AndroidStaticRule("abidiff",
blueprint.RuleParams{
@@ -93,7 +93,7 @@
type libraryProperties struct {
// Relative path to the symbol map.
// An example file can be seen here: TODO(danalbert): Make an example.
- Symbol_file *string
+ Symbol_file *string `android:"path"`
// The first API level a library was available. A library will be generated
// for every API level beginning with this one.
@@ -104,6 +104,12 @@
// used. This is only needed to work around platform bugs like
// https://github.com/android-ndk/ndk/issues/265.
Unversioned_until *string
+
+ // If true, does not emit errors when APIs lacking type information are
+ // found. This is false by default and should not be enabled outside bionic,
+ // where it is enabled pending a fix for http://b/190554910 (no debug info
+ // for asm implemented symbols).
+ Allow_untyped_symbols *bool
}
type stubDecorator struct {
@@ -278,8 +284,12 @@
}
func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
+ // libc/libm stubs libraries end up mismatching with clang's internal definition of these
+ // functions (which have noreturn attributes and other things). Because we just want to create a
+ // stub with symbol definitions, and types aren't important in C, ignore the mismatch.
+ flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, "-fno-builtin")
return compileObjs(ctx, flagsToBuilderFlags(flags), "",
- android.Paths{src}, nil, nil, nil)
+ android.Paths{src}, nil, nil, nil, nil)
}
func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
@@ -315,8 +325,18 @@
}
// Feature flag.
-func canDumpAbi() bool {
- return runtime.GOOS != "darwin"
+func canDumpAbi(config android.Config) bool {
+ if runtime.GOOS == "darwin" {
+ return false
+ }
+ // abidw doesn't currently handle top-byte-ignore correctly. Disable ABI
+ // dumping for those configs while we wait for a fix. We'll still have ABI
+ // checking coverage from non-hwasan builds.
+ // http://b/190554910
+ if android.InList("hwaddress", config.SanitizeDevice()) {
+ return false
+ }
+ return true
}
// Feature flag to disable diffing against prebuilts.
@@ -339,14 +359,22 @@
"symbolList": symbolList.String(),
},
})
+
this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
this.apiLevel.String(), ctx.Arch().ArchType.String(),
this.libraryName(ctx), "abi.xml")
+ untypedFlag := "--abort-on-untyped-symbols"
+ if proptools.BoolDefault(this.properties.Allow_untyped_symbols, false) {
+ untypedFlag = ""
+ }
ctx.Build(pctx, android.BuildParams{
Rule: abitidy,
Description: fmt.Sprintf("abitidy %s", implementationLibrary),
Input: abiRawPath,
Output: this.abiDumpPath,
+ Args: map[string]string{
+ "flags": untypedFlag,
+ },
})
}
@@ -444,7 +472,7 @@
nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
c.versionScriptPath = nativeAbiResult.versionScript
- if canDumpAbi() {
+ if canDumpAbi(ctx.Config()) {
c.dumpAbi(ctx, nativeAbiResult.symbolList)
if canDiffAbi() {
c.diffAbi(ctx)
diff --git a/cc/object.go b/cc/object.go
index 24f6ed4..65a11e0 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -19,6 +19,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
)
//
@@ -37,7 +38,6 @@
SupportsSdk: true,
},
prebuiltModuleType: "cc_prebuilt_object",
- linkTypes: nil,
}
type objectLinker struct {
@@ -46,23 +46,30 @@
}
type objectBazelHandler struct {
- android.BazelHandler
-
module *Module
}
-func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
- bazelCtx := ctx.Config().BazelContext
- objPaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
- if ok {
- if len(objPaths) != 1 {
- ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
- return false
- }
+var _ BazelHandler = (*objectBazelHandler)(nil)
- handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
+func (handler *objectBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+}
+
+func (handler *objectBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
}
- return ok
+
+ if len(objPaths) != 1 {
+ ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
+ return
+ }
+
+ handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
}
type ObjectLinkerProperties struct {
@@ -133,6 +140,7 @@
Absolute_includes bazel.StringListAttribute
Stl *string
Linker_script bazel.LabelAttribute
+ sdkAttributes
}
// objectBp2Build is the bp2build converter from cc_object modules to the
@@ -191,11 +199,12 @@
Absolute_includes: compilerAttrs.absoluteIncludes,
Stl: compilerAttrs.stl,
Linker_script: linkerScript,
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_object",
- Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/cc/pgo.go b/cc/pgo.go
index aa0feae..0632c15 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -32,8 +32,8 @@
}
globalPgoProfileProjects = []string{
- "toolchain/pgo-profiles",
- "vendor/google_data/pgo_profile",
+ "toolchain/pgo-profiles/pgo",
+ "vendor/google_data/pgo_profile/pgo",
}
)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index c928ed9..a29e618 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,9 +16,11 @@
import (
"path/filepath"
+ "strings"
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
)
func init() {
@@ -188,6 +190,16 @@
TableOfContents: p.tocFile,
})
+ // TODO(b/220898484): Mainline module sdk prebuilts of stub libraries use a stub
+ // library as their source and must not be installed, but libclang_rt.* libraries
+ // have stubs because they are LLNDK libraries, but use an implementation library
+ // as their source and need to be installed. This discrepancy should be resolved
+ // without the prefix hack below.
+ if p.hasStubsVariants() && !p.buildStubs() && !ctx.Host() &&
+ !strings.HasPrefix(ctx.baseModuleName(), "libclang_rt.") {
+ ctx.Module().MakeUninstallable()
+ }
+
return outputFile
}
}
@@ -242,6 +254,7 @@
func NewPrebuiltLibrary(hod android.HostOrDeviceSupported, srcsProperty string) (*Module, *libraryDecorator) {
module, library := NewLibrary(hod)
module.compiler = nil
+ module.bazelable = true
prebuilt := &prebuiltLibraryLinker{
libraryDecorator: library,
@@ -327,9 +340,22 @@
Export_system_includes bazel.StringListAttribute
}
-func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module) {
- prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
- exportedIncludes := Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx, module)
+// TODO(b/228623543): The below is not entirely true until the bug is fixed. For now, both targets are always generated
+// Implements bp2build for cc_prebuilt_library modules. This will generate:
+// * Only a prebuilt_library_static if the shared.enabled property is set to false across all variants.
+// * Only a prebuilt_library_shared if the static.enabled property is set to false across all variants
+// * Both a prebuilt_library_static and prebuilt_library_shared if the aforementioned properties are not false across
+// all variants
+//
+// In all cases, prebuilt_library_static target names will be appended with "_bp2build_cc_library_static".
+func prebuiltLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ prebuiltLibraryStaticBp2Build(ctx, module, true)
+ prebuiltLibrarySharedBp2Build(ctx, module)
+}
+
+func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module, fullBuild bool) {
+ prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, true)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, nil)
attrs := &bazelPrebuiltLibraryStaticAttributes{
Static_library: prebuiltAttrs.Src,
@@ -339,11 +365,14 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "prebuilt_library_static",
- Bzl_load_location: "//build/bazel/rules:prebuilt_library_static.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_static.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(module.Name())
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+ if fullBuild {
+ name += "_bp2build_cc_library_static"
+ }
+ ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
}
type bazelPrebuiltLibrarySharedAttributes struct {
@@ -351,7 +380,7 @@
}
func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
- prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+ prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, false)
attrs := &bazelPrebuiltLibrarySharedAttributes{
Shared_library: prebuiltAttrs.Src,
@@ -359,11 +388,11 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "prebuilt_library_shared",
- Bzl_load_location: "//build/bazel/rules:prebuilt_library_shared.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_shared.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(module.Name())
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+ ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
}
type prebuiltObjectProperties struct {
@@ -378,25 +407,28 @@
}
type prebuiltStaticLibraryBazelHandler struct {
- android.BazelHandler
-
module *Module
library *libraryDecorator
}
-func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+var _ BazelHandler = (*prebuiltStaticLibraryBazelHandler)(nil)
+
+func (h *prebuiltStaticLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+}
+
+func (h *prebuiltStaticLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
- ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
- }
- if !ok {
- return false
+ ctx.ModuleErrorf(err.Error())
+ return
}
staticLibs := ccInfo.CcStaticLibraryFiles
if len(staticLibs) > 1 {
ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
- return false
+ return
}
// TODO(b/184543518): cc_prebuilt_library_static may have properties for re-exporting flags
@@ -411,7 +443,7 @@
if len(staticLibs) == 0 {
h.module.outputFile = android.OptionalPath{}
- return true
+ return
}
out := android.PathForBazelOut(ctx, staticLibs[0])
@@ -423,30 +455,31 @@
TransitiveStaticLibrariesForOrdering: depSet,
})
-
- return true
}
type prebuiltSharedLibraryBazelHandler struct {
- android.BazelHandler
-
module *Module
library *libraryDecorator
}
-func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+var _ BazelHandler = (*prebuiltSharedLibraryBazelHandler)(nil)
+
+func (h *prebuiltSharedLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+}
+
+func (h *prebuiltSharedLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
- ctx.ModuleErrorf("Error getting Bazel CcInfo for %s: %s", label, err)
- }
- if !ok {
- return false
+ ctx.ModuleErrorf(err.Error())
+ return
}
sharedLibs := ccInfo.CcSharedLibraryFiles
if len(sharedLibs) != 1 {
ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs)
- return false
+ return
}
// TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags
@@ -461,7 +494,7 @@
if len(sharedLibs) == 0 {
h.module.outputFile = android.OptionalPath{}
- return true
+ return
}
out := android.PathForBazelOut(ctx, sharedLibs[0])
@@ -486,8 +519,6 @@
h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
h.module.maybeUnhideFromMake()
-
- return true
}
func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt {
@@ -499,7 +530,16 @@
func (p *prebuiltObjectLinker) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
if len(p.properties.Srcs) > 0 {
- return p.Prebuilt.SingleSourcePath(ctx)
+ // Copy objects to a name matching the final installed name
+ in := p.Prebuilt.SingleSourcePath(ctx)
+ outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".o")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.CpExecutable,
+ Description: "prebuilt",
+ Output: outputFile,
+ Input: in,
+ })
+ return outputFile
}
return nil
}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 94f75fe..901f458 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -20,6 +20,7 @@
"android/soong/android"
"android/soong/bazel/cquery"
+
"github.com/google/blueprint"
)
@@ -29,6 +30,7 @@
)
func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
+ t.Helper()
result := android.GroupFixturePreparers(
prepareForPrebuiltTest,
fs.AddToFixture(),
@@ -449,3 +451,72 @@
expectedOutputFiles := []string{pathPrefix + "foo.so"}
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
}
+
+func TestPrebuiltStubNoinstall(t *testing.T) {
+ testFunc := func(t *testing.T, bp string) {
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.PrepareForTestWithMakevars,
+ ).RunTestWithBp(t, bp)
+
+ installRules := result.InstallMakeRulesForTesting(t)
+ var installedlibRule *android.InstallMakeRule
+ for i, rule := range installRules {
+ if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" {
+ if installedlibRule != nil {
+ t.Errorf("Duplicate install rules for %s", rule.Target)
+ }
+ installedlibRule = &installRules[i]
+ }
+ }
+ if installedlibRule == nil {
+ t.Errorf("No install rule found for installedlib")
+ return
+ }
+
+ android.AssertStringListDoesNotContain(t,
+ "installedlib has install dependency on stub",
+ installedlibRule.Deps,
+ "out/target/product/test_device/system/lib/stublib.so")
+ android.AssertStringListDoesNotContain(t,
+ "installedlib has order-only install dependency on stub",
+ installedlibRule.OrderOnlyDeps,
+ "out/target/product/test_device/system/lib/stublib.so")
+ }
+
+ const prebuiltStublibBp = `
+ cc_prebuilt_library {
+ name: "stublib",
+ prefer: true,
+ srcs: ["foo.so"],
+ stubs: {
+ versions: ["1"],
+ },
+ }
+ `
+
+ const installedlibBp = `
+ cc_library {
+ name: "installedlib",
+ shared_libs: ["stublib"],
+ }
+ `
+
+ t.Run("prebuilt without source", func(t *testing.T) {
+ testFunc(t, prebuiltStublibBp+installedlibBp)
+ })
+
+ const disabledSourceStublibBp = `
+ cc_library {
+ name: "stublib",
+ enabled: false,
+ stubs: {
+ versions: ["1"],
+ },
+ }
+ `
+
+ t.Run("prebuilt with disabled source", func(t *testing.T) {
+ testFunc(t, disabledSourceStublibBp+prebuiltStublibBp+installedlibBp)
+ })
+}
diff --git a/cc/proto.go b/cc/proto.go
index f3410bc..8e6d5ed 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -177,7 +177,7 @@
func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps {
var ret bp2buildProtoDeps
- protoInfo, ok := android.Bp2buildProtoProperties(ctx, m, protoSrcs)
+ protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
if !ok {
return ret
}
@@ -210,7 +210,7 @@
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{
Rule_class: rule_class,
- Bzl_load_location: "//build/bazel/rules:cc_proto.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
},
android.CommonAttributes{Name: name},
&protoAttrs)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 6c68822..8cf61fa 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -42,6 +42,7 @@
"-fno-omit-frame-pointer",
"-Wno-frame-larger-than=",
"-fsanitize-hwaddress-abi=platform",
+ "-mllvm", "-hwasan-use-after-scope=1",
}
// ThinLTO performs codegen during link time, thus these flags need to
@@ -75,7 +76,7 @@
minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
"-fno-sanitize-recover=integer,undefined"}
hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
- "export_memory_stats=0", "max_malloc_fill_size=0"}
+ "export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
)
type SanitizerType int
@@ -275,7 +276,7 @@
type SanitizeProperties struct {
Sanitize SanitizeUserProps `android:"arch_variant"`
SanitizerEnabled bool `blueprint:"mutated"`
- SanitizeDep bool `blueprint:"mutated"`
+ SanitizeDepTypes []SanitizerType `blueprint:"mutated"`
MinimalRuntimeDep bool `blueprint:"mutated"`
BuiltinsDep bool `blueprint:"mutated"`
UbsanRuntimeDep bool `blueprint:"mutated"`
@@ -479,8 +480,8 @@
s.Diag.Cfi = nil
}
- // Disable sanitizers that depend on the UBSan runtime for windows/darwin/musl builds.
- if !ctx.Os().Linux() || ctx.Os() == android.LinuxMusl {
+ // Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
+ if !ctx.Os().Linux() {
s.Cfi = nil
s.Diag.Cfi = nil
s.Misc_undefined = nil
@@ -489,6 +490,12 @@
s.Integer_overflow = nil
}
+ // Disable CFI for musl
+ if ctx.toolchain().Musl() {
+ s.Cfi = nil
+ s.Diag.Cfi = nil
+ }
+
// Also disable CFI for VNDK variants of components
if ctx.isVndk() && ctx.useVndk() {
if ctx.static() {
@@ -582,20 +589,12 @@
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
- minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
- builtinsRuntimeLib := config.BuiltinsRuntimeLibrary(ctx.toolchain()) + ".a"
- builtinsRuntimePath := "${config.ClangAsanLibDir}/" + builtinsRuntimeLib
if sanitize.Properties.MinimalRuntimeDep {
flags.Local.LdFlags = append(flags.Local.LdFlags,
- minimalRuntimePath,
"-Wl,--exclude-libs,"+minimalRuntimeLib)
}
- if sanitize.Properties.BuiltinsDep {
- flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
- }
-
if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
return flags
}
@@ -705,28 +704,22 @@
if len(sanitize.Properties.Sanitizers) > 0 {
sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
-
flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
- if ctx.Host() {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
+
+ if ctx.toolchain().Bionic() || ctx.toolchain().Musl() {
+ // Bionic and musl sanitizer runtimes have already been added as dependencies so that
+ // the right variant of the runtime will be used (with the "-android" or "-musl"
+ // suffixes), so don't let clang the runtime library.
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-link-runtime")
+ } else {
// Host sanitizers only link symbols in the final executable, so
// there will always be undefined symbols in intermediate libraries.
_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
- flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
- // non-Bionic toolchain prebuilts are missing UBSan's vptr and function sanitizers
- if !ctx.toolchain().Bionic() {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
- }
- }
-
- if enableMinimalRuntime(sanitize) {
- flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
- flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
- if !ctx.toolchain().Bionic() {
- flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
- }
+ // non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
}
if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -737,6 +730,12 @@
} else {
flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
}
+
+ if enableMinimalRuntime(sanitize) {
+ flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
+ }
+
// http://b/119329758, Android core does not boot up with this sanitizer yet.
if toDisableImplicitIntegerChange(flags.Local.CFlags) {
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
@@ -946,7 +945,7 @@
// determine defaultVariation in sanitizerMutator below.
// Instead, just mark SanitizeDep to forcefully create cfi variant.
enabled = true
- c.SetSanitizeDep(true)
+ c.SetSanitizeDep(t)
}
if enabled {
isSanitizableDependencyTag := c.SanitizableDepTagChecker()
@@ -961,15 +960,29 @@
if d.StaticallyLinked() && d.SanitizerSupported(t) {
// Rust does not support some of these sanitizers, so we need to check if it's
// supported before setting this true.
- d.SetSanitizeDep(true)
+ d.SetSanitizeDep(t)
}
} else {
- d.SetSanitizeDep(true)
+ d.SetSanitizeDep(t)
}
}
return true
})
}
+ } else if jniSanitizeable, ok := mctx.Module().(JniSanitizeable); ok {
+ // If it's a Java module with native dependencies through jni,
+ // set the sanitizer for them
+ if jniSanitizeable.IsSanitizerEnabledForJni(mctx, t.name()) {
+ mctx.VisitDirectDeps(func(child android.Module) {
+ if c, ok := child.(PlatformSanitizeable); ok &&
+ mctx.OtherModuleDependencyTag(child) == JniFuzzLibTag &&
+ c.SanitizePropDefined() &&
+ !c.SanitizeNever() &&
+ !c.IsSanitizerExplicitlyDisabled(t) {
+ c.SetSanitizeDep(t)
+ }
+ })
+ }
} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
// If an APEX module includes a lib which is enabled for a sanitizer T, then
// the APEX module is also enabled for the same sanitizer type.
@@ -1195,7 +1208,37 @@
}
}
- if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
+ addStaticDeps := func(deps ...string) {
+ // If we're using snapshots, redirect to snapshot whenever possible
+ snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+ for idx, dep := range deps {
+ if lib, ok := snapshot.StaticLibs[dep]; ok {
+ deps[idx] = lib
+ }
+ }
+
+ // static executable gets static runtime libs
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if c.Device() {
+ variations = append(variations, c.ImageVariation())
+ }
+ if c.UseSdk() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, deps...)
+
+ }
+ if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
+ addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain))
+ }
+ if c.sanitize.Properties.BuiltinsDep {
+ addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
+ }
+
+ if runtimeLibrary != "" && (toolchain.Bionic() || toolchain.Musl() || c.sanitize.Properties.UbsanRuntimeDep) {
// UBSan is supported on non-bionic linux host builds as well
// Adding dependency to the runtime library. We are using *FarVariation*
@@ -1206,23 +1249,8 @@
// Note that by adding dependency with {static|shared}DepTag, the lib is
// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
if c.staticBinary() {
- deps := append(extraStaticDeps, runtimeLibrary)
- // If we're using snapshots, redirect to snapshot whenever possible
- snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
- for idx, dep := range deps {
- if lib, ok := snapshot.StaticLibs[dep]; ok {
- deps[idx] = lib
- }
- }
-
- // static executable gets static runtime libs
- depTag := libraryDependencyTag{Kind: staticLibraryDependency}
- variations := append(mctx.Target().Variations(),
- blueprint.Variation{Mutator: "link", Variation: "static"})
- if c.Device() {
- variations = append(variations, c.ImageVariation())
- }
- mctx.AddFarVariationDependencies(variations, depTag, deps...)
+ addStaticDeps(runtimeLibrary)
+ addStaticDeps(extraStaticDeps...)
} else if !c.static() && !c.Header() {
// If we're using snapshots, redirect to snapshot whenever possible
snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
@@ -1247,6 +1275,10 @@
if c.Device() {
variations = append(variations, c.ImageVariation())
}
+ if c.UseSdk() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+ }
AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
}
// static lib does not have dependency to the runtime library. The
@@ -1263,6 +1295,11 @@
AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
}
+type JniSanitizeable interface {
+ android.Module
+ IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool
+}
+
func (c *Module) MinimalRuntimeDep() bool {
return c.sanitize.Properties.MinimalRuntimeDep
}
@@ -1279,8 +1316,14 @@
return c.sanitize.isSanitizerEnabled(t)
}
-func (c *Module) SanitizeDep() bool {
- return c.sanitize.Properties.SanitizeDep
+func (c *Module) SanitizeDep(t SanitizerType) bool {
+ for _, e := range c.sanitize.Properties.SanitizeDepTypes {
+ if t == e {
+ return true
+ }
+ }
+
+ return false
}
func (c *Module) StaticallyLinked() bool {
@@ -1299,9 +1342,9 @@
}
}
-func (c *Module) SetSanitizeDep(b bool) {
- if c.sanitize != nil {
- c.sanitize.Properties.SanitizeDep = b
+func (c *Module) SetSanitizeDep(t SanitizerType) {
+ if !c.SanitizeDep(t) {
+ c.sanitize.Properties.SanitizeDepTypes = append(c.sanitize.Properties.SanitizeDepTypes, t)
}
}
@@ -1318,7 +1361,7 @@
if c.Binary() && c.IsSanitizerEnabled(t) {
modules := mctx.CreateVariations(t.variationName())
modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
- } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
+ } else if c.IsSanitizerEnabled(t) || c.SanitizeDep(t) {
isSanitizerEnabled := c.IsSanitizerEnabled(t)
if c.StaticallyLinked() || c.Header() || t == Fuzzer {
// Static and header libs are split into non-sanitized and sanitized variants.
@@ -1340,8 +1383,6 @@
modules := mctx.CreateVariations("", t.variationName())
modules[0].(PlatformSanitizeable).SetSanitizer(t, false)
modules[1].(PlatformSanitizeable).SetSanitizer(t, true)
- modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
- modules[1].(PlatformSanitizeable).SetSanitizeDep(false)
if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
@@ -1374,7 +1415,6 @@
// Shared libs are not split. Only the sanitized variant is created.
modules := mctx.CreateVariations(t.variationName())
modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
- modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
// locate the asan libraries under /data/asan
if mctx.Device() && t == Asan && isSanitizerEnabled {
@@ -1388,11 +1428,13 @@
}
}
}
- c.SetSanitizeDep(false)
} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
- // APEX modules fall here
+ // APEX fuzz modules fall here
sanitizeable.AddSanitizerDependencies(mctx, t.name())
mctx.CreateVariations(t.variationName())
+ } else if _, ok := mctx.Module().(JniSanitizeable); ok {
+ // Java fuzz modules fall here
+ mctx.CreateVariations(t.variationName())
} else if c, ok := mctx.Module().(*Module); ok {
//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
@@ -1491,12 +1533,10 @@
if !Bool(sanitize.Properties.Sanitize.Address) &&
!Bool(sanitize.Properties.Sanitize.Hwaddress) &&
!Bool(sanitize.Properties.Sanitize.Fuzzer) &&
-
(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
len(sanitize.Properties.Sanitize.Misc_undefined) > 0 ||
Bool(sanitize.Properties.Sanitize.Undefined) ||
Bool(sanitize.Properties.Sanitize.All_undefined)) &&
-
!(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
Bool(sanitize.Properties.Sanitize.Diag.Cfi) ||
Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 0070e40..c1ca034 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -24,11 +24,7 @@
var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
cc_library_shared {
- name: "libclang_rt.asan-aarch64-android",
- }
-
- cc_library_shared {
- name: "libclang_rt.asan-arm-android",
+ name: "libclang_rt.asan",
}
`))
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 753d74c..9d40ad0 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -680,6 +680,9 @@
Input: in,
})
+ // binary snapshots need symlinking
+ p.setSymlinkList(ctx)
+
return outputFile
}
diff --git a/cc/test.go b/cc/test.go
index d8b7833..5703571 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -25,14 +25,22 @@
"android/soong/tradefed"
)
-type TestProperties struct {
+// TestLinkerProperties properties to be registered via the linker
+type TestLinkerProperties struct {
// if set, build against the gtest library. Defaults to true.
Gtest *bool
- // if set, use the isolated gtest runner. Defaults to false.
+ // if set, use the isolated gtest runner. Defaults to true if gtest is also true and the arch is Windows, false
+ // otherwise.
Isolated *bool
}
+// TestInstallerProperties properties to be registered via the installer
+type TestInstallerProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be installed into.
+ Test_suites []string `android:"arch_variant"`
+}
+
// Test option struct.
type TestOptions struct {
// The UID that you want to run the test as on a device.
@@ -83,10 +91,6 @@
// list of binary modules that should be installed alongside the test
Data_bins []string `android:"arch_variant"`
- // list of compatibility suites (for example "cts", "vts") that the module should be
- // installed into.
- Test_suites []string `android:"arch_variant"`
-
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
Test_config *string `android:"path,arch_variant"`
@@ -243,12 +247,21 @@
}
type testDecorator struct {
- Properties TestProperties
- linker *baseLinker
+ LinkerProperties TestLinkerProperties
+ InstallerProperties TestInstallerProperties
+ installer *baseInstaller
+ linker *baseLinker
}
func (test *testDecorator) gtest() bool {
- return BoolDefault(test.Properties.Gtest, true)
+ return BoolDefault(test.LinkerProperties.Gtest, true)
+}
+
+func (test *testDecorator) isolated(ctx BaseModuleContext) bool {
+ if !ctx.Windows() {
+ return BoolDefault(test.LinkerProperties.Isolated, false)
+ }
+ return BoolDefault(test.LinkerProperties.Isolated, false)
}
func (test *testDecorator) testBinary() bool {
@@ -283,7 +296,7 @@
if test.gtest() {
if ctx.useSdk() && ctx.Device() {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
- } else if BoolDefault(test.Properties.Isolated, false) {
+ } else if test.isolated(ctx) {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
// The isolated library requires liblog, but adding it
// as a static library means unit tests cannot override
@@ -316,7 +329,11 @@
}
func (test *testDecorator) linkerProps() []interface{} {
- return []interface{}{&test.Properties}
+ return []interface{}{&test.LinkerProperties}
+}
+
+func (test *testDecorator) installerProps() []interface{} {
+ return []interface{}{&test.InstallerProperties}
}
func NewTestInstaller() *baseInstaller {
@@ -324,7 +341,7 @@
}
type testBinary struct {
- testDecorator
+ *testDecorator
*binaryDecorator
*baseCompiler
Properties TestBinaryProperties
@@ -358,6 +375,10 @@
return flags
}
+func (test *testBinary) installerProps() []interface{} {
+ return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
+}
+
func (test *testBinary) install(ctx ModuleContext, file android.Path) {
// TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
testInstallBase := "/data/local/tmp"
@@ -411,7 +432,7 @@
var options []tradefed.Option
configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.StopServicesSetup", options})
}
- if Bool(test.testDecorator.Properties.Isolated) {
+ if test.isolated(ctx) {
configs = append(configs, tradefed.Option{Name: "not-shardable", Value: "true"})
}
if test.Properties.Test_options.Run_test_as != nil {
@@ -441,7 +462,7 @@
}
test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
- test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config, testInstallBase)
+ test.Properties.Test_config_template, test.testDecorator.InstallerProperties.Test_suites, configs, test.Properties.Auto_gen_config, testInstallBase)
test.extraTestConfigs = android.PathsForModuleSrc(ctx, test.Properties.Test_options.Extra_test_configs)
@@ -466,8 +487,9 @@
binary.baseInstaller = NewTestInstaller()
test := &testBinary{
- testDecorator: testDecorator{
- linker: binary.baseLinker,
+ testDecorator: &testDecorator{
+ linker: binary.baseLinker,
+ installer: binary.baseInstaller,
},
binaryDecorator: binary,
baseCompiler: NewBaseCompiler(),
@@ -479,12 +501,14 @@
}
type testLibrary struct {
- testDecorator
+ *testDecorator
*libraryDecorator
}
func (test *testLibrary) linkerProps() []interface{} {
- return append(test.testDecorator.linkerProps(), test.libraryDecorator.linkerProps()...)
+ var props []interface{}
+ props = append(props, test.testDecorator.linkerProps()...)
+ return append(props, test.libraryDecorator.linkerProps()...)
}
func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
@@ -504,16 +528,22 @@
return flags
}
+func (test *testLibrary) installerProps() []interface{} {
+ return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
+}
+
func NewTestLibrary(hod android.HostOrDeviceSupported) *Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.baseInstaller = NewTestInstaller()
test := &testLibrary{
- testDecorator: testDecorator{
- linker: library.baseLinker,
+ testDecorator: &testDecorator{
+ linker: library.baseLinker,
+ installer: library.baseInstaller,
},
libraryDecorator: library,
}
module.linker = test
+ module.installer = test
return module
}
diff --git a/cc/testing.go b/cc/testing.go
index 3d0c10a..ecdae8b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -29,6 +29,7 @@
RegisterBinaryBuildComponents(ctx)
RegisterLibraryBuildComponents(ctx)
RegisterLibraryHeadersBuildComponents(ctx)
+ RegisterLibraryStubBuildComponents(ctx)
ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
ctx.RegisterModuleType("cc_object", ObjectFactory)
@@ -86,42 +87,20 @@
}
cc_prebuilt_library_static {
- name: "libclang_rt.builtins-arm-android",
+ name: "libclang_rt.builtins",
defaults: ["toolchain_libs_defaults"],
- native_bridge_supported: true,
+ host_supported: true,
+ vendor_available: true,
vendor_ramdisk_available: true,
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.builtins-aarch64-android",
- defaults: ["toolchain_libs_defaults"],
native_bridge_supported: true,
- vendor_ramdisk_available: true,
}
cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ name: "libclang_rt.hwasan",
defaults: ["toolchain_libs_defaults"],
}
cc_prebuilt_library_static {
- name: "libclang_rt.builtins-i686-android",
- defaults: ["toolchain_libs_defaults"],
- vendor_ramdisk_available: true,
- native_bridge_supported: true,
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.builtins-x86_64-android",
- defaults: [
- "linux_bionic_supported",
- "toolchain_libs_defaults",
- ],
- native_bridge_supported: true,
- vendor_ramdisk_available: true,
- }
-
- cc_prebuilt_library_static {
name: "libunwind",
defaults: [
"linux_bionic_supported",
@@ -132,30 +111,7 @@
}
cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-arm-android",
- defaults: ["toolchain_libs_defaults"],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-aarch64-android",
- defaults: ["toolchain_libs_defaults"],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-i686-android",
- defaults: ["toolchain_libs_defaults"],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-x86_64-android",
- defaults: [
- "linux_bionic_supported",
- "toolchain_libs_defaults",
- ],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-x86_64",
+ name: "libclang_rt.fuzzer",
defaults: [
"linux_bionic_supported",
"toolchain_libs_defaults",
@@ -164,7 +120,12 @@
// Needed for sanitizer
cc_prebuilt_library_shared {
- name: "libclang_rt.ubsan_standalone-aarch64-android",
+ name: "libclang_rt.ubsan_standalone",
+ defaults: ["toolchain_libs_defaults"],
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.ubsan_minimal",
defaults: ["toolchain_libs_defaults"],
}
diff --git a/cc/tidy.go b/cc/tidy.go
index 1f5f56d..ac1521b 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -76,9 +76,10 @@
// the global WITH_TIDY or module 'tidy' property is true.
flags.Tidy = true
- // If explicitly enabled, by global default or local tidy property,
+ // If explicitly enabled, by global WITH_TIDY or local tidy:true property,
// set flags.NeedTidyFiles to make this module depend on .tidy files.
- if ctx.Config().ClangTidy() || Bool(tidy.Properties.Tidy) {
+ // Note that locally set tidy:true is ignored if ALLOW_LOCAL_TIDY_TRUE is not set to true.
+ if ctx.Config().IsEnvTrue("WITH_TIDY") || (ctx.Config().IsEnvTrue("ALLOW_LOCAL_TIDY_TRUE") && Bool(tidy.Properties.Tidy)) {
flags.NeedTidyFiles = true
}
@@ -95,13 +96,24 @@
if !android.SubstringInList(flags.TidyFlags, "-header-filter=") {
defaultDirs := ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS")
headerFilter := "-header-filter="
+ // Default header filter should include only the module directory,
+ // not the out/soong/.../ModuleDir/...
+ // Otherwise, there will be too many warnings from generated files in out/...
+ // If a module wants to see warnings in the generated source files,
+ // it should specify its own -header-filter flag.
if defaultDirs == "" {
- headerFilter += ctx.ModuleDir() + "/"
+ headerFilter += "^" + ctx.ModuleDir() + "/"
} else {
- headerFilter += "\"(" + ctx.ModuleDir() + "/|" + defaultDirs + ")\""
+ headerFilter += "\"(^" + ctx.ModuleDir() + "/|" + defaultDirs + ")\""
}
flags.TidyFlags = append(flags.TidyFlags, headerFilter)
}
+ // Work around RBE bug in parsing clang-tidy flags, replace "--flag" with "-flag".
+ // Some C/C++ modules added local tidy flags like --header-filter= and --extra-arg-before=.
+ doubleDash := regexp.MustCompile("^('?)--(.*)$")
+ for i, s := range flags.TidyFlags {
+ flags.TidyFlags[i] = doubleDash.ReplaceAllString(s, "$1-$2")
+ }
// If clang-tidy is not enabled globally, add the -quiet flag.
if !ctx.Config().ClangTidy() {
diff --git a/cc/tidy_test.go b/cc/tidy_test.go
new file mode 100644
index 0000000..339b302
--- /dev/null
+++ b/cc/tidy_test.go
@@ -0,0 +1,98 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestWithTidy(t *testing.T) {
+ // When WITH_TIDY=1 or (ALLOW_LOCAL_TIDY_TRUE=1 and local tidy:true)
+ // a C++ library should depend on .tidy files.
+ testCases := []struct {
+ withTidy, allowLocalTidyTrue string // "_" means undefined
+ needTidyFile []bool // for {libfoo_0, libfoo_1} and {libbar_0, libbar_1}
+ }{
+ {"_", "_", []bool{false, false, false}},
+ {"_", "0", []bool{false, false, false}},
+ {"_", "1", []bool{false, true, false}},
+ {"_", "true", []bool{false, true, false}},
+ {"0", "_", []bool{false, false, false}},
+ {"0", "1", []bool{false, true, false}},
+ {"1", "_", []bool{true, true, false}},
+ {"1", "false", []bool{true, true, false}},
+ {"1", "1", []bool{true, true, false}},
+ {"true", "_", []bool{true, true, false}},
+ }
+ bp := `
+ cc_library_shared {
+ name: "libfoo_0", // depends on .tidy if WITH_TIDY=1
+ srcs: ["foo.c"],
+ }
+ cc_library_shared { // depends on .tidy if WITH_TIDY=1 or ALLOW_LOCAL_TIDY_TRUE=1
+ name: "libfoo_1",
+ srcs: ["foo.c"],
+ tidy: true,
+ }
+ cc_library_shared { // no .tidy
+ name: "libfoo_2",
+ srcs: ["foo.c"],
+ tidy: false,
+ }
+ cc_library_static {
+ name: "libbar_0", // depends on .tidy if WITH_TIDY=1
+ srcs: ["bar.c"],
+ }
+ cc_library_static { // depends on .tidy if WITH_TIDY=1 or ALLOW_LOCAL_TIDY_TRUE=1
+ name: "libbar_1",
+ srcs: ["bar.c"],
+ tidy: true,
+ }
+ cc_library_static { // no .tidy
+ name: "libbar_2",
+ srcs: ["bar.c"],
+ tidy: false,
+ }`
+ for index, test := range testCases {
+ testName := fmt.Sprintf("case%d,%v,%v", index, test.withTidy, test.allowLocalTidyTrue)
+ t.Run(testName, func(t *testing.T) {
+ testEnv := map[string]string{}
+ if test.withTidy != "_" {
+ testEnv["WITH_TIDY"] = test.withTidy
+ }
+ if test.allowLocalTidyTrue != "_" {
+ testEnv["ALLOW_LOCAL_TIDY_TRUE"] = test.allowLocalTidyTrue
+ }
+ ctx := android.GroupFixturePreparers(prepareForCcTest, android.FixtureMergeEnv(testEnv)).RunTestWithBp(t, bp)
+ for n := 0; n < 3; n++ {
+ checkLibraryRule := func(foo, variant, ruleName string) {
+ libName := fmt.Sprintf("lib%s_%d", foo, n)
+ tidyFile := "out/soong/.intermediates/" + libName + "/" + variant + "/obj/" + foo + ".tidy"
+ depFiles := ctx.ModuleForTests(libName, variant).Rule(ruleName).Validations.Strings()
+ if test.needTidyFile[n] {
+ android.AssertStringListContains(t, libName+" needs .tidy file", depFiles, tidyFile)
+ } else {
+ android.AssertStringListDoesNotContain(t, libName+" does not need .tidy file", depFiles, tidyFile)
+ }
+ }
+ checkLibraryRule("foo", "android_arm64_armv8-a_shared", "ld")
+ checkLibraryRule("bar", "android_arm64_armv8-a_static", "ar")
+ }
+ })
+ }
+}
diff --git a/cc/util.go b/cc/util.go
index b256b9a..4e10037 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -15,9 +15,7 @@
package cc
import (
- "fmt"
"path/filepath"
- "regexp"
"strings"
"android/soong/android"
@@ -30,30 +28,12 @@
return android.JoinWithPrefix(dirs.Strings(), "-I")
}
-func ldDirsToFlags(dirs []string) string {
- return android.JoinWithPrefix(dirs, "-L")
-}
-
-func libNamesToFlags(names []string) string {
- return android.JoinWithPrefix(names, "-l")
-}
-
var indexList = android.IndexList
var inList = android.InList
var filterList = android.FilterList
var removeListFromList = android.RemoveListFromList
var removeFromList = android.RemoveFromList
-var libNameRegexp = regexp.MustCompile(`^lib(.*)$`)
-
-func moduleToLibName(module string) (string, error) {
- matches := libNameRegexp.FindStringSubmatch(module)
- if matches == nil {
- return "", fmt.Errorf("Library module name %s does not start with lib", module)
- }
- return matches[1], nil
-}
-
func flagsToBuilderFlags(in Flags) builderFlags {
return builderFlags{
globalCommonFlags: strings.Join(in.Global.CommonFlags, " "),
@@ -113,13 +93,6 @@
return list
}
-func addSuffix(list []string, suffix string) []string {
- for i := range list {
- list[i] = list[i] + suffix
- }
- return list
-}
-
// linkDirOnDevice/linkName -> target
func makeSymlinkCmd(linkDirOnDevice string, linkName string, target string) string {
dir := filepath.Join("$(PRODUCT_OUT)", linkDirOnDevice)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 8a17e2e..e7c05ac 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -146,6 +146,7 @@
// binary flags
Symlinks []string `json:",omitempty"`
StaticExecutable bool `json:",omitempty"`
+ InstallInRoot bool `json:",omitempty"`
// dependencies
SharedLibs []string `json:",omitempty"`
@@ -320,6 +321,7 @@
// binary flags
prop.Symlinks = m.Symlinks()
prop.StaticExecutable = m.StaticExecutable()
+ prop.InstallInRoot = m.InstallInRoot()
prop.SharedLibs = m.SnapshotSharedLibs()
// static libs dependencies are required to collect the NOTICE files.
prop.StaticLibs = m.SnapshotStaticLibs()
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 645b2cc..2bb43ab 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -741,6 +741,7 @@
src: "bin",
},
},
+ symlinks: ["binfoo", "binbar"],
}
vendor_snapshot_binary {
@@ -920,7 +921,21 @@
ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
// bin is installed by bin.vendor_binary.31.arm64
- ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant).Output("bin")
+ bin64Module := ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant)
+ bin64Module.Output("bin")
+
+ // also test symlinks
+ bin64MkEntries := android.AndroidMkEntriesForTest(t, ctx, bin64Module.Module())
+ bin64KatiSymlinks := bin64MkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"]
+
+ // Either AndroidMk entries contain symlinks, or symlinks should be installed by Soong
+ for _, symlink := range []string{"binfoo", "binbar"} {
+ if inList(symlink, bin64KatiSymlinks) {
+ continue
+ }
+
+ bin64Module.Output(symlink)
+ }
// bin32 is installed by bin32.vendor_binary.31.arm64
ctx.ModuleForTests("bin32.vendor_binary.31.arm64", binary32Variant).Output("bin32")
diff --git a/cc/vndk.go b/cc/vndk.go
index c9c9f2c..bf6148b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -450,7 +450,7 @@
// Therefore, by removing the library here, we cause it to only be installed if libc
// depends on it.
func llndkLibrariesTxtFactory() android.SingletonModule {
- return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "LLNDK_LIBRARIES", "libclang_rt.hwasan-")
+ return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "LLNDK_LIBRARIES", "libclang_rt.hwasan")
}
// vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 1280553..aaca1dd 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -114,6 +114,10 @@
fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
+ // Discard the PT_INTERP section so that the linker doesn't need to be passed the
+ // --no-dynamic-linker flag.
+ fmt.Fprintln(script, " /DISCARD/ : { *(.interp) }")
+
fmt.Fprintln(script, "}")
fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
diff --git a/cmd/go2bp/go2bp.go b/cmd/go2bp/go2bp.go
index 07cb5df..fb5a746 100644
--- a/cmd/go2bp/go2bp.go
+++ b/cmd/go2bp/go2bp.go
@@ -335,12 +335,15 @@
}
cmd := exec.Command("go", "list", "-json", "./...")
- output, err := cmd.Output()
- if err != nil {
- fmt.Fprintf(os.Stderr, "Failed to dump the go packages: %v\n", err)
+ var stdoutb, stderrb bytes.Buffer
+ cmd.Stdout = &stdoutb
+ cmd.Stderr = &stderrb
+ if err := cmd.Run(); err != nil {
+ fmt.Fprintf(os.Stderr, "Running %q to dump the Go packages failed: %v, stderr:\n%s\n",
+ cmd.String(), err, stderrb.Bytes())
os.Exit(1)
}
- decoder := json.NewDecoder(bytes.NewReader(output))
+ decoder := json.NewDecoder(bytes.NewReader(stdoutb.Bytes()))
pkgs := []*GoPackage{}
pkgMap := map[string]*GoPackage{}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0577c86..7cb8ab7 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -407,10 +407,10 @@
s.Finish()
- if failures == 1 {
+ if failures.count == 1 {
log.Fatal("1 failure")
- } else if failures > 1 {
- log.Fatalf("%d failures", failures)
+ } else if failures.count > 1 {
+ log.Fatalf("%d failures %q", failures.count, failures.fails)
} else {
fmt.Fprintln(output, "Success")
}
@@ -522,19 +522,23 @@
})
}
-type failureCount int
+type failureCount struct {
+ count int
+ fails []string
+}
func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
if result.Error != nil {
- *f += 1
+ f.count += 1
+ f.fails = append(f.fails, result.Action.Description)
}
}
func (f *failureCount) Message(level status.MsgLevel, message string) {
if level >= status.ErrorLvl {
- *f += 1
+ f.count += 1
}
}
diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go
index a4fe3e4..8b9de52 100644
--- a/cmd/path_interposer/main.go
+++ b/cmd/path_interposer/main.go
@@ -12,6 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// This tool tries to prohibit access to tools on the system on which the build
+// is run.
+//
+// The rationale is that if the build uses a binary that is not shipped in the
+// source tree, it is unknowable which version of that binary will be installed
+// and therefore the output of the build will be unpredictable. Therefore, we
+// should make every effort to use only tools under our control.
+//
+// This is currently implemented by a "sandbox" that sets $PATH to a specific,
+// single directory and creates a symlink for every binary in $PATH in it. That
+// symlink will point to path_interposer, which then uses an embedded
+// configuration to determine whether to allow access to the binary (in which
+// case it calls the original executable) or not (in which case it fails). It
+// can also optionally log invocations.
+//
+// This, of course, does not help if one invokes the tool in question with its
+// full path.
package main
import (
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 4fa7486..4f7451d 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -38,9 +38,11 @@
)
var (
- sandboxesRoot string
- manifestFile string
- keepOutDir bool
+ sandboxesRoot string
+ outputDir string
+ manifestFile string
+ keepOutDir bool
+ writeIfChanged bool
)
const (
@@ -51,10 +53,14 @@
func init() {
flag.StringVar(&sandboxesRoot, "sandbox-path", "",
"root of temp directory to put the sandbox into")
+ flag.StringVar(&outputDir, "output-dir", "",
+ "directory which will contain all output files and only output files")
flag.StringVar(&manifestFile, "manifest", "",
"textproto manifest describing the sandboxed command(s)")
flag.BoolVar(&keepOutDir, "keep-out-dir", false,
"whether to keep the sandbox directory when done")
+ flag.BoolVar(&writeIfChanged, "write-if-changed", false,
+ "only write the output files if they have changed")
}
func usageViolation(violation string) {
@@ -197,23 +203,19 @@
// createCommandScript will create and return an exec.Cmd that runs rawCommand.
//
// rawCommand is executed via a script in the sandbox.
-// tempDir is the temporary where the script is created.
-// toDirInSandBox is the path containing the script in the sbox environment.
-// toDirInSandBox is the path containing the script in the sbox environment.
-// seed is a unique integer used to distinguish different scripts that might be at location.
+// scriptPath is the temporary where the script is created.
+// scriptPathInSandbox is the path to the script in the sbox environment.
//
// returns an exec.Cmd that can be ran from within sbox context if no error, or nil if error.
// caller must ensure script is cleaned up if function succeeds.
//
-func createCommandScript(rawCommand string, tempDir, toDirInSandbox string, seed int) (*exec.Cmd, error) {
- scriptName := fmt.Sprintf("sbox_command.%d.bash", seed)
- scriptPathAndName := joinPath(tempDir, scriptName)
- err := os.WriteFile(scriptPathAndName, []byte(rawCommand), 0644)
+func createCommandScript(rawCommand, scriptPath, scriptPathInSandbox string) (*exec.Cmd, error) {
+ err := os.WriteFile(scriptPath, []byte(rawCommand), 0644)
if err != nil {
return nil, fmt.Errorf("failed to write command %s... to %s",
- rawCommand[0:40], scriptPathAndName)
+ rawCommand[0:40], scriptPath)
}
- return exec.Command("bash", joinPath(toDirInSandbox, filepath.Base(scriptName))), nil
+ return exec.Command("bash", scriptPathInSandbox), nil
}
// readManifest reads an sbox manifest from a textproto file.
@@ -241,6 +243,12 @@
return "", fmt.Errorf("command is required")
}
+ // Remove files from the output directory
+ err = clearOutputDirectory(command.CopyAfter, outputDir, writeType(writeIfChanged))
+ if err != nil {
+ return "", err
+ }
+
pathToTempDirInSbox := tempDir
if command.GetChdir() {
pathToTempDirInSbox = "."
@@ -252,7 +260,7 @@
}
// Copy in any files specified by the manifest.
- err = copyFiles(command.CopyBefore, "", tempDir, false)
+ err = copyFiles(command.CopyBefore, "", tempDir, requireFromExists, alwaysWrite)
if err != nil {
return "", err
}
@@ -277,7 +285,10 @@
return "", err
}
- cmd, err := createCommandScript(rawCommand, tempDir, pathToTempDirInSbox, commandIndex)
+ scriptName := fmt.Sprintf("sbox_command.%d.bash", commandIndex)
+ scriptPath := joinPath(tempDir, scriptName)
+ scriptPathInSandbox := joinPath(pathToTempDirInSbox, scriptName)
+ cmd, err := createCommandScript(rawCommand, scriptPath, scriptPathInSandbox)
if err != nil {
return "", err
}
@@ -306,7 +317,7 @@
// especially useful for linters with baselines that print an error message on failure
// with a command to copy the output lint errors to the new baseline. Use a copy instead of
// a move to leave the sandbox intact for manual inspection
- copyFiles(command.CopyAfter, tempDir, "", true)
+ copyFiles(command.CopyAfter, tempDir, "", allowFromNotExists, writeType(writeIfChanged))
}
// If the command was executed but failed with an error, print a debugging message before
@@ -315,9 +326,9 @@
fmt.Fprintf(os.Stderr,
"The failing command was run inside an sbox sandbox in temporary directory\n"+
"%s\n"+
- "The failing command line was:\n"+
+ "The failing command line can be found in\n"+
"%s\n",
- tempDir, rawCommand)
+ tempDir, scriptPath)
}
// Write the command's combined stdout/stderr.
@@ -327,39 +338,16 @@
return "", err
}
- missingOutputErrors := validateOutputFiles(command.CopyAfter, tempDir)
-
- if len(missingOutputErrors) > 0 {
- // find all created files for making a more informative error message
- createdFiles := findAllFilesUnder(tempDir)
-
- // build error message
- errorMessage := "mismatch between declared and actual outputs\n"
- errorMessage += "in sbox command(" + rawCommand + ")\n\n"
- errorMessage += "in sandbox " + tempDir + ",\n"
- errorMessage += fmt.Sprintf("failed to create %v files:\n", len(missingOutputErrors))
- for _, missingOutputError := range missingOutputErrors {
- errorMessage += " " + missingOutputError.Error() + "\n"
- }
- if len(createdFiles) < 1 {
- errorMessage += "created 0 files."
- } else {
- errorMessage += fmt.Sprintf("did create %v files:\n", len(createdFiles))
- creationMessages := createdFiles
- maxNumCreationLines := 10
- if len(creationMessages) > maxNumCreationLines {
- creationMessages = creationMessages[:maxNumCreationLines]
- creationMessages = append(creationMessages, fmt.Sprintf("...%v more", len(createdFiles)-maxNumCreationLines))
- }
- for _, creationMessage := range creationMessages {
- errorMessage += " " + creationMessage + "\n"
- }
- }
-
- return "", errors.New(errorMessage)
+ err = validateOutputFiles(command.CopyAfter, tempDir, outputDir, rawCommand)
+ if err != nil {
+ return "", err
}
+
// the created files match the declared files; now move them
- err = moveFiles(command.CopyAfter, tempDir, "")
+ err = moveFiles(command.CopyAfter, tempDir, "", writeType(writeIfChanged))
+ if err != nil {
+ return "", err
+ }
return depFile, nil
}
@@ -380,8 +368,9 @@
// validateOutputFiles verifies that all files that have a rule to be copied out of the sandbox
// were created by the command.
-func validateOutputFiles(copies []*sbox_proto.Copy, sandboxDir string) []error {
+func validateOutputFiles(copies []*sbox_proto.Copy, sandboxDir, outputDir, rawCommand string) error {
var missingOutputErrors []error
+ var incorrectOutputDirectoryErrors []error
for _, copyPair := range copies {
fromPath := joinPath(sandboxDir, copyPair.GetFrom())
fileInfo, err := os.Stat(fromPath)
@@ -392,17 +381,91 @@
if fileInfo.IsDir() {
missingOutputErrors = append(missingOutputErrors, fmt.Errorf("%s: not a file", fromPath))
}
+
+ toPath := copyPair.GetTo()
+ if rel, err := filepath.Rel(outputDir, toPath); err != nil {
+ return err
+ } else if strings.HasPrefix(rel, "../") {
+ incorrectOutputDirectoryErrors = append(incorrectOutputDirectoryErrors,
+ fmt.Errorf("%s is not under %s", toPath, outputDir))
+ }
}
- return missingOutputErrors
+
+ const maxErrors = 10
+
+ if len(incorrectOutputDirectoryErrors) > 0 {
+ errorMessage := ""
+ more := 0
+ if len(incorrectOutputDirectoryErrors) > maxErrors {
+ more = len(incorrectOutputDirectoryErrors) - maxErrors
+ incorrectOutputDirectoryErrors = incorrectOutputDirectoryErrors[:maxErrors]
+ }
+
+ for _, err := range incorrectOutputDirectoryErrors {
+ errorMessage += err.Error() + "\n"
+ }
+ if more > 0 {
+ errorMessage += fmt.Sprintf("...%v more", more)
+ }
+
+ return errors.New(errorMessage)
+ }
+
+ if len(missingOutputErrors) > 0 {
+ // find all created files for making a more informative error message
+ createdFiles := findAllFilesUnder(sandboxDir)
+
+ // build error message
+ errorMessage := "mismatch between declared and actual outputs\n"
+ errorMessage += "in sbox command(" + rawCommand + ")\n\n"
+ errorMessage += "in sandbox " + sandboxDir + ",\n"
+ errorMessage += fmt.Sprintf("failed to create %v files:\n", len(missingOutputErrors))
+ for _, missingOutputError := range missingOutputErrors {
+ errorMessage += " " + missingOutputError.Error() + "\n"
+ }
+ if len(createdFiles) < 1 {
+ errorMessage += "created 0 files."
+ } else {
+ errorMessage += fmt.Sprintf("did create %v files:\n", len(createdFiles))
+ creationMessages := createdFiles
+ if len(creationMessages) > maxErrors {
+ creationMessages = creationMessages[:maxErrors]
+ creationMessages = append(creationMessages, fmt.Sprintf("...%v more", len(createdFiles)-maxErrors))
+ }
+ for _, creationMessage := range creationMessages {
+ errorMessage += " " + creationMessage + "\n"
+ }
+ }
+
+ return errors.New(errorMessage)
+ }
+
+ return nil
}
-// copyFiles copies files in or out of the sandbox. If allowFromNotExists is true then errors
-// caused by a from path not existing are ignored.
-func copyFiles(copies []*sbox_proto.Copy, fromDir, toDir string, allowFromNotExists bool) error {
+type existsType bool
+
+const (
+ requireFromExists existsType = false
+ allowFromNotExists = true
+)
+
+type writeType bool
+
+const (
+ alwaysWrite writeType = false
+ onlyWriteIfChanged = true
+)
+
+// copyFiles copies files in or out of the sandbox. If exists is allowFromNotExists then errors
+// caused by a from path not existing are ignored. If write is onlyWriteIfChanged then the output
+// file is compared to the input file and not written to if it is the same, avoiding updating
+// the timestamp.
+func copyFiles(copies []*sbox_proto.Copy, fromDir, toDir string, exists existsType, write writeType) error {
for _, copyPair := range copies {
fromPath := joinPath(fromDir, copyPair.GetFrom())
toPath := joinPath(toDir, copyPair.GetTo())
- err := copyOneFile(fromPath, toPath, copyPair.GetExecutable(), allowFromNotExists)
+ err := copyOneFile(fromPath, toPath, copyPair.GetExecutable(), exists, write)
if err != nil {
return fmt.Errorf("error copying %q to %q: %w", fromPath, toPath, err)
}
@@ -411,8 +474,11 @@
}
// copyOneFile copies a file and its permissions. If forceExecutable is true it adds u+x to the
-// permissions. If allowFromNotExists is true it returns nil if the from path doesn't exist.
-func copyOneFile(from string, to string, forceExecutable, allowFromNotExists bool) error {
+// permissions. If exists is allowFromNotExists it returns nil if the from path doesn't exist.
+// If write is onlyWriteIfChanged then the output file is compared to the input file and not written to
+// if it is the same, avoiding updating the timestamp.
+func copyOneFile(from string, to string, forceExecutable bool, exists existsType,
+ write writeType) error {
err := os.MkdirAll(filepath.Dir(to), 0777)
if err != nil {
return err
@@ -420,7 +486,7 @@
stat, err := os.Stat(from)
if err != nil {
- if os.IsNotExist(err) && allowFromNotExists {
+ if os.IsNotExist(err) && exists == allowFromNotExists {
return nil
}
return err
@@ -431,6 +497,10 @@
perm = perm | 0100 // u+x
}
+ if write == onlyWriteIfChanged && filesHaveSameContents(from, to) {
+ return nil
+ }
+
in, err := os.Open(from)
if err != nil {
return err
@@ -504,7 +574,7 @@
to := applyPathMappings(rspFile.PathMappings, from)
// Copy the file into the sandbox.
- err := copyOneFile(from, joinPath(toDir, to), false, false)
+ err := copyOneFile(from, joinPath(toDir, to), false, requireFromExists, alwaysWrite)
if err != nil {
return err
}
@@ -551,9 +621,10 @@
// moveFiles moves files specified by a set of copy rules. It uses os.Rename, so it is restricted
// to moving files where the source and destination are in the same filesystem. This is OK for
-// sbox because the temporary directory is inside the out directory. It updates the timestamp
-// of the new file.
-func moveFiles(copies []*sbox_proto.Copy, fromDir, toDir string) error {
+// sbox because the temporary directory is inside the out directory. If write is onlyWriteIfChanged
+// then the output file is compared to the input file and not written to if it is the same, avoiding
+// updating the timestamp. Otherwise it always updates the timestamp of the new file.
+func moveFiles(copies []*sbox_proto.Copy, fromDir, toDir string, write writeType) error {
for _, copyPair := range copies {
fromPath := joinPath(fromDir, copyPair.GetFrom())
toPath := joinPath(toDir, copyPair.GetTo())
@@ -562,6 +633,10 @@
return err
}
+ if write == onlyWriteIfChanged && filesHaveSameContents(fromPath, toPath) {
+ continue
+ }
+
err = os.Rename(fromPath, toPath)
if err != nil {
return err
@@ -578,6 +653,37 @@
return nil
}
+// clearOutputDirectory removes all files in the output directory if write is alwaysWrite, or
+// any files not listed in copies if write is onlyWriteIfChanged
+func clearOutputDirectory(copies []*sbox_proto.Copy, outputDir string, write writeType) error {
+ if outputDir == "" {
+ return fmt.Errorf("output directory must be set")
+ }
+
+ if write == alwaysWrite {
+ // When writing all the output files remove the whole output directory
+ return os.RemoveAll(outputDir)
+ }
+
+ outputFiles := make(map[string]bool, len(copies))
+ for _, copyPair := range copies {
+ outputFiles[copyPair.GetTo()] = true
+ }
+
+ existingFiles := findAllFilesUnder(outputDir)
+ for _, existingFile := range existingFiles {
+ fullExistingFile := filepath.Join(outputDir, existingFile)
+ if !outputFiles[fullExistingFile] {
+ err := os.Remove(fullExistingFile)
+ if err != nil {
+ return fmt.Errorf("failed to remove obsolete output file %s: %w", fullExistingFile, err)
+ }
+ }
+ }
+
+ return nil
+}
+
// Rewrite one or more depfiles so that it doesn't include the (randomized) sandbox directory
// to an output file.
func rewriteDepFiles(ins []string, out string) error {
@@ -621,6 +727,66 @@
return filepath.Join(dir, file)
}
+// filesHaveSameContents compares the contents if two files, returning true if they are the same
+// and returning false if they are different or any errors occur.
+func filesHaveSameContents(a, b string) bool {
+ // Compare the sizes of the two files
+ statA, err := os.Stat(a)
+ if err != nil {
+ return false
+ }
+ statB, err := os.Stat(b)
+ if err != nil {
+ return false
+ }
+
+ if statA.Size() != statB.Size() {
+ return false
+ }
+
+ // Open the two files
+ fileA, err := os.Open(a)
+ if err != nil {
+ return false
+ }
+ defer fileA.Close()
+ fileB, err := os.Open(b)
+ if err != nil {
+ return false
+ }
+ defer fileB.Close()
+
+ // Compare the files 1MB at a time
+ const bufSize = 1 * 1024 * 1024
+ bufA := make([]byte, bufSize)
+ bufB := make([]byte, bufSize)
+
+ remain := statA.Size()
+ for remain > 0 {
+ toRead := int64(bufSize)
+ if toRead > remain {
+ toRead = remain
+ }
+
+ _, err = io.ReadFull(fileA, bufA[:toRead])
+ if err != nil {
+ return false
+ }
+ _, err = io.ReadFull(fileB, bufB[:toRead])
+ if err != nil {
+ return false
+ }
+
+ if bytes.Compare(bufA[:toRead], bufB[:toRead]) != 0 {
+ return false
+ }
+
+ remain -= toRead
+ }
+
+ return true
+}
+
func makeAbsPathEnv(pathEnv string) (string, error) {
pathEnvElements := filepath.SplitList(pathEnv)
for i, p := range pathEnvElements {
diff --git a/cmd/sbox/sbox_test.go b/cmd/sbox/sbox_test.go
new file mode 100644
index 0000000..3f13d2f
--- /dev/null
+++ b/cmd/sbox/sbox_test.go
@@ -0,0 +1,127 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func Test_filesHaveSameContents(t *testing.T) {
+
+ tests := []struct {
+ name string
+ a string
+ b string
+ missingA bool
+ missingB bool
+
+ equal bool
+ }{
+ {
+ name: "empty",
+ a: "",
+ b: "",
+ equal: true,
+ },
+ {
+ name: "equal",
+ a: "foo",
+ b: "foo",
+ equal: true,
+ },
+ {
+ name: "unequal",
+ a: "foo",
+ b: "bar",
+ equal: false,
+ },
+ {
+ name: "unequal different sizes",
+ a: "foo",
+ b: "foobar",
+ equal: false,
+ },
+ {
+ name: "equal large",
+ a: strings.Repeat("a", 2*1024*1024),
+ b: strings.Repeat("a", 2*1024*1024),
+ equal: true,
+ },
+ {
+ name: "equal large unaligned",
+ a: strings.Repeat("a", 2*1024*1024+10),
+ b: strings.Repeat("a", 2*1024*1024+10),
+ equal: true,
+ },
+ {
+ name: "unequal large",
+ a: strings.Repeat("a", 2*1024*1024),
+ b: strings.Repeat("a", 2*1024*1024-1) + "b",
+ equal: false,
+ },
+ {
+ name: "unequal large unaligned",
+ a: strings.Repeat("a", 2*1024*1024+10),
+ b: strings.Repeat("a", 2*1024*1024+9) + "b",
+ equal: false,
+ },
+ {
+ name: "missing a",
+ missingA: true,
+ b: "foo",
+ equal: false,
+ },
+ {
+ name: "missing b",
+ a: "foo",
+ missingB: true,
+ equal: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tempDir, err := os.MkdirTemp("", "testFilesHaveSameContents")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %s", err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ fileA := filepath.Join(tempDir, "a")
+ fileB := filepath.Join(tempDir, "b")
+
+ if !tt.missingA {
+ err := ioutil.WriteFile(fileA, []byte(tt.a), 0666)
+ if err != nil {
+ t.Fatalf("failed to write %s: %s", fileA, err)
+ }
+ }
+
+ if !tt.missingB {
+ err := ioutil.WriteFile(fileB, []byte(tt.b), 0666)
+ if err != nil {
+ t.Fatalf("failed to write %s: %s", fileB, err)
+ }
+ }
+
+ if got := filesHaveSameContents(fileA, fileB); got != tt.equal {
+ t.Errorf("filesHaveSameContents() = %v, want %v", got, tt.equal)
+ }
+ })
+ }
+}
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index e85163e..72af3e0 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -25,6 +25,7 @@
"golang-protobuf-android",
"soong",
"soong-android",
+ "soong-provenance",
"soong-bp2build",
"soong-ui-metrics_proto",
],
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index b3a6ee0..bd5a1bd 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -26,10 +26,11 @@
"android/soong/android"
"android/soong/bp2build"
"android/soong/shared"
+ "android/soong/ui/metrics/bp2build_metrics_proto"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/deptools"
- "github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/metrics"
androidProtobuf "google.golang.org/protobuf/android"
)
@@ -128,37 +129,33 @@
return configuration
}
-// Bazel-enabled mode. Soong runs in two passes.
-// First pass: Analyze the build tree, but only store all bazel commands
-// needed to correctly evaluate the tree in the second pass.
-// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
-// the incorrect results from the first pass, and file I/O is expensive.
-func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
- bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration)
+// Bazel-enabled mode. Attaches a mutator to queue Bazel requests, adds a
+// BeforePrepareBuildActionsHook to invoke Bazel, and then uses Bazel metadata
+// for modules that should be handled by Bazel.
+func runMixedModeBuild(configuration android.Config, ctx *android.Context, extraNinjaDeps []string) {
+ ctx.EventHandler.Begin("mixed_build")
+ defer ctx.EventHandler.End("mixed_build")
- // Invoke bazel commands and save results for second pass.
- if err := configuration.BazelContext.InvokeBazel(); err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
+ bazelHook := func() error {
+ ctx.EventHandler.Begin("bazel")
+ defer ctx.EventHandler.End("bazel")
+ return configuration.BazelContext.InvokeBazel()
}
- // Second pass: Full analysis, using the bazel command results. Output ninja file.
- secondConfig, err := android.ConfigForAdditionalRun(configuration)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
- }
- secondCtx := newContext(secondConfig)
- ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig)
+ ctx.SetBeforePrepareBuildActionsHook(bazelHook)
+
+ ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, ctx.Context, configuration)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- globListFiles := writeBuildGlobsNinjaFile(secondCtx.SrcDir(), configuration.SoongOutDir(), secondCtx.Globs, configuration)
+ globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
ninjaDeps = append(ninjaDeps, globListFiles...)
- writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
+ writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps)
}
// Run the code-generation phase to convert BazelTargetModules to BUILD files.
func runQueryView(queryviewDir, queryviewMarker string, configuration android.Config, ctx *android.Context) {
+ ctx.EventHandler.Begin("queryview")
+ defer ctx.EventHandler.End("queryview")
codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
@@ -169,9 +166,13 @@
touch(shared.JoinPath(topDir, queryviewMarker))
}
-func writeMetrics(configuration android.Config) {
- metricsFile := filepath.Join(configuration.SoongOutDir(), "soong_build_metrics.pb")
- err := android.WriteMetrics(configuration, metricsFile)
+func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
+ if len(metricsDir) < 1 {
+ fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
+ os.Exit(1)
+ }
+ metricsFile := filepath.Join(metricsDir, "soong_build_metrics.pb")
+ err := android.WriteMetrics(configuration, eventHandler, metricsFile)
if err != nil {
fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
os.Exit(1)
@@ -191,18 +192,23 @@
ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
}
-func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
+func writeBuildGlobsNinjaFile(ctx *android.Context, buildDir string, config interface{}) []string {
+ ctx.EventHandler.Begin("globs_ninja_file")
+ defer ctx.EventHandler.End("globs_ninja_file")
+
globDir := bootstrap.GlobDirectory(buildDir, globListDir)
bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
- GlobLister: globs,
+ GlobLister: ctx.Globs,
GlobFile: globFile,
GlobDir: globDir,
- SrcDir: srcDir,
+ SrcDir: ctx.SrcDir(),
}, config)
return bootstrap.GlobFileListFiles(globDir)
}
-func writeDepFile(outputFile string, ninjaDeps []string) {
+func writeDepFile(outputFile string, eventHandler metrics.EventHandler, ninjaDeps []string) {
+ eventHandler.Begin("ninja_deps")
+ defer eventHandler.End("ninja_deps")
depFile := shared.JoinPath(topDir, outputFile+".d")
err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps)
if err != nil {
@@ -214,7 +220,7 @@
// doChosenActivity runs Soong for a specific activity, like bp2build, queryview
// or the actual Soong build for the build.ninja file. Returns the top level
// output file of the specific activity.
-func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
+func doChosenActivity(configuration android.Config, extraNinjaDeps []string, logDir string) string {
mixedModeBuild := configuration.BazelContext.BazelEnabled()
generateBazelWorkspace := bp2buildMarker != ""
generateQueryView := bazelQueryViewDir != ""
@@ -230,36 +236,36 @@
blueprintArgs := cmdlineArgs
- var stopBefore bootstrap.StopBefore
- if generateModuleGraphFile {
- stopBefore = bootstrap.StopBeforeWriteNinja
- } else if generateQueryView {
- stopBefore = bootstrap.StopBeforePrepareBuildActions
- } else if generateDocFile {
- stopBefore = bootstrap.StopBeforePrepareBuildActions
- } else {
- stopBefore = bootstrap.DoEverything
- }
-
ctx := newContext(configuration)
if mixedModeBuild {
runMixedModeBuild(configuration, ctx, extraNinjaDeps)
} else {
+ var stopBefore bootstrap.StopBefore
+ if generateModuleGraphFile {
+ stopBefore = bootstrap.StopBeforeWriteNinja
+ } else if generateQueryView {
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ } else if generateDocFile {
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ } else {
+ stopBefore = bootstrap.DoEverything
+ }
+
ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- globListFiles := writeBuildGlobsNinjaFile(ctx.SrcDir(), configuration.SoongOutDir(), ctx.Globs, configuration)
+ globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
ninjaDeps = append(ninjaDeps, globListFiles...)
// Convert the Soong module graph into Bazel BUILD files.
if generateQueryView {
queryviewMarkerFile := bazelQueryViewDir + ".marker"
runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
- writeDepFile(queryviewMarkerFile, ninjaDeps)
+ writeDepFile(queryviewMarkerFile, *ctx.EventHandler, ninjaDeps)
return queryviewMarkerFile
} else if generateModuleGraphFile {
writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
- writeDepFile(moduleGraphFile, ninjaDeps)
+ writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps)
return moduleGraphFile
} else if generateDocFile {
// TODO: we could make writeDocs() return the list of documentation files
@@ -269,16 +275,16 @@
fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
os.Exit(1)
}
- writeDepFile(docFile, ninjaDeps)
+ writeDepFile(docFile, *ctx.EventHandler, ninjaDeps)
return docFile
} else {
// The actual output (build.ninja) was written in the RunBlueprint() call
// above
- writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
+ writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps)
}
}
- writeMetrics(configuration)
+ writeMetrics(configuration, *ctx.EventHandler, logDir)
return cmdlineArgs.OutFile
}
@@ -334,7 +340,12 @@
extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
}
- finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+ // Bypass configuration.Getenv, as LOG_DIR does not need to be dependency tracked. By definition, it will
+ // change between every CI build, so tracking it would require re-running Soong for every build.
+ logDir := availableEnv["LOG_DIR"]
+
+ finalOutputFile := doChosenActivity(configuration, extraNinjaDeps, logDir)
+
writeUsedEnvironmentFile(configuration, finalOutputFile)
}
@@ -466,98 +477,112 @@
// an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files.
func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
- // Register an alternate set of singletons and mutators for bazel
- // conversion for Bazel conversion.
- bp2buildCtx := android.NewContext(configuration)
+ eventHandler := metrics.EventHandler{}
+ var metrics bp2build.CodegenMetrics
+ eventHandler.Do("bp2build", func() {
- // Soong internals like LoadHooks behave differently when running as
- // bp2build. This is the bit to differentiate between Soong-as-Soong and
- // Soong-as-bp2build.
- bp2buildCtx.SetRunningAsBp2build()
+ // Register an alternate set of singletons and mutators for bazel
+ // conversion for Bazel conversion.
+ bp2buildCtx := android.NewContext(configuration)
- // Propagate "allow misssing dependencies" bit. This is normally set in
- // newContext(), but we create bp2buildCtx without calling that method.
- bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
- bp2buildCtx.SetNameInterface(newNameResolver(configuration))
- bp2buildCtx.RegisterForBazelConversion()
+ // Soong internals like LoadHooks behave differently when running as
+ // bp2build. This is the bit to differentiate between Soong-as-Soong and
+ // Soong-as-bp2build.
+ bp2buildCtx.SetRunningAsBp2build()
- // The bp2build process is a purely functional process that only depends on
- // Android.bp files. It must not depend on the values of per-build product
- // configurations or variables, since those will generate different BUILD
- // files based on how the user has configured their tree.
- bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
- modulePaths, err := bp2buildCtx.ListModulePaths(".")
- if err != nil {
- panic(err)
- }
+ // Propagate "allow misssing dependencies" bit. This is normally set in
+ // newContext(), but we create bp2buildCtx without calling that method.
+ bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
+ bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+ bp2buildCtx.RegisterForBazelConversion()
- extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+ // The bp2build process is a purely functional process that only depends on
+ // Android.bp files. It must not depend on the values of per-build product
+ // configurations or variables, since those will generate different BUILD
+ // files based on how the user has configured their tree.
+ bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+ modulePaths, err := bp2buildCtx.ListModulePaths(".")
+ if err != nil {
+ panic(err)
+ }
- // Run the loading and analysis pipeline to prepare the graph of regular
- // Modules parsed from Android.bp files, and the BazelTargetModules mapped
- // from the regular Modules.
- blueprintArgs := cmdlineArgs
- ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
- ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+ extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
- globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx.SrcDir(), configuration.SoongOutDir(), bp2buildCtx.Globs, configuration)
- ninjaDeps = append(ninjaDeps, globListFiles...)
+ // Run the loading and analysis pipeline to prepare the graph of regular
+ // Modules parsed from Android.bp files, and the BazelTargetModules mapped
+ // from the regular Modules.
+ blueprintArgs := cmdlineArgs
+ ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- // Run the code-generation phase to convert BazelTargetModules to BUILD files
- // and print conversion metrics to the user.
- codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
- metrics := bp2build.Codegen(codegenContext)
+ globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
+ ninjaDeps = append(ninjaDeps, globListFiles...)
- generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
- workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
+ // Run the code-generation phase to convert BazelTargetModules to BUILD files
+ // and print conversion metrics to the user.
+ codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
+ metrics = bp2build.Codegen(codegenContext)
- excludes := []string{
- "bazel-bin",
- "bazel-genfiles",
- "bazel-out",
- "bazel-testlogs",
- "bazel-" + filepath.Base(topDir),
- }
+ generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
+ workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
- if outDir[0] != '/' {
- excludes = append(excludes, outDir)
- }
+ excludes := []string{
+ "bazel-bin",
+ "bazel-genfiles",
+ "bazel-out",
+ "bazel-testlogs",
+ "bazel-" + filepath.Base(topDir),
+ }
- existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
- os.Exit(1)
- }
+ if outDir[0] != '/' {
+ excludes = append(excludes, outDir)
+ }
- pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
- excludes = append(excludes, pathsToIgnoredBuildFiles...)
+ existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+ os.Exit(1)
+ }
- excludes = append(excludes, getTemporaryExcludes()...)
+ pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
+ excludes = append(excludes, pathsToIgnoredBuildFiles...)
- symlinkForestDeps := bp2build.PlantSymlinkForest(
- topDir, workspaceRoot, generatedRoot, ".", excludes)
+ excludes = append(excludes, getTemporaryExcludes()...)
+
+ symlinkForestDeps := bp2build.PlantSymlinkForest(
+ topDir, workspaceRoot, generatedRoot, ".", excludes)
+
+ ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+ ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+
+ writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
+
+ // Create an empty bp2build marker file.
+ touch(shared.JoinPath(topDir, bp2buildMarker))
+ })
// Only report metrics when in bp2build mode. The metrics aren't relevant
// for queryview, since that's a total repo-wide conversion and there's a
// 1:1 mapping for each module.
metrics.Print()
- writeBp2BuildMetrics(&metrics, configuration)
-
- ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
- ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
-
- writeDepFile(bp2buildMarker, ninjaDeps)
-
- // Create an empty bp2build marker file.
- touch(shared.JoinPath(topDir, bp2buildMarker))
+ writeBp2BuildMetrics(&metrics, configuration, eventHandler)
}
// Write Bp2Build metrics into $LOG_DIR
-func writeBp2BuildMetrics(metrics *bp2build.CodegenMetrics, configuration android.Config) {
+func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics,
+ configuration android.Config, eventHandler metrics.EventHandler) {
+ for _, event := range eventHandler.CompletedEvents() {
+ codegenMetrics.Events = append(codegenMetrics.Events,
+ &bp2build_metrics_proto.Event{
+ Name: event.Id,
+ StartTime: uint64(event.Start.UnixNano()),
+ RealTime: event.RuntimeNanoseconds(),
+ })
+ }
metricsDir := configuration.Getenv("LOG_DIR")
if len(metricsDir) < 1 {
fmt.Fprintf(os.Stderr, "\nMissing required env var for generating bp2build metrics: LOG_DIR\n")
os.Exit(1)
}
- metrics.Write(metricsDir)
+ codegenMetrics.Write(metricsDir)
}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 13b20f4..a03a86a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -23,6 +23,7 @@
"path/filepath"
"strconv"
"strings"
+ "syscall"
"time"
"android/soong/shared"
@@ -204,6 +205,8 @@
buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
+ setMaxFiles(buildCtx)
+
{
// The order of the function calls is important. The last defer function call
// is the first one that is executed to save the rbe metrics to a protobuf
@@ -218,7 +221,6 @@
}
defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
defer met.Dump(soongMetricsFile)
- defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
}
// Read the time at the starting point.
@@ -604,3 +606,24 @@
}
}
}
+
+func setMaxFiles(ctx build.Context) {
+ var limits syscall.Rlimit
+
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
+ if err != nil {
+ ctx.Println("Failed to get file limit:", err)
+ return
+ }
+
+ ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
+ if limits.Cur == limits.Max {
+ return
+ }
+
+ limits.Cur = limits.Max
+ err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
+ if err != nil {
+ ctx.Println("Failed to increase file limit:", err)
+ }
+}
diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp
new file mode 100644
index 0000000..0ba3b07
--- /dev/null
+++ b/cmd/symbols_map/Android.bp
@@ -0,0 +1,34 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+ name: "symbols_map",
+ srcs: [
+ "elf.go",
+ "r8.go",
+ "symbols_map.go",
+ ],
+ testSrcs: [
+ "elf_test.go",
+ "r8_test.go",
+ ],
+ deps: [
+ "blueprint-pathtools",
+ "golang-protobuf-encoding-prototext",
+ "soong-response",
+ "symbols_map_proto",
+ ],
+}
+
+bootstrap_go_package {
+ name: "symbols_map_proto",
+ pkgPath: "android/soong/cmd/symbols_map/symbols_map_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "symbols_map_proto/symbols_map.pb.go",
+ ],
+}
diff --git a/cmd/symbols_map/elf.go b/cmd/symbols_map/elf.go
new file mode 100644
index 0000000..950e3b2
--- /dev/null
+++ b/cmd/symbols_map/elf.go
@@ -0,0 +1,118 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "debug/elf"
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+)
+
+const gnuBuildID = "GNU\x00"
+
+// elfIdentifier extracts the elf build ID from an elf file. If allowMissing is true it returns
+// an empty identifier if the file exists but the build ID note does not.
+func elfIdentifier(filename string, allowMissing bool) (string, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return "", fmt.Errorf("failed to open %s: %w", filename, err)
+ }
+ defer f.Close()
+
+ return elfIdentifierFromReaderAt(f, filename, allowMissing)
+}
+
+// elfIdentifierFromReaderAt extracts the elf build ID from a ReaderAt. If allowMissing is true it
+// returns an empty identifier if the file exists but the build ID note does not.
+func elfIdentifierFromReaderAt(r io.ReaderAt, filename string, allowMissing bool) (string, error) {
+ f, err := elf.NewFile(r)
+ if err != nil {
+ if allowMissing {
+ if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
+ return "", nil
+ }
+ if _, ok := err.(*elf.FormatError); ok {
+ // The file was not an elf file.
+ return "", nil
+ }
+ }
+ return "", fmt.Errorf("failed to parse elf file %s: %w", filename, err)
+ }
+ defer f.Close()
+
+ buildIDNote := f.Section(".note.gnu.build-id")
+ if buildIDNote == nil {
+ if allowMissing {
+ return "", nil
+ }
+ return "", fmt.Errorf("failed to find .note.gnu.build-id in %s", filename)
+ }
+
+ buildIDs, err := readNote(buildIDNote.Open(), f.ByteOrder)
+ if err != nil {
+ return "", fmt.Errorf("failed to read .note.gnu.build-id: %w", err)
+ }
+
+ for name, desc := range buildIDs {
+ if name == gnuBuildID {
+ return hex.EncodeToString(desc), nil
+ }
+ }
+
+ return "", nil
+}
+
+// readNote reads the contents of a note section, returning it as a map from name to descriptor.
+func readNote(note io.Reader, byteOrder binary.ByteOrder) (map[string][]byte, error) {
+ var noteHeader struct {
+ Namesz uint32
+ Descsz uint32
+ Type uint32
+ }
+
+ notes := make(map[string][]byte)
+ for {
+ err := binary.Read(note, byteOrder, ¬eHeader)
+ if err != nil {
+ if err == io.EOF {
+ return notes, nil
+ }
+ return nil, fmt.Errorf("failed to read note header: %w", err)
+ }
+
+ nameBuf := make([]byte, align4(noteHeader.Namesz))
+ err = binary.Read(note, byteOrder, &nameBuf)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read note name: %w", err)
+ }
+ name := string(nameBuf[:noteHeader.Namesz])
+
+ descBuf := make([]byte, align4(noteHeader.Descsz))
+ err = binary.Read(note, byteOrder, &descBuf)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read note desc: %w", err)
+ }
+ notes[name] = descBuf[:noteHeader.Descsz]
+ }
+}
+
+// align4 rounds the input up to the next multiple of 4.
+func align4(i uint32) uint32 {
+ return (i + 3) &^ 3
+}
diff --git a/cmd/symbols_map/elf_test.go b/cmd/symbols_map/elf_test.go
new file mode 100644
index 0000000..a94c87f
--- /dev/null
+++ b/cmd/symbols_map/elf_test.go
@@ -0,0 +1,152 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "debug/elf"
+ "encoding/binary"
+ "reflect"
+ "testing"
+)
+
+func Test_elfIdentifierFromReaderAt_BadElfFile(t *testing.T) {
+ tests := []struct {
+ name string
+ contents string
+ }{
+ {
+ name: "empty",
+ contents: "",
+ },
+ {
+ name: "text",
+ contents: "#!/bin/bash\necho foobar",
+ },
+ {
+ name: "empty elf",
+ contents: emptyElfFile(),
+ },
+ {
+ name: "short section header",
+ contents: shortSectionHeaderElfFile(),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ buf := bytes.NewReader([]byte(tt.contents))
+ _, err := elfIdentifierFromReaderAt(buf, "<>", false)
+ if err == nil {
+ t.Errorf("expected error reading bad elf file without allowMissing")
+ }
+ _, err = elfIdentifierFromReaderAt(buf, "<>", true)
+ if err != nil {
+ t.Errorf("expected no error reading bad elf file with allowMissing, got %q", err.Error())
+ }
+ })
+ }
+}
+
+func Test_readNote(t *testing.T) {
+ note := []byte{
+ 0x04, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ 0x47, 0x4e, 0x55, 0x00,
+ 0xca, 0xaf, 0x44, 0xd2, 0x82, 0x78, 0x68, 0xfe, 0xc0, 0x90, 0xa3, 0x43, 0x85, 0x36, 0x6c, 0xc7,
+ }
+
+ descs, err := readNote(bytes.NewBuffer(note), binary.LittleEndian)
+ if err != nil {
+ t.Fatalf("unexpected error in readNote: %s", err)
+ }
+
+ expectedDescs := map[string][]byte{
+ "GNU\x00": []byte{0xca, 0xaf, 0x44, 0xd2, 0x82, 0x78, 0x68, 0xfe, 0xc0, 0x90, 0xa3, 0x43, 0x85, 0x36, 0x6c, 0xc7},
+ }
+
+ if !reflect.DeepEqual(descs, expectedDescs) {
+ t.Errorf("incorrect return, want %#v got %#v", expectedDescs, descs)
+ }
+}
+
+// emptyElfFile returns an elf file header with no program headers or sections.
+func emptyElfFile() string {
+ ident := [elf.EI_NIDENT]byte{}
+ identBuf := bytes.NewBuffer(ident[0:0:elf.EI_NIDENT])
+ binary.Write(identBuf, binary.LittleEndian, []byte("\x7fELF"))
+ binary.Write(identBuf, binary.LittleEndian, elf.ELFCLASS64)
+ binary.Write(identBuf, binary.LittleEndian, elf.ELFDATA2LSB)
+ binary.Write(identBuf, binary.LittleEndian, elf.EV_CURRENT)
+ binary.Write(identBuf, binary.LittleEndian, elf.ELFOSABI_LINUX)
+ binary.Write(identBuf, binary.LittleEndian, make([]byte, 8))
+
+ header := elf.Header64{
+ Ident: ident,
+ Type: uint16(elf.ET_EXEC),
+ Machine: uint16(elf.EM_X86_64),
+ Version: uint32(elf.EV_CURRENT),
+ Entry: 0,
+ Phoff: uint64(binary.Size(elf.Header64{})),
+ Shoff: uint64(binary.Size(elf.Header64{})),
+ Flags: 0,
+ Ehsize: uint16(binary.Size(elf.Header64{})),
+ Phentsize: 0x38,
+ Phnum: 0,
+ Shentsize: 0x40,
+ Shnum: 0,
+ Shstrndx: 0,
+ }
+
+ buf := &bytes.Buffer{}
+ binary.Write(buf, binary.LittleEndian, header)
+ return buf.String()
+}
+
+// shortSectionHeader returns an elf file header with a section header that extends past the end of
+// the file.
+func shortSectionHeaderElfFile() string {
+ ident := [elf.EI_NIDENT]byte{}
+ identBuf := bytes.NewBuffer(ident[0:0:elf.EI_NIDENT])
+ binary.Write(identBuf, binary.LittleEndian, []byte("\x7fELF"))
+ binary.Write(identBuf, binary.LittleEndian, elf.ELFCLASS64)
+ binary.Write(identBuf, binary.LittleEndian, elf.ELFDATA2LSB)
+ binary.Write(identBuf, binary.LittleEndian, elf.EV_CURRENT)
+ binary.Write(identBuf, binary.LittleEndian, elf.ELFOSABI_LINUX)
+ binary.Write(identBuf, binary.LittleEndian, make([]byte, 8))
+
+ header := elf.Header64{
+ Ident: ident,
+ Type: uint16(elf.ET_EXEC),
+ Machine: uint16(elf.EM_X86_64),
+ Version: uint32(elf.EV_CURRENT),
+ Entry: 0,
+ Phoff: uint64(binary.Size(elf.Header64{})),
+ Shoff: uint64(binary.Size(elf.Header64{})),
+ Flags: 0,
+ Ehsize: uint16(binary.Size(elf.Header64{})),
+ Phentsize: 0x38,
+ Phnum: 0,
+ Shentsize: 0x40,
+ Shnum: 1,
+ Shstrndx: 0,
+ }
+
+ buf := &bytes.Buffer{}
+ binary.Write(buf, binary.LittleEndian, header)
+ binary.Write(buf, binary.LittleEndian, []byte{0})
+ return buf.String()
+}
diff --git a/cmd/symbols_map/r8.go b/cmd/symbols_map/r8.go
new file mode 100644
index 0000000..6f73e09
--- /dev/null
+++ b/cmd/symbols_map/r8.go
@@ -0,0 +1,56 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+)
+
+const hashPrefix = "# pg_map_hash: "
+const hashTypePrefix = "SHA-256 "
+const commentPrefix = "#"
+
+// r8Identifier extracts the hash from the comments of a dictionary produced by R8. It returns
+// an empty identifier if no matching comment was found before the first non-comment line.
+func r8Identifier(filename string) (string, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return "", fmt.Errorf("failed to open %s: %w", filename, err)
+ }
+ defer f.Close()
+
+ return extractR8CompilerHash(f)
+}
+
+func extractR8CompilerHash(r io.Reader) (string, error) {
+ s := bufio.NewScanner(r)
+ for s.Scan() {
+ line := s.Text()
+ if strings.HasPrefix(line, hashPrefix) {
+ hash := strings.TrimPrefix(line, hashPrefix)
+ if !strings.HasPrefix(hash, hashTypePrefix) {
+ return "", fmt.Errorf("invalid hash type found in %q", line)
+ }
+ return strings.TrimPrefix(hash, hashTypePrefix), nil
+ } else if !strings.HasPrefix(line, commentPrefix) {
+ break
+ }
+ }
+ return "", nil
+}
diff --git a/cmd/symbols_map/r8_test.go b/cmd/symbols_map/r8_test.go
new file mode 100644
index 0000000..5712da9
--- /dev/null
+++ b/cmd/symbols_map/r8_test.go
@@ -0,0 +1,91 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+func Test_extractR8CompilerHash(t *testing.T) {
+ testCases := []struct {
+ name string
+ data string
+
+ hash string
+ err string
+ }{
+ {
+ name: "simple",
+ data: `# compiler: R8
+# compiler_version: 3.3.18-dev
+# min_api: 10000
+# compiler_hash: bab44c1a04a2201b55fe10394f477994205c34e0
+# common_typos_disable
+# {"id":"com.android.tools.r8.mapping","version":"2.0"}
+# pg_map_id: 7fe8b95
+# pg_map_hash: SHA-256 7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da
+android.car.userlib.UserHelper -> android.car.userlib.UserHelper:
+`,
+ hash: "7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da",
+ },
+ {
+ name: "empty",
+ data: ``,
+ hash: "",
+ },
+ {
+ name: "non comment line",
+ data: `# compiler: R8
+# compiler_version: 3.3.18-dev
+# min_api: 10000
+# compiler_hash: bab44c1a04a2201b55fe10394f477994205c34e0
+# common_typos_disable
+# {"id":"com.android.tools.r8.mapping","version":"2.0"}
+# pg_map_id: 7fe8b95
+android.car.userlib.UserHelper -> android.car.userlib.UserHelper:
+# pg_map_hash: SHA-256 7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da
+`,
+ hash: "",
+ },
+ {
+ name: "invalid hash",
+ data: `# pg_map_hash: foobar 7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da`,
+ err: "invalid hash type",
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ hash, err := extractR8CompilerHash(bytes.NewBufferString(tt.data))
+ if err != nil {
+ if tt.err != "" {
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("incorrect error in extractR8CompilerHash, want %s got %s", tt.err, err)
+ }
+ } else {
+ t.Fatalf("unexpected error in extractR8CompilerHash: %s", err)
+ }
+ } else if tt.err != "" {
+ t.Fatalf("missing error in extractR8CompilerHash, want %s", tt.err)
+ }
+
+ if g, w := hash, tt.hash; g != w {
+ t.Errorf("incorrect hash, want %q got %q", w, g)
+ }
+ })
+ }
+}
diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go
new file mode 100644
index 0000000..938446d
--- /dev/null
+++ b/cmd/symbols_map/symbols_map.go
@@ -0,0 +1,202 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "android/soong/cmd/symbols_map/symbols_map_proto"
+ "android/soong/response"
+
+ "github.com/google/blueprint/pathtools"
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+)
+
+// This tool is used to extract a hash from an elf file or an r8 dictionary and store it as a
+// textproto, or to merge multiple textprotos together.
+
+func main() {
+ var expandedArgs []string
+ for _, arg := range os.Args[1:] {
+ if strings.HasPrefix(arg, "@") {
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ expandedArgs = append(expandedArgs, respArgs...)
+ } else {
+ expandedArgs = append(expandedArgs, arg)
+ }
+ }
+
+ flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+ // Hide the flag package to prevent accidental references to flag instead of flags.
+ flag := struct{}{}
+ _ = flag
+
+ flags.Usage = func() {
+ fmt.Fprintf(flags.Output(), "Usage of %s:\n", os.Args[0])
+ fmt.Fprintf(flags.Output(), " %s -elf|-r8 <input file> [-write_if_changed] <output file>\n", os.Args[0])
+ fmt.Fprintf(flags.Output(), " %s -merge <output file> [-write_if_changed] [-ignore_missing_files] [-strip_prefix <prefix>] [<input file>...]\n", os.Args[0])
+ fmt.Fprintln(flags.Output())
+
+ flags.PrintDefaults()
+ }
+
+ elfFile := flags.String("elf", "", "extract identifier from an elf file")
+ r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
+ merge := flags.String("merge", "", "merge multiple identifier protos")
+
+ writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
+ ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "ignore missing input files in merge mode")
+ stripPrefix := flags.String("strip_prefix", "", "prefix to strip off of the location field in merge mode")
+
+ flags.Parse(expandedArgs)
+
+ if *merge != "" {
+ // If merge mode was requested perform the merge and exit early.
+ err := mergeProtos(*merge, flags.Args(), *stripPrefix, *writeIfChanged, *ignoreMissingFiles)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to merge protos: %s", err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ }
+
+ if *elfFile == "" && *r8File == "" {
+ fmt.Fprintf(os.Stderr, "-elf or -r8 argument is required\n")
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ if *elfFile != "" && *r8File != "" {
+ fmt.Fprintf(os.Stderr, "only one of -elf or -r8 argument is allowed\n")
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ if flags.NArg() != 1 {
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ output := flags.Arg(0)
+
+ var identifier string
+ var location string
+ var typ symbols_map_proto.Mapping_Type
+ var err error
+
+ if *elfFile != "" {
+ typ = symbols_map_proto.Mapping_ELF
+ location = *elfFile
+ identifier, err = elfIdentifier(*elfFile, true)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err)
+ os.Exit(1)
+ }
+ } else if *r8File != "" {
+ typ = symbols_map_proto.Mapping_R8
+ identifier, err = r8Identifier(*r8File)
+ location = *r8File
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error reading r8 identifier: %s\n", err)
+ os.Exit(1)
+ }
+ } else {
+ panic("shouldn't get here")
+ }
+
+ mapping := symbols_map_proto.Mapping{
+ Identifier: proto.String(identifier),
+ Location: proto.String(location),
+ Type: typ.Enum(),
+ }
+
+ err = writeTextProto(output, &mapping, *writeIfChanged)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
+ os.Exit(1)
+ }
+}
+
+// writeTextProto writes a proto to an output file as a textproto, optionally leaving the file
+// unmodified if it was already up to date.
+func writeTextProto(output string, message proto.Message, writeIfChanged bool) error {
+ marshaller := prototext.MarshalOptions{Multiline: true}
+ data, err := marshaller.Marshal(message)
+ if err != nil {
+ return fmt.Errorf("error marshalling textproto: %w", err)
+ }
+
+ if writeIfChanged {
+ err = pathtools.WriteFileIfChanged(output, data, 0666)
+ } else {
+ err = ioutil.WriteFile(output, data, 0666)
+ }
+
+ if err != nil {
+ return fmt.Errorf("error writing to %s: %w\n", output, err)
+ }
+
+ return nil
+}
+
+// mergeProtos merges a list of textproto files containing Mapping messages into a single textproto
+// containing a Mappings message.
+func mergeProtos(output string, inputs []string, stripPrefix string, writeIfChanged bool, ignoreMissingFiles bool) error {
+ mappings := symbols_map_proto.Mappings{}
+ for _, input := range inputs {
+ mapping := symbols_map_proto.Mapping{}
+ data, err := ioutil.ReadFile(input)
+ if err != nil {
+ if ignoreMissingFiles && os.IsNotExist(err) {
+ // Merge mode is used on a list of files in the packaging directory. If multiple
+ // goals are included on the build command line, for example `dist` and `tests`,
+ // then the symbols packaging rule for `dist` can run while a dependency of `tests`
+ // is modifying the symbols packaging directory. That can result in a file that
+ // existed when the file list was generated being deleted as part of updating it,
+ // resulting in sporadic ENOENT errors. Ignore them if -ignore_missing_files
+ // was passed on the command line.
+ continue
+ }
+ return fmt.Errorf("failed to read %s: %w", input, err)
+ }
+ err = prototext.Unmarshal(data, &mapping)
+ if err != nil {
+ return fmt.Errorf("failed to parse textproto %s: %w", input, err)
+ }
+ if stripPrefix != "" && mapping.Location != nil {
+ mapping.Location = proto.String(strings.TrimPrefix(*mapping.Location, stripPrefix))
+ }
+ mappings.Mappings = append(mappings.Mappings, &mapping)
+ }
+
+ return writeTextProto(output, &mappings, writeIfChanged)
+}
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
new file mode 100644
index 0000000..f9c0ce5
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
@@ -0,0 +1,315 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.9.1
+// source: symbols_map.proto
+
+package symbols_map_proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Type is the valid types of a mapping.
+type Mapping_Type int32
+
+const (
+ // ELF denotes a mapping from an elf build ID to an unstripped elf file.
+ Mapping_ELF Mapping_Type = 0
+ // R8 denotes a mapping from an R8 dictionary hash to an R8 dictionary.
+ Mapping_R8 Mapping_Type = 1
+)
+
+// Enum value maps for Mapping_Type.
+var (
+ Mapping_Type_name = map[int32]string{
+ 0: "ELF",
+ 1: "R8",
+ }
+ Mapping_Type_value = map[string]int32{
+ "ELF": 0,
+ "R8": 1,
+ }
+)
+
+func (x Mapping_Type) Enum() *Mapping_Type {
+ p := new(Mapping_Type)
+ *p = x
+ return p
+}
+
+func (x Mapping_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Mapping_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_symbols_map_proto_enumTypes[0].Descriptor()
+}
+
+func (Mapping_Type) Type() protoreflect.EnumType {
+ return &file_symbols_map_proto_enumTypes[0]
+}
+
+func (x Mapping_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Mapping_Type) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = Mapping_Type(num)
+ return nil
+}
+
+// Deprecated: Use Mapping_Type.Descriptor instead.
+func (Mapping_Type) EnumDescriptor() ([]byte, []int) {
+ return file_symbols_map_proto_rawDescGZIP(), []int{0, 0}
+}
+
+type Mapping struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // identifier is a unique identifier of a location, generally the hash of the file. For an
+ // elf file it is the elf build ID, for an R8 dictionary it is the hash from the comments in the
+ // top of the file. It may be empty if no hash could be extracted from the file.
+ Identifier *string `protobuf:"bytes,1,opt,name=identifier" json:"identifier,omitempty"`
+ // location is the path to the file with the given identifier. The location should be valid
+ // both on the local disk and in the distributed symbols.zip or proguard_dict.zip files.
+ Location *string `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
+ // type is the type of the mapping, either ELF or R8.
+ Type *Mapping_Type `protobuf:"varint,3,opt,name=type,enum=symbols_map.Mapping_Type" json:"type,omitempty"`
+}
+
+func (x *Mapping) Reset() {
+ *x = Mapping{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_symbols_map_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Mapping) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Mapping) ProtoMessage() {}
+
+func (x *Mapping) ProtoReflect() protoreflect.Message {
+ mi := &file_symbols_map_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Mapping.ProtoReflect.Descriptor instead.
+func (*Mapping) Descriptor() ([]byte, []int) {
+ return file_symbols_map_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Mapping) GetIdentifier() string {
+ if x != nil && x.Identifier != nil {
+ return *x.Identifier
+ }
+ return ""
+}
+
+func (x *Mapping) GetLocation() string {
+ if x != nil && x.Location != nil {
+ return *x.Location
+ }
+ return ""
+}
+
+func (x *Mapping) GetType() Mapping_Type {
+ if x != nil && x.Type != nil {
+ return *x.Type
+ }
+ return Mapping_ELF
+}
+
+type Mappings struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Mappings []*Mapping `protobuf:"bytes,4,rep,name=mappings" json:"mappings,omitempty"`
+}
+
+func (x *Mappings) Reset() {
+ *x = Mappings{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_symbols_map_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Mappings) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Mappings) ProtoMessage() {}
+
+func (x *Mappings) ProtoReflect() protoreflect.Message {
+ mi := &file_symbols_map_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Mappings.ProtoReflect.Descriptor instead.
+func (*Mappings) Descriptor() ([]byte, []int) {
+ return file_symbols_map_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Mappings) GetMappings() []*Mapping {
+ if x != nil {
+ return x.Mappings
+ }
+ return nil
+}
+
+var File_symbols_map_proto protoreflect.FileDescriptor
+
+var file_symbols_map_proto_rawDesc = []byte{
+ 0x0a, 0x11, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70,
+ 0x22, 0x8d, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
+ 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+ 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
+ 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x79, 0x70,
+ 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x17, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
+ 0x07, 0x0a, 0x03, 0x45, 0x4c, 0x46, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x52, 0x38, 0x10, 0x01,
+ 0x22, 0x3c, 0x0a, 0x08, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x08,
+ 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14,
+ 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70,
+ 0x70, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x31,
+ 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+ 0x63, 0x6d, 0x64, 0x2f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2f,
+ 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f,
+}
+
+var (
+ file_symbols_map_proto_rawDescOnce sync.Once
+ file_symbols_map_proto_rawDescData = file_symbols_map_proto_rawDesc
+)
+
+func file_symbols_map_proto_rawDescGZIP() []byte {
+ file_symbols_map_proto_rawDescOnce.Do(func() {
+ file_symbols_map_proto_rawDescData = protoimpl.X.CompressGZIP(file_symbols_map_proto_rawDescData)
+ })
+ return file_symbols_map_proto_rawDescData
+}
+
+var file_symbols_map_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_symbols_map_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_symbols_map_proto_goTypes = []interface{}{
+ (Mapping_Type)(0), // 0: symbols_map.Mapping.Type
+ (*Mapping)(nil), // 1: symbols_map.Mapping
+ (*Mappings)(nil), // 2: symbols_map.Mappings
+}
+var file_symbols_map_proto_depIdxs = []int32{
+ 0, // 0: symbols_map.Mapping.type:type_name -> symbols_map.Mapping.Type
+ 1, // 1: symbols_map.Mappings.mappings:type_name -> symbols_map.Mapping
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_symbols_map_proto_init() }
+func file_symbols_map_proto_init() {
+ if File_symbols_map_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_symbols_map_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Mapping); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_symbols_map_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Mappings); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_symbols_map_proto_rawDesc,
+ NumEnums: 1,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_symbols_map_proto_goTypes,
+ DependencyIndexes: file_symbols_map_proto_depIdxs,
+ EnumInfos: file_symbols_map_proto_enumTypes,
+ MessageInfos: file_symbols_map_proto_msgTypes,
+ }.Build()
+ File_symbols_map_proto = out.File
+ file_symbols_map_proto_rawDesc = nil
+ file_symbols_map_proto_goTypes = nil
+ file_symbols_map_proto_depIdxs = nil
+}
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.proto b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
new file mode 100644
index 0000000..693fe3e
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
@@ -0,0 +1,44 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+package symbols_map;
+option go_package = "android/soong/cmd/symbols_map/symbols_map_proto";
+
+message Mapping {
+ // identifier is a unique identifier of a location, generally the hash of the file. For an
+ // elf file it is the elf build ID, for an R8 dictionary it is the hash from the comments in the
+ // top of the file. It may be empty if no hash could be extracted from the file.
+ optional string identifier = 1;
+
+ // location is the path to the file with the given identifier. The location should be valid
+ // both on the local disk and in the distributed symbols.zip or proguard_dict.zip files.
+ optional string location = 2;
+
+ // Type is the valid types of a mapping.
+ enum Type {
+ // ELF denotes a mapping from an elf build ID to an unstripped elf file.
+ ELF = 0;
+ // R8 denotes a mapping from an R8 dictionary hash to an R8 dictionary.
+ R8 = 1;
+ }
+
+ // type is the type of the mapping, either ELF or R8.
+ optional Type type = 3;
+}
+
+message Mappings {
+ repeated Mapping mappings = 4;
+}
\ No newline at end of file
diff --git a/cmd/symbols_map/symbols_map_test.go b/cmd/symbols_map/symbols_map_test.go
new file mode 100644
index 0000000..754b7ef
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_test.go
@@ -0,0 +1,217 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "android/soong/cmd/symbols_map/symbols_map_proto"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+)
+
+func Test_mergeProtos(t *testing.T) {
+ type testFile struct {
+ filename string
+ contents *symbols_map_proto.Mapping
+ missing bool
+ }
+
+ tests := []struct {
+ name string
+ inputs []testFile
+ stripPrefix string
+ writeIfChanged bool
+ ignoreMissingFiles bool
+
+ error string
+ output *symbols_map_proto.Mappings
+ }{
+ {
+ name: "empty",
+ output: &symbols_map_proto.Mappings{},
+ },
+ {
+ name: "merge",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("bar"),
+ Location: proto.String("symbols/bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ output: &symbols_map_proto.Mappings{
+ Mappings: []*symbols_map_proto.Mapping{
+ {
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ {
+ Identifier: proto.String("bar"),
+ Location: proto.String("symbols/bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ },
+ {
+ name: "strip prefix",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("bar"),
+ Location: proto.String("symbols/bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ stripPrefix: "symbols/",
+ output: &symbols_map_proto.Mappings{
+ Mappings: []*symbols_map_proto.Mapping{
+ {
+ Identifier: proto.String("foo"),
+ Location: proto.String("foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ {
+ Identifier: proto.String("bar"),
+ Location: proto.String("bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ },
+ {
+ name: "missing",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ missing: true,
+ },
+ },
+ error: "no such file or directory",
+ },
+ {
+ name: "ignore missing",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ missing: true,
+ },
+ },
+ ignoreMissingFiles: true,
+ output: &symbols_map_proto.Mappings{
+ Mappings: []*symbols_map_proto.Mapping{
+ {
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dir, err := os.MkdirTemp("", "test_mergeProtos")
+ if err != nil {
+ t.Fatalf("failed to create temporary directory: %s", err)
+ }
+ defer os.RemoveAll(dir)
+
+ var inputs []string
+ for _, in := range tt.inputs {
+ path := filepath.Join(dir, in.filename)
+ inputs = append(inputs, path)
+ if !in.missing {
+ err := writeTextProto(path, in.contents, false)
+ if err != nil {
+ t.Fatalf("failed to create input file %s: %s", path, err)
+ }
+ }
+ }
+ output := filepath.Join(dir, "out")
+
+ err = mergeProtos(output, inputs, tt.stripPrefix, tt.writeIfChanged, tt.ignoreMissingFiles)
+ if err != nil {
+ if tt.error != "" {
+ if !strings.Contains(err.Error(), tt.error) {
+ t.Fatalf("expected error %q, got %s", tt.error, err.Error())
+ }
+ } else {
+ t.Fatalf("unexpected error %q", err)
+ }
+ } else if tt.error != "" {
+ t.Fatalf("missing error %q", tt.error)
+ } else {
+ data, err := ioutil.ReadFile(output)
+ if err != nil {
+ t.Fatalf("failed to read output file %s: %s", output, err)
+ }
+ var got symbols_map_proto.Mappings
+ err = prototext.Unmarshal(data, &got)
+ if err != nil {
+ t.Fatalf("failed to unmarshal textproto %s: %s", output, err)
+ }
+
+ if !proto.Equal(tt.output, &got) {
+ t.Fatalf("expected output %q, got %q", tt.output.String(), got.String())
+ }
+ }
+ })
+ }
+}
diff --git a/compliance/copy_license_metadata/Android.bp b/compliance/copy_license_metadata/Android.bp
new file mode 100644
index 0000000..83019eb
--- /dev/null
+++ b/compliance/copy_license_metadata/Android.bp
@@ -0,0 +1,30 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+ name: "copy_license_metadata",
+ srcs: [
+ "copy_license_metadata.go",
+ ],
+ deps: [
+ "license_metadata_proto",
+ "golang-protobuf-proto",
+ "golang-protobuf-encoding-prototext",
+ "soong-response",
+ ],
+}
diff --git a/compliance/copy_license_metadata/copy_license_metadata.go b/compliance/copy_license_metadata/copy_license_metadata.go
new file mode 100644
index 0000000..36b9489
--- /dev/null
+++ b/compliance/copy_license_metadata/copy_license_metadata.go
@@ -0,0 +1,144 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+
+ "android/soong/compliance/license_metadata_proto"
+ "android/soong/response"
+)
+
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
+ var f multiString
+ flags.Var(&f, name, usage)
+ return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ var expandedArgs []string
+ for _, arg := range os.Args[1:] {
+ if strings.HasPrefix(arg, "@") {
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ expandedArgs = append(expandedArgs, respArgs...)
+ } else {
+ expandedArgs = append(expandedArgs, arg)
+ }
+ }
+
+ flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+ installed := flags.String("i", "", "installed target")
+ sources := newMultiString(flags, "s", "source (input) file")
+ dep := flags.String("d", "", "license metadata file dependency")
+ outFile := flags.String("o", "", "output file")
+
+ flags.Parse(expandedArgs)
+
+ if len(*dep) == 0 || len(*installed) == 0 || len(*sources) == 0 {
+ flags.Usage()
+ if len(*dep) == 0 {
+ fmt.Fprintf(os.Stderr, "source license metadata (-d flag) required\n")
+ }
+ if len(*sources) == 0 {
+ fmt.Fprintf(os.Stderr, "source copy (-s flag required\n")
+ }
+ if len(*installed) == 0 {
+ fmt.Fprintf(os.Stderr, "installed copy (-i flag) required\n")
+ }
+ os.Exit(1)
+ }
+
+ src_metadata := license_metadata_proto.LicenseMetadata{}
+ err := readMetadata(*dep, &src_metadata)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+ os.Exit(2)
+ }
+
+ metadata := src_metadata
+ metadata.Built = nil
+ metadata.InstallMap = nil
+ metadata.Installed = []string{*installed}
+ metadata.Sources = *sources
+ metadata.Deps = []*license_metadata_proto.AnnotatedDependency{&license_metadata_proto.AnnotatedDependency{
+ File: proto.String(*dep),
+ Annotations: []string{"static"},
+ }}
+
+ err = writeMetadata(*outFile, &metadata)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+ os.Exit(2)
+ }
+}
+
+func readMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
+ if file == "" {
+ return fmt.Errorf("source metadata file (-d) required")
+ }
+ buf, err := ioutil.ReadFile(file)
+ if err != nil {
+ return fmt.Errorf("error reading textproto %q: %w", file, err)
+ }
+
+ err = prototext.Unmarshal(buf, metadata)
+ if err != nil {
+ return fmt.Errorf("error unmarshalling textproto: %w", err)
+ }
+
+ return nil
+}
+
+func writeMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
+ buf, err := prototext.MarshalOptions{Multiline: true}.Marshal(metadata)
+ if err != nil {
+ return fmt.Errorf("error marshalling textproto: %w", err)
+ }
+
+ if file != "" {
+ err = ioutil.WriteFile(file, buf, 0666)
+ if err != nil {
+ return fmt.Errorf("error writing textproto %q: %w", file, err)
+ }
+ } else {
+ _, _ = os.Stdout.Write(buf)
+ }
+
+ return nil
+}
diff --git a/dexpreopt/DEXPREOPT_IMPLEMENTATION.md b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
new file mode 100644
index 0000000..c3a1730
--- /dev/null
+++ b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
@@ -0,0 +1,258 @@
+## Dexpreopt implementation
+
+### Introduction
+
+All dexpreopted Java code falls into three categories:
+
+- bootclasspath
+- system server
+- apps and libraries
+
+Dexpreopt implementation for bootclasspath libraries (boot images) is located in
+[soong/java] (see e.g. [soong/java/dexpreopt_bootjars.go]), and install rules
+are in [make/core/dex_preopt.mk].
+
+Dexpreopt implementation for system server, libraries and apps is located in
+[soong/dexpreopt]. For the rest of this section we focus primarily on it (and
+not boot images).
+
+Dexpeopt implementation is split across the Soong part and the Make part. The
+core logic is in Soong, and Make only generates configs and scripts to pass
+information to Soong.
+
+### Global and module dexpreopt.config
+
+The build system generates a global JSON dexpreopt config that is populated from
+product variables. This is static configuration that is passed to both Soong and
+Make. The `$OUT/soong/dexpreopt.config` file is generated in
+[make/core/dex_preopt_config.mk]. Soong reads it in [soong/dexpreopt/config.go]
+and makes a device-specific copy (this is needed to ensure incremental build
+correctness). The global config contains lists of bootclasspath jars, system
+server jars, dex2oat options, global switches that enable and disable parts of
+dexpreopt and so on.
+
+The build system also generates a module config for each dexpreopted package. It
+contains package-specific configuration that is derived from the global
+configuration and Android.bp or Android.mk module for the package.
+
+Module configs for Make packages are generated in
+[make/core/dex_preopt_odex_install.mk]; they are materialized as per-package
+JSON dexpreopt.config files.
+
+Module configs in Soong are not materialized as dexpreopt.config files and exist
+as Go structures in memory, unless it is necessary to materialize them as a file
+for dependent Make packages or for post-dexpreopting. Module configs are defined
+in [soong/dexpreopt/config.go].
+
+### Dexpreopt in Soong
+
+The Soong implementation of dexpreopt consists roughly of the following steps:
+
+- Read global dexpreopt config passed from Make ([soong/dexpreopt/config.go]).
+
+- Construct a static boot image config ([soong/java/dexpreopt_config.go]).
+
+- During dependency mutator pass, for each suitable module:
+ - add uses-library dependencies (e.g. for apps: [soong/java/app.go:deps])
+
+- During rule generation pass, for each suitable module:
+ - compute transitive uses-library dependency closure
+ ([soong/java/java.go:addCLCFromDep])
+
+ - construct CLC from the dependency closure
+ ([soong/dexpreopt/class_loader_context.go])
+
+ - construct module config with CLC, boot image locations, etc.
+ ([soong/java/dexpreopt.go])
+
+ - generate build rules to verify build-time CLC against the manifest (e.g.
+ for apps: [soong/java/app.go:verifyUsesLibraries])
+
+ - generate dexpreopt build rule ([soong/dexpreopt/dexpreopt.go])
+
+- At the end of rule generation pass:
+ - generate build rules for boot images ([soong/java/dexpreopt_bootjars.go],
+ [soong/java/bootclasspath_fragment.go] and
+ [soong/java/platform_bootclasspath.go])
+
+### Dexpreopt in Make - dexpreopt_gen
+
+In order to reuse the same dexpreopt implementation for both Soong and Make
+packages, part of Soong is compiled into a standalone binary dexpreopt_gen. It
+runs during the Ninja stage of the build and generates shell scripts with
+dexpreopt build rules for Make packages, and then executes them.
+
+This setup causes many inconveniences. To name a few:
+
+- Errors in the build rules are only revealed at the late stage of the build.
+
+- These rules are not tested by the presubmit builds that run `m nothing` on
+ many build targets/products.
+
+- It is impossible to find dexpreopt build rules in the generated Ninja files.
+
+However all these issues are a lesser evil compared to having a duplicate
+dexpreopt implementation in Make. Also note that it would be problematic to
+reimplement the logic in Make anyway, because Android.mk modules are not
+processed in the order of uses-library dependencies and propagating dependency
+information from one module to another would require a similar workaround with
+a script.
+
+Dexpreopt for Make packages involves a few steps:
+
+- At Soong phase (during `m nothing`), see dexpreopt_gen:
+ - generate build rules for dexpreopt_gen binary
+
+- At Make/Kati phase (during `m nothing`), see
+ [make/core/dex_preopt_odex_install.mk]:
+ - generate build rules for module dexpreopt.config
+
+ - generate build rules for merging dependency dexpreopt.config files (see
+ [make/core/dex_preopt_config_merger.py])
+
+ - generate build rules for dexpreopt_gen invocation
+
+ - generate build rules for executing dexpreopt.sh scripts
+
+- At Ninja phase (during `m`):
+ - generate dexpreopt.config files
+
+ - execute dexpreopt_gen rules (generate dexpreopt.sh scripts)
+
+ - execute dexpreopt.sh scripts (this runs the actual dexpreopt)
+
+The Make/Kati phase adds all the necessary dependencies that trigger
+dexpreopt_gen and dexpreopt.sh rules. The real dexpreopt command (dex2oat
+invocation that will be executed to AOT-compile a package) is in the
+dexpreopt.sh script, which is generated close to the end of the build.
+
+### Indirect build rules
+
+The process described above for Make packages involves "indirect build rules",
+i.e. build rules that are generated not at the time when the build system is
+created (which is a small step at the very beginning of the build triggered with
+`m nothing`), but at the time when the actual build is done (`m` phase).
+
+Some build systems, such as Make, allow modifications of the build graph during
+the build. Other build systems, such as Soong, have a clear separation into the
+first "generation phase" (this is when build rules are created) and the second
+"build phase" (this is when the build rules are executed), and they do not allow
+modifications of the dependency graph during the second phase. The Soong
+approach is better from performance standpoint, because with the Make approach
+there are no guarantees regarding the time of the build --- recursive build
+graph modfications continue until fixpoint. However the Soong approach is also
+more restictive, as it can only generate build rules from the information that
+is passed to the build system via global configuration, Android.bp files or
+encoded in the Go code. Any other information (such as the contents of the Java
+manifest files) are not accessible and cannot be used to generate build rules.
+
+Hence the need for the "indirect build rules": during the generation phase only
+stubs of the build rules are generated, and the real rules are generated by the
+stub rules during the build phase (and executed immediately). Note that the
+build system still has to add all the necessary dependencies during the
+generation phase, because it will not be possible to change build order during
+the build phase.
+
+Indirect buils rules are used in a couple of places in dexpreopt:
+
+- [soong/scripts/manifest_check.py]: first to extract targetSdkVersion from the
+ manifest, and later to extract `<uses-library/>` tags from the manifest and
+ compare them to the uses-library list known to the build system
+
+- [soong/scripts/construct_context.py]: to trim compatibility libraries in CLC
+
+- [make/core/dex_preopt_config_merger.py]: to merge information from
+ dexpreopt.config files for uses-library dependencies into the dependent's
+ dexpreopt.config file (mostly the CLC)
+
+- autogenerated dexpreopt.sh scripts: to call dexpreopt_gen
+
+### Consistency check - manifest_check.py
+
+Because the information from the manifests has to be duplicated in the
+Android.bp/Android.mk files, there is a danger that it may get out of sync. To
+guard against that, the build system generates a rule that verifies
+uses-libraries: checks the metadata in the build files against the contents of a
+manifest. The manifest can be available as a source file, or as part of a
+prebuilt APK.
+
+The check is implemented in [soong/scripts/manifest_check.py].
+
+It is possible to turn off the check globally for a product by setting
+`PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true` in a product makefile, or for a
+particular build by setting `RELAX_USES_LIBRARY_CHECK=true`.
+
+### Compatibility libraries - construct_context.py
+
+Compatibility libraries are libraries that didn’t exist prior to a certain SDK
+version (say, `N`), but classes in them were in the bootclasspath jars, etc.,
+and in version `N` they have been separated into a standalone uses-library.
+Compatibility libraries should only be in the CLC of an app if its
+`targetSdkVersion` in the manifest is less than `N`.
+
+Currently compatibility libraries only affect apps (but not other libraries).
+
+The build system cannot see `targetSdkVersion` of an app at the time it
+generates dexpreopt build rules, so it doesn't know whether to add compatibility
+libaries to CLC or not. As a workaround, the build system includes all
+compatibility libraries regardless of the app version, and appends some extra
+logic to the dexpreopt rule that will extract `targetSdkVersion` from the
+manifest and filter CLC based on that version during Ninja stage of the build,
+immediately before executing the dexpreopt command (see the
+soong/scripts/construct_context.py script).
+
+As of the time of writing (January 2022), there are the following compatibility
+libraries:
+
+- org.apache.http.legacy (SDK 28)
+- android.hidl.base-V1.0-java (SDK 29)
+- android.hidl.manager-V1.0-java (SDK 29)
+- android.test.base (SDK 30)
+- android.test.mock (SDK 30)
+
+### Manifest fixer
+
+Sometimes uses-library tags are missing from the source manifest of a
+library/app. This may happen for example if one of the transitive dependencies
+of the library/app starts using another uses-library, and the library/app's
+manifest isn't updated to include it.
+
+Soong can compute some of the missing uses-library tags for a given library/app
+automatically as SDK libraries in the transitive dependency closure of the
+library/app. The closure is needed because a library/app may depend on a static
+library that may in turn depend on an SDK library (possibly transitively via
+another library).
+
+Not all uses-library tags can be computed in this way, because some of the
+uses-library dependencies are not SDK libraries, or they are not reachable via
+transitive dependency closure. But when possible, allowing Soong to calculate
+the manifest entries is less prone to errors and simplifies maintenance. For
+example, consider a situation when many apps use some static library that adds a
+new uses-library dependency -- all the apps will have to be updated. That is
+difficult to maintain.
+
+There is also a manifest merger, because sometimes the final manifest of an app
+is merged from a few dependency manifests, so the final manifest installed on
+devices contains a superset of uses-library tags of the source manifest of the
+app.
+
+
+[make/core/dex_preopt.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt.mk
+[make/core/dex_preopt_config.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config.mk
+[make/core/dex_preopt_config_merger.py]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config_merger.py
+[make/core/dex_preopt_odex_install.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_odex_install.mk
+[soong/dexpreopt]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt
+[soong/dexpreopt/class_loader_context.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/class_loader_context.go
+[soong/dexpreopt/config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/config.go
+[soong/dexpreopt/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/dexpreopt.go
+[soong/java]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java
+[soong/java/app.go:deps]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20deps%22
+[soong/java/app.go:verifyUsesLibraries]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20verifyUsesLibraries%22
+[soong/java/bootclasspath_fragment.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/bootclasspath_fragment.go
+[soong/java/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt.go
+[soong/java/dexpreopt_bootjars.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_bootjars.go
+[soong/java/dexpreopt_config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_config.go
+[soong/java/java.go:addCLCFromDep]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/java.go?q=%22func%20addCLCfromDep%22
+[soong/java/platform_bootclasspath.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/platform_bootclasspath.go
+[soong/scripts/construct_context.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/construct_context.py
+[soong/scripts/manifest_check.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/manifest_check.py
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 658e8e2..7bc9ab2 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -196,10 +196,6 @@
// If the library is optional or required.
Optional bool
- // If the library is implicitly infered by Soong (as opposed to explicitly added via `uses_libs`
- // or `optional_uses_libs`.
- Implicit bool
-
// On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
Host android.Path
@@ -210,6 +206,34 @@
Subcontexts []*ClassLoaderContext
}
+// excludeLibs excludes the libraries from this ClassLoaderContext.
+//
+// This treats the supplied context as being immutable (as it may come from a dependency). So, it
+// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within this context then this will return a deep copy of this without those libraries.
+//
+// If this ClassLoaderContext matches one of the libraries to exclude then this returns (nil, true)
+// to indicate that this context should be excluded from the containing list.
+//
+// If any of this ClassLoaderContext's Subcontexts reference the excluded libraries then this
+// returns a pointer to a copy of this without the excluded libraries and true to indicate that this
+// was copied.
+//
+// Otherwise, this returns a pointer to this and false to indicate that this was not copied.
+func (c *ClassLoaderContext) excludeLibs(excludedLibs []string) (*ClassLoaderContext, bool) {
+ if android.InList(c.Name, excludedLibs) {
+ return nil, true
+ }
+
+ if excludedList, modified := excludeLibsFromCLCList(c.Subcontexts, excludedLibs); modified {
+ clcCopy := *c
+ clcCopy.Subcontexts = excludedList
+ return &clcCopy, true
+ }
+
+ return c, false
+}
+
// ClassLoaderContextMap is a map from SDK version to CLC. There is a special entry with key
// AnySdkVersion that stores unconditional CLC that is added regardless of the target SDK version.
//
@@ -262,9 +286,8 @@
const AnySdkVersion int = android.FutureApiLevelInt
// Add class loader context for the given library to the map entry for the given SDK version.
-func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int,
- lib string, optional, implicit bool, hostPath, installPath android.Path,
- nestedClcMap ClassLoaderContextMap) error {
+func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
+ optional bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
// For prebuilts, library should have the same name as the source module.
lib = android.RemoveOptionalPrebuiltPrefix(lib)
@@ -313,7 +336,6 @@
clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
Name: lib,
Optional: optional,
- Implicit: implicit,
Host: hostPath,
Device: devicePath,
Subcontexts: subcontexts,
@@ -326,10 +348,9 @@
// about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
// are validated later before CLC is used (in validateClassLoaderContext).
func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
- lib string, optional, implicit bool, hostPath, installPath android.Path,
- nestedClcMap ClassLoaderContextMap) {
+ lib string, optional bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
- err := clcMap.addContext(ctx, sdkVer, lib, optional, implicit, hostPath, installPath, nestedClcMap)
+ err := clcMap.addContext(ctx, sdkVer, lib, optional, hostPath, installPath, nestedClcMap)
if err != nil {
ctx.ModuleErrorf(err.Error())
}
@@ -373,15 +394,13 @@
// included). This is the list of libraries that should be in the <uses-library> tags in the
// manifest. Some of them may be present in the source manifest, others are added by manifest_fixer.
// Required and optional libraries are in separate lists.
-func (clcMap ClassLoaderContextMap) usesLibs(implicit bool) (required []string, optional []string) {
+func (clcMap ClassLoaderContextMap) UsesLibs() (required []string, optional []string) {
if clcMap != nil {
clcs := clcMap[AnySdkVersion]
required = make([]string, 0, len(clcs))
optional = make([]string, 0, len(clcs))
for _, clc := range clcs {
- if implicit && !clc.Implicit {
- // Skip, this is an explicit library and we need only the implicit ones.
- } else if clc.Optional {
+ if clc.Optional {
optional = append(optional, clc.Name)
} else {
required = append(required, clc.Name)
@@ -391,14 +410,6 @@
return required, optional
}
-func (clcMap ClassLoaderContextMap) UsesLibs() ([]string, []string) {
- return clcMap.usesLibs(false)
-}
-
-func (clcMap ClassLoaderContextMap) ImplicitUsesLibs() ([]string, []string) {
- return clcMap.usesLibs(true)
-}
-
func (clcMap ClassLoaderContextMap) Dump() string {
jsonCLC := toJsonClassLoaderContext(clcMap)
bytes, err := json.MarshalIndent(jsonCLC, "", " ")
@@ -408,6 +419,67 @@
return string(bytes)
}
+// excludeLibsFromCLCList excludes the libraries from the ClassLoaderContext in this list.
+//
+// This treats the supplied list as being immutable (as it may come from a dependency). So, it
+// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within the contexts in the list then this will return a deep copy of the list without those
+// libraries.
+//
+// If any of the ClassLoaderContext in the list reference the excluded libraries then this returns a
+// copy of this list without the excluded libraries and true to indicate that this was copied.
+//
+// Otherwise, this returns the list and false to indicate that this was not copied.
+func excludeLibsFromCLCList(clcList []*ClassLoaderContext, excludedLibs []string) ([]*ClassLoaderContext, bool) {
+ modifiedList := false
+ copiedList := make([]*ClassLoaderContext, 0, len(clcList))
+ for _, clc := range clcList {
+ resultClc, modifiedClc := clc.excludeLibs(excludedLibs)
+ if resultClc != nil {
+ copiedList = append(copiedList, resultClc)
+ }
+ modifiedList = modifiedList || modifiedClc
+ }
+
+ if modifiedList {
+ return copiedList, true
+ } else {
+ return clcList, false
+ }
+}
+
+// ExcludeLibs excludes the libraries from the ClassLoaderContextMap.
+//
+// If the list o libraries is empty then this returns the ClassLoaderContextMap.
+//
+// This treats the ClassLoaderContextMap as being immutable (as it may come from a dependency). So,
+// it implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within the contexts in the map then this will return a deep copy of the map without those
+// libraries.
+//
+// Otherwise, this returns the map unchanged.
+func (clcMap ClassLoaderContextMap) ExcludeLibs(excludedLibs []string) ClassLoaderContextMap {
+ if len(excludedLibs) == 0 {
+ return clcMap
+ }
+
+ excludedClcMap := make(ClassLoaderContextMap)
+ modifiedMap := false
+ for sdkVersion, clcList := range clcMap {
+ excludedList, modifiedList := excludeLibsFromCLCList(clcList, excludedLibs)
+ if len(excludedList) != 0 {
+ excludedClcMap[sdkVersion] = excludedList
+ }
+ modifiedMap = modifiedMap || modifiedList
+ }
+
+ if modifiedMap {
+ return excludedClcMap
+ } else {
+ return clcMap
+ }
+}
+
// Now that the full unconditional context is known, reconstruct conditional context.
// Apply filters for individual libraries, mirroring what the PackageManager does when it
// constructs class loader context on device.
@@ -542,7 +614,6 @@
type jsonClassLoaderContext struct {
Name string
Optional bool
- Implicit bool
Host string
Device string
Subcontexts []*jsonClassLoaderContext
@@ -575,7 +646,6 @@
clcs = append(clcs, &ClassLoaderContext{
Name: clc.Name,
Optional: clc.Optional,
- Implicit: clc.Implicit,
Host: constructPath(ctx, clc.Host),
Device: clc.Device,
Subcontexts: fromJsonClassLoaderContextRec(ctx, clc.Subcontexts),
@@ -589,6 +659,9 @@
jClcMap := make(jsonClassLoaderContextMap)
for sdkVer, clcs := range clcMap {
sdkVerStr := fmt.Sprintf("%d", sdkVer)
+ if sdkVer == AnySdkVersion {
+ sdkVerStr = "any"
+ }
jClcMap[sdkVerStr] = toJsonClassLoaderContextRec(clcs)
}
return jClcMap
@@ -608,7 +681,6 @@
jClcs[i] = &jsonClassLoaderContext{
Name: clc.Name,
Optional: clc.Optional,
- Implicit: clc.Implicit,
Host: host,
Device: clc.Device,
Subcontexts: toJsonClassLoaderContextRec(clc.Subcontexts),
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 4a3d390..8b3c013 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -50,34 +50,33 @@
ctx := testContext()
optional := false
- implicit := true
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
- m.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, AnySdkVersion, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
// Add some libraries with nested subcontexts.
m1 := make(ClassLoaderContextMap)
- m1.AddContext(ctx, AnySdkVersion, "a1", optional, implicit, buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
- m1.AddContext(ctx, AnySdkVersion, "b1", optional, implicit, buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
+ m1.AddContext(ctx, AnySdkVersion, "a1", optional, buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+ m1.AddContext(ctx, AnySdkVersion, "b1", optional, buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
m2 := make(ClassLoaderContextMap)
- m2.AddContext(ctx, AnySdkVersion, "a2", optional, implicit, buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
- m2.AddContext(ctx, AnySdkVersion, "b2", optional, implicit, buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
- m2.AddContext(ctx, AnySdkVersion, "c2", optional, implicit, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+ m2.AddContext(ctx, AnySdkVersion, "a2", optional, buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "b2", optional, buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "c2", optional, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
m3 := make(ClassLoaderContextMap)
- m3.AddContext(ctx, AnySdkVersion, "a3", optional, implicit, buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
- m3.AddContext(ctx, AnySdkVersion, "b3", optional, implicit, buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
+ m3.AddContext(ctx, AnySdkVersion, "a3", optional, buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+ m3.AddContext(ctx, AnySdkVersion, "b3", optional, buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
- m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+ m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
// When the same library is both in conditional and unconditional context, it should be removed
// from conditional context.
- m.AddContext(ctx, 42, "f", optional, implicit, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
- m.AddContext(ctx, AnySdkVersion, "f", optional, implicit, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, 42, "f", optional, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, AnySdkVersion, "f", optional, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
// Merge map with implicit root library that is among toplevel contexts => does nothing.
m.AddContextMap(m1, "c")
@@ -86,12 +85,12 @@
m.AddContextMap(m3, "m_g")
// Compatibility libraries with unknown install paths get default paths.
- m.AddContext(ctx, 29, AndroidHidlManager, optional, implicit, buildPath(ctx, AndroidHidlManager), nil, nil)
- m.AddContext(ctx, 29, AndroidHidlBase, optional, implicit, buildPath(ctx, AndroidHidlBase), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlManager, optional, buildPath(ctx, AndroidHidlManager), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlBase, optional, buildPath(ctx, AndroidHidlBase), nil, nil)
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well.
- m.AddContext(ctx, 30, AndroidTestMock, optional, implicit, buildPath(ctx, AndroidTestMock), nil, nil)
+ m.AddContext(ctx, 30, AndroidTestMock, optional, buildPath(ctx, AndroidTestMock), nil, nil)
valid, validationError := validateClassLoaderContext(m)
@@ -165,12 +164,11 @@
func TestCLCJson(t *testing.T) {
ctx := testContext()
optional := false
- implicit := true
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContext(ctx, 29, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
- m.AddContext(ctx, 30, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
- m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
jsonCLC := toJsonClassLoaderContext(m)
restored := fromJsonClassLoaderContext(ctx, jsonCLC)
android.AssertIntEquals(t, "The size of the maps should be the same.", len(m), len(restored))
@@ -191,13 +189,12 @@
func testCLCUnknownPath(t *testing.T, whichPath string) {
ctx := testContext()
optional := false
- implicit := true
m := make(ClassLoaderContextMap)
if whichPath == "build" {
- m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, nil, nil, nil)
+ m.AddContext(ctx, AnySdkVersion, "a", optional, nil, nil, nil)
} else {
- m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, buildPath(ctx, "a"), nil, nil)
+ m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), nil, nil)
}
// The library should be added to <uses-library> tags by the manifest_fixer.
@@ -232,11 +229,10 @@
func TestCLCNestedConditional(t *testing.T) {
ctx := testContext()
optional := false
- implicit := true
m1 := make(ClassLoaderContextMap)
- m1.AddContext(ctx, 42, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m1.AddContext(ctx, 42, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), m1)
+ err := m.addContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), m1)
checkError(t, err, "nested class loader context shouldn't have conditional part")
}
@@ -245,12 +241,11 @@
func TestCLCSdkVersionOrder(t *testing.T) {
ctx := testContext()
optional := false
- implicit := true
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContext(ctx, 29, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
- m.AddContext(ctx, 30, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
- m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
valid, validationError := validateClassLoaderContext(m)
@@ -284,6 +279,135 @@
})
}
+func TestCLCMExcludeLibs(t *testing.T) {
+ ctx := testContext()
+ const optional = false
+
+ excludeLibs := func(t *testing.T, m ClassLoaderContextMap, excluded_libs ...string) ClassLoaderContextMap {
+ // Dump the CLCM before creating a new copy that excludes a specific set of libraries.
+ before := m.Dump()
+
+ // Create a new CLCM that excludes some libraries.
+ c := m.ExcludeLibs(excluded_libs)
+
+ // Make sure that the original CLCM was not changed.
+ after := m.Dump()
+ android.AssertStringEquals(t, "input CLCM modified", before, after)
+
+ return c
+ }
+
+ t.Run("exclude nothing", func(t *testing.T) {
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+
+ a := excludeLibs(t, m)
+
+ android.AssertStringEquals(t, "output CLCM ", `{
+ "28": [
+ {
+ "Name": "a",
+ "Optional": false,
+ "Host": "out/soong/a.jar",
+ "Device": "/system/a.jar",
+ "Subcontexts": []
+ }
+ ]
+}`, a.Dump())
+ })
+
+ t.Run("one item from list", func(t *testing.T) {
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 28, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+
+ a := excludeLibs(t, m, "a")
+
+ expected := `{
+ "28": [
+ {
+ "Name": "b",
+ "Optional": false,
+ "Host": "out/soong/b.jar",
+ "Device": "/system/b.jar",
+ "Subcontexts": []
+ }
+ ]
+}`
+ android.AssertStringEquals(t, "output CLCM ", expected, a.Dump())
+ })
+
+ t.Run("all items from a list", func(t *testing.T) {
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 28, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+
+ a := excludeLibs(t, m, "a", "b")
+
+ android.AssertStringEquals(t, "output CLCM ", `{}`, a.Dump())
+ })
+
+ t.Run("items from a subcontext", func(t *testing.T) {
+ s := make(ClassLoaderContextMap)
+ s.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ s.AddContext(ctx, AnySdkVersion, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), s)
+
+ a := excludeLibs(t, m, "b")
+
+ android.AssertStringEquals(t, "output CLCM ", `{
+ "28": [
+ {
+ "Name": "a",
+ "Optional": false,
+ "Host": "out/soong/a.jar",
+ "Device": "/system/a.jar",
+ "Subcontexts": [
+ {
+ "Name": "c",
+ "Optional": false,
+ "Host": "out/soong/c.jar",
+ "Device": "/system/c.jar",
+ "Subcontexts": []
+ }
+ ]
+ }
+ ]
+}`, a.Dump())
+ })
+}
+
+// Test that CLC is correctly serialized to JSON.
+func TestCLCtoJSON(t *testing.T) {
+ ctx := testContext()
+ optional := false
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ android.AssertStringEquals(t, "output CLCM ", `{
+ "28": [
+ {
+ "Name": "a",
+ "Optional": false,
+ "Host": "out/soong/a.jar",
+ "Device": "/system/a.jar",
+ "Subcontexts": []
+ }
+ ],
+ "any": [
+ {
+ "Name": "b",
+ "Optional": false,
+ "Host": "out/soong/b.jar",
+ "Device": "/system/b.jar",
+ "Subcontexts": []
+ }
+ ]
+}`, m.Dump())
+}
+
func checkError(t *testing.T, have error, want string) {
if have == nil {
t.Errorf("\nwant error: '%s'\nhave: none", want)
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 153b025..64cd46a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -36,8 +36,6 @@
PreoptWithUpdatableBcp bool // If updatable boot jars are included in dexpreopt or not.
- UseArtImage bool // use the art image (use other boot class path dex files without image)
-
HasSystemOther bool // store odex files that match PatternsOnSystemOther on the system_other partition
PatternsOnSystemOther []string // patterns (using '%' to denote a prefix match) to put odex on the system_other partition
@@ -252,8 +250,9 @@
}
type globalConfigAndRaw struct {
- global *GlobalConfig
- data []byte
+ global *GlobalConfig
+ data []byte
+ pathErrors []error
}
// GetGlobalConfig returns the global dexpreopt.config that's created in the
@@ -274,16 +273,26 @@
var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
+type pathContextErrorCollector struct {
+ android.PathContext
+ errors []error
+}
+
+func (p *pathContextErrorCollector) Errorf(format string, args ...interface{}) {
+ p.errors = append(p.errors, fmt.Errorf(format, args...))
+}
+
func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
- return ctx.Config().Once(globalConfigOnceKey, func() interface{} {
+ config := ctx.Config().Once(globalConfigOnceKey, func() interface{} {
if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
panic(err)
} else if data != nil {
- globalConfig, err := ParseGlobalConfig(ctx, data)
+ pathErrorCollectorCtx := &pathContextErrorCollector{PathContext: ctx}
+ globalConfig, err := ParseGlobalConfig(pathErrorCollectorCtx, data)
if err != nil {
panic(err)
}
- return globalConfigAndRaw{globalConfig, data}
+ return globalConfigAndRaw{globalConfig, data, pathErrorCollectorCtx.errors}
}
// No global config filename set, see if there is a test config set
@@ -293,16 +302,35 @@
DisablePreopt: true,
DisablePreoptBootImages: true,
DisableGenerateProfile: true,
- }, nil}
+ }, nil, nil}
})
}).(globalConfigAndRaw)
+
+ // Avoid non-deterministic errors by reporting cached path errors on all callers.
+ for _, err := range config.pathErrors {
+ if ctx.Config().AllowMissingDependencies() {
+ // When AllowMissingDependencies it set, report errors through AddMissingDependencies.
+ // If AddMissingDependencies doesn't exist on the current context (for example when
+ // called with a SingletonContext), just swallow the errors since there is no way to
+ // report them.
+ if missingDepsCtx, ok := ctx.(interface {
+ AddMissingDependencies(missingDeps []string)
+ }); ok {
+ missingDepsCtx.AddMissingDependencies([]string{err.Error()})
+ }
+ } else {
+ android.ReportPathErrorf(ctx, "%w", err)
+ }
+ }
+
+ return config
}
// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
// will return. It must be called before the first call to GetGlobalConfig for
// the config.
func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
- config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
+ config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil, nil} })
}
// This struct is required to convert ModuleConfig from/to JSON.
diff --git a/docs/rbe.json b/docs/rbe.json
new file mode 100644
index 0000000..f6ff107
--- /dev/null
+++ b/docs/rbe.json
@@ -0,0 +1,24 @@
+{
+ "env": {
+ "USE_RBE": "1",
+
+ "RBE_R8_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_CXX_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_D8_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_JAVAC_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_JAVAC": "1",
+ "RBE_R8": "1",
+ "RBE_D8": "1",
+
+ "RBE_instance": "[replace with your RBE instance]",
+ "RBE_service": "[replace with your RBE service endpoint]",
+
+ "RBE_DIR": "prebuilts/remoteexecution-client/live",
+
+ "RBE_use_application_default_credentials": "true",
+
+ "RBE_log_dir": "/tmp",
+ "RBE_output_dir": "/tmp",
+ "RBE_proxy_log_dir": "/tmp"
+ }
+}
diff --git a/docs/rbe.md b/docs/rbe.md
new file mode 100644
index 0000000..cfe86d7
--- /dev/null
+++ b/docs/rbe.md
@@ -0,0 +1,70 @@
+# Build Android Platform on Remote Build Execution
+
+Soong is integrated with Google's Remote Build Execution(RBE) service, which
+implements the
+[Remote Executaion API](https://github.com/bazelbuild/remote-apis).
+
+With RBE enabled, it can speed up the Android Platform builds by distributing
+build actions through a worker pool sharing a central cache of build results.
+
+## Configuration
+
+To enable RBE, you need to set several environment variables before triggering
+the build. You can set them through a
+[environment variables config file](https://android.googlesource.com/platform/build/soong/+/master/README.md#environment-variables-config-file).
+As an example, [build/soong/docs/rbe.json](rbe.json) is a config that enables
+RBE in the build. Once the config file is created, you need to let Soong load
+the config file by specifying `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment
+variable and `ANDROID_BUILD_ENVIRONMENT_CONFIG` environment variable. The
+following command starts Soong with [build/soong/docs/rbe.json](rbe.json)
+loaded:
+
+```shell
+ANDROID_BUILD_ENVIRONMENT_CONFIG=rbe \
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong/doc \
+ build/soong/soong_ui.bash
+```
+
+### Configuration Explanation
+
+Below a brief explanation of each field in
+[build/soong/docs/rbe.json](rbe.json):
+
+##### USE\_RBE:
+If set to 1, enable RBE for the build.
+
+##### RBE\_CXX\_EXEC\_STRATEGY / RBE\_JAVAC\_EXEC\_STRATEGY / RBE\_R8\_EXEC\_STRATEGY / RBE\_D8\_EXEC\_STRATEGY:
+
+Sets strategies for C++/javac/r8/d8 action types. Available options are
+(**Note**: all options will update the remote cache if the right permissions to
+update cache are given to the user.):
+
+* **local**: Only execute locally.
+* **remote**: Only execute remotely.
+* **remote_local_fallback**: Try executing remotely and fall back to local
+ execution if failed.
+* **racing**: Race remote execution and local execution and use the earlier
+ result.
+
+##### RBE\_JAVAC / RBE\_R8 / RBE\_D8
+
+If set to 1, enable javac/r8/d8 support. C++ compilation is enabled by default.
+
+##### RBE\_service / RBE\_instance
+
+The remote execution service endpoint and instance ID to target when calling
+remote execution via gRPC to execute actions.
+
+##### RBE\_DIR
+
+Where to find remote client binaries (rewrapper, reproxy)
+
+##### RBE\_use\_application\_default\_credentials
+
+reclient uses
+[application default credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login)
+for autentication, as generated by `gcloud auth application-default login`
+
+##### RBE\_log\_dir/RBE\_proxy\_log\_dir/RBE\_output\_dir
+
+Logs generated by rewrapper and reproxy will go here.
diff --git a/docs/tidy.md b/docs/tidy.md
index 3140198..2eb8234 100644
--- a/docs/tidy.md
+++ b/docs/tidy.md
@@ -31,7 +31,7 @@
The global default can be overwritten by module properties in Android.bp.
-### `tidy` and `tidy_checks`
+### `tidy`, `tidy_checks`, and `ALLOW_LOCAL_TIDY_TRUE`
For example, in
[system/bpf/Android.bp](https://android.googlesource.com/platform/system/bpf/+/refs/heads/master/Android.bp),
@@ -52,8 +52,16 @@
}
```
That means in normal builds, even without `WITH_TIDY=1`,
-the modules that use `bpf_defaults` will run clang-tidy
+the modules that use `bpf_defaults` _should_ run clang-tidy
over C/C++ source files with the given `tidy_checks`.
+
+However since clang-tidy warnings and its runtime cost might
+not be wanted by all people, the default is to ignore the
+`tidy:true` property unless the environment variable
+`ALLOW_LOCAL_TIDY_TRUE` is set to true or 1.
+To run clang-tidy on all modules that should be tested with clang-tidy,
+`ALLOW_LOCAL_TIDY_TRUE` or `WITH_TIDY` should be set to true or 1.
+
Note that `clang-analyzer-security*` is included in `tidy_checks`
but not all `clang-analyzer-*` checks. Check `cert-err34-c` is
disabled, although `cert-*` is selected.
@@ -80,6 +88,9 @@
}
```
+Note that `tidy:false` always disables clang-tidy, no matter
+`ALLOW_LOCAL_TIDY_TRUE` is set or not.
+
### `tidy_checks_as_errors`
The global tidy checks are enabled as warnings.
@@ -225,6 +236,16 @@
in several Android continuous builds where `WITH_TIDY=1` and
`CLANG_ANALYZER_CHECKS=1` are set.
+Similar to `tidy_disabled_srcs` a `tidy_timeout_srcs` list
+can be used to include all source files that took too much time to compile
+with clang-tidy. Files listed in `tidy_timeout_srcs` will not
+be compiled by clang-tidy when `TIDY_TIMEOUT` is defined.
+This can save global build time, when it is necessary to set some
+time limit globally to finish in an acceptable time.
+For developers who want to find all clang-tidy warnings and
+are willing to spend more time on all files in a project,
+they should not define `TIDY_TIMEOUT` and build only the wanted project directories.
+
## Capabilities for Android.bp and Android.mk
Some of the previously mentioned features are defined only
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index a142833..719771f 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -474,6 +474,7 @@
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
android.InitDefaultableModule(module)
+ android.InitBazelModule(module)
return module
}
@@ -668,25 +669,17 @@
// For Bazel / bp2build
-type bazelPrebuiltEtcAttributes struct {
+type bazelPrebuiltFileAttributes struct {
Src bazel.LabelAttribute
Filename string
- Sub_dir string
+ Dir string
Installable bazel.BoolAttribute
}
// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
-func (p *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- // All prebuilt_* modules are PrebuiltEtc, but at this time, we only convert prebuilt_etc modules.
- if p.installDirBase != "etc" {
- return
- }
-
- prebuiltEtcBp2BuildInternal(ctx, p)
-}
-
-func prebuiltEtcBp2BuildInternal(ctx android.TopDownMutatorContext, module *PrebuiltEtc) {
- var srcLabelAttribute bazel.LabelAttribute
+// All prebuilt_* modules are PrebuiltEtc, which we treat uniformily as *PrebuiltFile*
+func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ var src bazel.LabelAttribute
for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltEtcProperties{}) {
for config, p := range configToProps {
props, ok := p.(*prebuiltEtcProperties)
@@ -695,7 +688,7 @@
}
if props.Src != nil {
label := android.BazelLabelForModuleSrcSingle(ctx, *props.Src)
- srcLabelAttribute.SetSelectValue(axis, config, label)
+ src.SetSelectValue(axis, config, label)
}
}
}
@@ -705,26 +698,30 @@
filename = *module.properties.Filename
}
- var subDir string
- if module.subdirProperties.Sub_dir != nil {
- subDir = *module.subdirProperties.Sub_dir
+ var dir = module.installDirBase
+ // prebuilt_file supports only `etc` or `usr/share`
+ if !(dir == "etc" || dir == "usr/share") {
+ return
+ }
+ if subDir := module.subdirProperties.Sub_dir; subDir != nil {
+ dir = dir + "/" + *subDir
}
- var installableBoolAttribute bazel.BoolAttribute
- if module.properties.Installable != nil {
- installableBoolAttribute.Value = module.properties.Installable
+ var installable bazel.BoolAttribute
+ if install := module.properties.Installable; install != nil {
+ installable.Value = install
}
- attrs := &bazelPrebuiltEtcAttributes{
- Src: srcLabelAttribute,
+ attrs := &bazelPrebuiltFileAttributes{
+ Src: src,
Filename: filename,
- Sub_dir: subDir,
- Installable: installableBoolAttribute,
+ Dir: dir,
+ Installable: installable,
}
props := bazel.BazelTargetModuleProperties{
- Rule_class: "prebuilt_etc",
- Bzl_load_location: "//build/bazel/rules:prebuilt_etc.bzl",
+ Rule_class: "prebuilt_file",
+ Bzl_load_location: "//build/bazel/rules:prebuilt_file.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/example_config.json b/example_config.json
new file mode 100644
index 0000000..7489840
--- /dev/null
+++ b/example_config.json
@@ -0,0 +1,6 @@
+{
+ "env": {
+ "ENV_VAR_1": "Value1",
+ "ENV_VAR_2": "Value2"
+ }
+}
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 38684d3..857dfa7 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -15,6 +15,7 @@
"bootimg.go",
"filesystem.go",
"logical_partition.go",
+ "raw_binary.go",
"system_image.go",
"vbmeta.go",
"testing.go",
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 33beb37..352b451 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -228,6 +228,15 @@
return output
}
+// Calculates avb_salt from some input for deterministic output.
+func (b *bootimg) salt() string {
+ var input []string
+ input = append(input, b.properties.Cmdline...)
+ input = append(input, proptools.StringDefault(b.properties.Partition_name, b.Name()))
+ input = append(input, proptools.String(b.properties.Header_version))
+ return sha1sum(input)
+}
+
func (b *bootimg) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
var sb strings.Builder
var deps android.Paths
@@ -248,6 +257,7 @@
addStr("avb_add_hash_footer_args", "") // TODO(jiyong): add --rollback_index
partitionName := proptools.StringDefault(b.properties.Partition_name, b.Name())
addStr("partition_name", partitionName)
+ addStr("avb_salt", b.salt())
propFile = android.PathForModuleOut(ctx, "prop").OutputPath
android.WriteFileRule(ctx, propFile, sb.String())
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 0796258..6e1e78a 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -15,7 +15,9 @@
package filesystem
import (
+ "crypto/sha256"
"fmt"
+ "io"
"path/filepath"
"strings"
@@ -43,8 +45,14 @@
// Function that builds extra files under the root directory and returns the files
buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
+ // Function that filters PackagingSpecs returned by PackagingBase.GatherPackagingSpecs()
+ filterPackagingSpecs func(specs map[string]android.PackagingSpec)
+
output android.OutputPath
installDir android.InstallPath
+
+ // For testing. Keeps the result of CopyDepsToZip()
+ entries []string
}
type symlinkDefinition struct {
@@ -82,6 +90,13 @@
// Symbolic links to be created under root with "ln -sf <target> <name>".
Symlinks []symlinkDefinition
+
+ // Seconds since unix epoch to override timestamps of file entries
+ Fake_timestamp *string
+
+ // When set, passed to mkuserimg_mke2fs --mke2fs_uuid & --mke2fs_hash_seed.
+ // Otherwise, they'll be set as random which might cause indeterministic build output.
+ Uuid *string
}
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -226,7 +241,7 @@
func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.CopyDepsToZip(ctx, depsZipFile)
+ f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
builder := android.NewRuleBuilder(pctx, ctx)
depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
@@ -270,6 +285,11 @@
return fcBin.OutputPath
}
+// Calculates avb_salt from entry list (sorted) for deterministic output.
+func (f *filesystem) salt() string {
+ return sha1sum(f.entries)
+}
+
func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
type prop struct {
name string
@@ -315,12 +335,19 @@
addStr("avb_add_hashtree_footer_args", "--do_not_generate_fec")
partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name())
addStr("partition_name", partitionName)
+ addStr("avb_salt", f.salt())
}
if proptools.String(f.properties.File_contexts) != "" {
addPath("selinux_fc", f.buildFileContexts(ctx))
}
-
+ if timestamp := proptools.String(f.properties.Fake_timestamp); timestamp != "" {
+ addStr("timestamp", timestamp)
+ }
+ if uuid := proptools.String(f.properties.Uuid); uuid != "" {
+ addStr("uuid", uuid)
+ addStr("hash_seed", uuid)
+ }
propFile = android.PathForModuleOut(ctx, "prop").OutputPath
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().Text("rm").Flag("-rf").Output(propFile)
@@ -345,7 +372,7 @@
}
depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.CopyDepsToZip(ctx, depsZipFile)
+ f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
builder := android.NewRuleBuilder(pctx, ctx)
depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
@@ -434,3 +461,22 @@
}
return nil
}
+
+// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
+// Note that "apex" module installs its contents to "apex"(fake partition) as well
+// for symbol lookup by imitating "activated" paths.
+func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
+ specs := f.PackagingBase.GatherPackagingSpecs(ctx)
+ if f.filterPackagingSpecs != nil {
+ f.filterPackagingSpecs(specs)
+ }
+ return specs
+}
+
+func sha1sum(values []string) string {
+ h := sha256.New()
+ for _, value := range values {
+ io.WriteString(h, value)
+ }
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index e78fdff..cda06d9 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -45,11 +45,11 @@
func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) {
result := fixture.RunTestWithBp(t, `
- android_system_image {
+ android_system_image {
name: "myfilesystem",
deps: [
"libfoo",
- "libbar",
+ "libbar",
],
linker_config_src: "linker.config.json",
}
@@ -74,3 +74,54 @@
android.AssertStringDoesNotContain(t, "linker.config.pb should not have libbar",
output.RuleParams.Command, "libbar.so")
}
+
+func registerComponent(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("component", componentFactory)
+}
+
+func componentFactory() android.Module {
+ m := &component{}
+ m.AddProperties(&m.properties)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ return m
+}
+
+type component struct {
+ android.ModuleBase
+ properties struct {
+ Install_copy_in_data []string
+ }
+}
+
+func (c *component) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ output := android.PathForModuleOut(ctx, c.Name())
+ dir := android.PathForModuleInstall(ctx, "components")
+ ctx.InstallFile(dir, c.Name(), output)
+
+ dataDir := android.PathForModuleInPartitionInstall(ctx, "data", "components")
+ for _, d := range c.properties.Install_copy_in_data {
+ ctx.InstallFile(dataDir, d, output)
+ }
+}
+
+func TestFileSystemGathersItemsOnlyInSystemPartition(t *testing.T) {
+ f := android.GroupFixturePreparers(fixture, android.FixtureRegisterWithContext(registerComponent))
+ result := f.RunTestWithBp(t, `
+ android_system_image {
+ name: "myfilesystem",
+ multilib: {
+ common: {
+ deps: ["foo"],
+ },
+ },
+ linker_config_src: "linker.config.json",
+ }
+ component {
+ name: "foo",
+ install_copy_in_data: ["bar"],
+ }
+ `)
+
+ module := result.ModuleForTests("myfilesystem", "android_common").Module().(*systemImage)
+ android.AssertDeepEquals(t, "entries should have foo only", []string{"components/foo"}, module.entries)
+}
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
new file mode 100644
index 0000000..1544ea7
--- /dev/null
+++ b/filesystem/raw_binary.go
@@ -0,0 +1,121 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+ "fmt"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+var (
+ toRawBinary = pctx.AndroidStaticRule("toRawBinary",
+ blueprint.RuleParams{
+ Command: "${objcopy} --output-target=binary ${in} ${out} &&" +
+ "chmod -x ${out}",
+ CommandDeps: []string{"$objcopy"},
+ },
+ "objcopy")
+)
+
+func init() {
+ pctx.Import("android/soong/cc/config")
+
+ android.RegisterModuleType("raw_binary", rawBinaryFactory)
+}
+
+type rawBinary struct {
+ android.ModuleBase
+
+ properties rawBinaryProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type rawBinaryProperties struct {
+ // Set the name of the output. Defaults to <module_name>.bin.
+ Stem *string
+
+ // Name of input executable. Can be a name of a target.
+ Src *string `android:"path,arch_variant"`
+}
+
+func rawBinaryFactory() android.Module {
+ module := &rawBinary{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+func (r *rawBinary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // do nothing
+}
+
+func (r *rawBinary) installFileName() string {
+ return proptools.StringDefault(r.properties.Stem, r.BaseModuleName()+".bin")
+}
+
+func (r *rawBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ inputFile := android.PathForModuleSrc(ctx, proptools.String(r.properties.Src))
+ outputFile := android.PathForModuleOut(ctx, r.installFileName()).OutputPath
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: toRawBinary,
+ Description: "raw binary " + outputFile.Base(),
+ Output: outputFile,
+ Input: inputFile,
+ Args: map[string]string{
+ "objcopy": "${config.ClangBin}/llvm-objcopy",
+ },
+ })
+
+ r.output = outputFile
+ r.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(r.installDir, r.installFileName(), r.output)
+}
+
+var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (r *rawBinary) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(r.output),
+ }}
+}
+
+var _ Filesystem = (*rawBinary)(nil)
+
+func (r *rawBinary) OutputPath() android.Path {
+ return r.output
+}
+
+func (r *rawBinary) SignedOutputPath() android.Path {
+ return nil
+}
+
+var _ android.OutputFileProducer = (*rawBinary)(nil)
+
+// Implements android.OutputFileProducer
+func (r *rawBinary) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return []android.Path{r.output}, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 1d24d6d..75abf70 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -37,6 +37,7 @@
module := &systemImage{}
module.AddProperties(&module.properties)
module.filesystem.buildExtraFiles = module.buildExtraFiles
+ module.filesystem.filterPackagingSpecs = module.filterPackagingSpecs
initFilesystemModule(&module.filesystem)
return module
}
@@ -53,7 +54,7 @@
// we need "Module"s for packaging items
var otherModules []android.Module
- deps := s.GatherPackagingSpecs(ctx)
+ deps := s.gatherFilteredPackagingSpecs(ctx)
ctx.WalkDeps(func(child, parent android.Module) bool {
for _, ps := range child.PackagingSpecs() {
if _, ok := deps[ps.RelPathInPackage()]; ok {
@@ -68,3 +69,14 @@
builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
return output
}
+
+// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
+// Note that "apex" module installs its contents to "apex"(fake partition) as well
+// for symbol lookup by imitating "activated" paths.
+func (s *systemImage) filterPackagingSpecs(specs map[string]android.PackagingSpec) {
+ for k, ps := range specs {
+ if ps.Partition() != "system" {
+ delete(specs, k)
+ }
+ }
+}
diff --git a/finder/finder.go b/finder/finder.go
index 5413fa6..c5196c8 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -94,6 +94,10 @@
// RootDirs are the root directories used to initiate the search
RootDirs []string
+ // Whether symlinks are followed. If set, symlinks back to their own parent
+ // directory don't work.
+ FollowSymlinks bool
+
// ExcludeDirs are directory names that if encountered are removed from the search
ExcludeDirs []string
@@ -847,6 +851,7 @@
if err != nil {
return errors.New("No data to load from database\n")
}
+ defer reader.Close()
bufferedReader := bufio.NewReader(reader)
if !f.validateCacheHeader(bufferedReader) {
return errors.New("Cache header does not match")
@@ -1414,9 +1419,14 @@
// If stat fails this is probably a broken or dangling symlink, treat it as a file.
subfiles = append(subfiles, child.Name())
} else if childStat.IsDir() {
- // Skip symlink dirs.
- // We don't have to support symlink dirs because
- // that would cause duplicates.
+ // Skip symlink dirs if not requested otherwise. Android has a number
+ // of symlinks creating infinite source trees which would otherwise get
+ // us in an infinite loop.
+ // TODO(b/197349722): Revisit this once symlink loops are banned in the
+ // source tree.
+ if f.cacheMetadata.Config.FollowSymlinks {
+ subdirs = append(subdirs, child.Name())
+ }
} else {
// We do have to support symlink files because the link name might be
// different than the target name
diff --git a/finder/finder_test.go b/finder/finder_test.go
index 788dbdd..8f73719 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -90,6 +90,7 @@
CacheParams{
"/cwd",
[]string{root},
+ false,
nil,
nil,
[]string{"findme.txt", "skipme.txt"},
@@ -121,6 +122,7 @@
CacheParams{
"/cwd",
[]string{root},
+ false,
nil,
nil,
[]string{"findme.txt", "skipme.txt"},
diff --git a/finder/fs/test.go b/finder/fs/test.go
index cb2140e..ed981fd 100644
--- a/finder/fs/test.go
+++ b/finder/fs/test.go
@@ -74,6 +74,7 @@
if err != nil {
t.Fatalf(err.Error())
}
+ defer reader.Close()
bytes, err := ioutil.ReadAll(reader)
if err != nil {
t.Fatal(err.Error())
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 89f8187..700cdf0 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -82,6 +82,9 @@
Hwasan_options []string `json:"hwasan_options,omitempty"`
// Additional options to be passed to HWASAN when running on host in Haiku.
Asan_options []string `json:"asan_options,omitempty"`
+ // If there's a Java fuzzer with JNI, a different version of Jazzer would
+ // need to be added to the fuzzer package than one without JNI
+ IsJni *bool `json:"is_jni,omitempty"`
}
type FuzzProperties struct {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 1679a57..818e1bc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -25,6 +25,7 @@
"strconv"
"strings"
+ "android/soong/bazel/cquery"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
@@ -189,6 +190,8 @@
modulePaths []string
}
+var _ android.MixedBuildBuildable = (*Module)(nil)
+
type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
type generateTask struct {
@@ -249,27 +252,36 @@
}
}
-// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
-func (c *Module) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (g *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+ g.generateCommonBuildActions(ctx)
+
+ label := g.GetBazelLabel(ctx, g)
bazelCtx := ctx.Config().BazelContext
- filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
- if ok {
- var bazelOutputFiles android.Paths
- exportIncludeDirs := map[string]bool{}
- for _, bazelOutputFile := range filePaths {
- bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
- exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true
- }
- c.outputFiles = bazelOutputFiles
- c.outputDeps = bazelOutputFiles
- for includePath, _ := range exportIncludeDirs {
- c.exportedIncludeDirs = append(c.exportedIncludeDirs, android.PathForBazelOut(ctx, includePath))
- }
+ filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
}
- return ok
+
+ var bazelOutputFiles android.Paths
+ exportIncludeDirs := map[string]bool{}
+ for _, bazelOutputFile := range filePaths {
+ bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
+ exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true
+ }
+ g.outputFiles = bazelOutputFiles
+ g.outputDeps = bazelOutputFiles
+ for includePath, _ := range exportIncludeDirs {
+ g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForBazelOut(ctx, includePath))
+ }
}
-func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+// generateCommonBuildActions contains build action generation logic
+// common to both the mixed build case and the legacy case of genrule processing.
+// To fully support genrule in mixed builds, the contents of this function should
+// approach zero; there should be no genrule action registration done directly
+// by Soong logic in the mixed-build case.
+func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) {
g.subName = ctx.ModuleSubDir()
// Collect the module directory for IDE info in java/jdeps.go.
@@ -294,7 +306,7 @@
if _, exists := locationLabels[label]; !exists {
locationLabels[label] = loc
} else {
- ctx.ModuleErrorf("multiple labels for %q, %q and %q",
+ ctx.ModuleErrorf("multiple locations for label %q: %q and %q (do you have duplicate srcs entries?)",
label, locationLabels[label], loc)
}
}
@@ -382,9 +394,12 @@
addLocationLabel(toolFile, toolLocation{paths})
}
+ includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name())
var srcFiles android.Paths
for _, in := range g.properties.Srcs {
- paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
+ paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{
+ Context: ctx, Paths: []string{in}, ExcludePaths: g.properties.Exclude_srcs, IncludeDirs: includeDirInPaths,
+ })
if len(missingDeps) > 0 {
if !ctx.Config().AllowMissingDependencies() {
panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
@@ -496,7 +511,7 @@
}
return paths[0], nil
} else {
- return reportError("unknown location label %q", label)
+ return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label)
}
} else if strings.HasPrefix(name, "locations ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
@@ -507,7 +522,7 @@
}
return strings.Join(paths, " "), nil
} else {
- return reportError("unknown locations label %q", label)
+ return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label)
}
} else {
return reportError("unknown variable '$(%s)'", name)
@@ -572,29 +587,35 @@
}
g.outputFiles = outputFiles.Paths()
+}
- bazelModuleLabel := g.GetBazelLabel(ctx, g)
- bazelActionsUsed := false
- if g.MixedBuildsEnabled(ctx) {
- bazelActionsUsed = g.GenerateBazelBuildActions(ctx, bazelModuleLabel)
+func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ g.generateCommonBuildActions(ctx)
+
+ // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
+ // the genrules on AOSP. That will make things simpler to look at the graph in the common
+ // case. For larger sets of outputs, inject a phony target in between to limit ninja file
+ // growth.
+ if len(g.outputFiles) <= 6 {
+ g.outputDeps = g.outputFiles
+ } else {
+ phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Output: phonyFile,
+ Inputs: g.outputFiles,
+ })
+ g.outputDeps = android.Paths{phonyFile}
}
- if !bazelActionsUsed {
- // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
- // the genrules on AOSP. That will make things simpler to look at the graph in the common
- // case. For larger sets of outputs, inject a phony target in between to limit ninja file
- // growth.
- if len(g.outputFiles) <= 6 {
- g.outputDeps = g.outputFiles
- } else {
- phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
- ctx.Build(pctx, android.BuildParams{
- Rule: blueprint.Phony,
- Output: phonyFile,
- Inputs: g.outputFiles,
- })
- g.outputDeps = android.Paths{phonyFile}
- }
- }
+}
+
+func (g *Module) QueueBazelCall(ctx android.BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(g.GetBazelLabel(ctx, g), cquery.GetOutputFiles, android.GetConfigKey(ctx))
+}
+
+func (g *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ return true
}
// Collect information for opening IDE project files in java/jdeps.go.
@@ -784,6 +805,7 @@
func GenSrcsFactory() android.Module {
m := NewGenSrcs()
android.InitAndroidModule(m)
+ android.InitBazelModule(m)
return m
}
@@ -795,6 +817,13 @@
Shard_size *int64
}
+type bazelGensrcsAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Output_extension *string
+ Tools bazel.LabelListAttribute
+ Cmd string
+}
+
const defaultShardSize = 50
func NewGenRule() *Module {
@@ -859,8 +888,14 @@
// Replace in and out variables with $< and $@
var cmd string
if m.properties.Cmd != nil {
- cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
- cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+ if ctx.ModuleType() == "gensrcs" {
+ cmd = strings.ReplaceAll(*m.properties.Cmd, "$(in)", "$(SRC)")
+ cmd = strings.ReplaceAll(cmd, "$(out)", "$(OUT)")
+ } else {
+ cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
+ cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+ }
+
genDir := "$(GENDIR)"
if t := ctx.ModuleType(); t == "cc_genrule" || t == "java_genrule" || t == "java_genrule_host" {
genDir = "$(RULEDIR)"
@@ -880,30 +915,50 @@
}
}
- // The Out prop is not in an immediately accessible field
- // in the Module struct, so use GetProperties and cast it
- // to the known struct prop.
- var outs []string
- for _, propIntf := range m.GetProperties() {
- if props, ok := propIntf.(*genRuleProperties); ok {
- outs = props.Out
- break
+ if ctx.ModuleType() == "gensrcs" {
+ // The Output_extension prop is not in an immediately accessible field
+ // in the Module struct, so use GetProperties and cast it
+ // to the known struct prop.
+ var outputExtension *string
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*genSrcsProperties); ok {
+ outputExtension = props.Output_extension
+ break
+ }
}
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "gensrcs",
+ Bzl_load_location: "//build/bazel/rules:gensrcs.bzl",
+ }
+ attrs := &bazelGensrcsAttributes{
+ Srcs: srcs,
+ Output_extension: outputExtension,
+ Cmd: cmd,
+ Tools: tools,
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+ } else {
+ // The Out prop is not in an immediately accessible field
+ // in the Module struct, so use GetProperties and cast it
+ // to the known struct prop.
+ var outs []string
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*genRuleProperties); ok {
+ outs = props.Out
+ break
+ }
+ }
+ attrs := &bazelGenruleAttributes{
+ Srcs: srcs,
+ Outs: outs,
+ Cmd: cmd,
+ Tools: tools,
+ }
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "genrule",
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
}
-
- attrs := &bazelGenruleAttributes{
- Srcs: srcs,
- Outs: outs,
- Cmd: cmd,
- Tools: tools,
- }
-
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "genrule",
- }
-
- // Create the BazelTargetModule.
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
}
var Bool = proptools.Bool
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 04c97fd..1b5cef2 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -341,7 +341,7 @@
out: ["out"],
cmd: "echo foo > $(location missing)",
`,
- err: `unknown location label "missing"`,
+ err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
},
{
name: "error locations",
@@ -349,7 +349,7 @@
out: ["out"],
cmd: "echo foo > $(locations missing)",
`,
- err: `unknown locations label "missing"`,
+ err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
},
{
name: "error location no files",
diff --git a/go.mod b/go.mod
index 14444b3..8c1a9f0 100644
--- a/go.mod
+++ b/go.mod
@@ -16,4 +16,4 @@
// Indirect dep from go-cmp
exclude golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
-go 1.15
+go 1.18
diff --git a/java/Android.bp b/java/Android.bp
index c062941..e25accf 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -15,6 +15,7 @@
"soong-dexpreopt",
"soong-genrule",
"soong-java-config",
+ "soong-provenance",
"soong-python",
"soong-remoteexec",
"soong-tradefed",
@@ -81,10 +82,12 @@
"app_test.go",
"bootclasspath_fragment_test.go",
"device_host_converter_test.go",
+ "dex_test.go",
"dexpreopt_test.go",
"dexpreopt_bootjars_test.go",
"droiddoc_test.go",
"droidstubs_test.go",
+ "genrule_test.go",
"hiddenapi_singleton_test.go",
"jacoco_test.go",
"java_test.go",
@@ -95,6 +98,7 @@
"platform_compat_config_test.go",
"plugin_test.go",
"prebuilt_apis_test.go",
+ "proto_test.go",
"rro_test.go",
"sdk_test.go",
"sdk_library_test.go",
diff --git a/java/aar.go b/java/aar.go
index aabbec6..00ff7e7 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -267,18 +267,29 @@
})
func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext,
- classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string,
+ extraLinkFlags ...string) {
transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
aaptLibs(ctx, sdkContext, classLoaderContexts)
+ // Exclude any libraries from the supplied list.
+ classLoaderContexts = classLoaderContexts.ExcludeLibs(excludedLibs)
+
// App manifest file
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
- manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts,
- a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode,
- a.LoggingParent)
+ manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
+ SdkContext: sdkContext,
+ ClassLoaderContexts: classLoaderContexts,
+ IsLibrary: a.isLibrary,
+ UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs,
+ UsesNonSdkApis: a.usesNonSdkApis,
+ UseEmbeddedDex: a.useEmbeddedDex,
+ HasNoCode: a.hasNoCode,
+ LoggingParent: a.LoggingParent,
+ })
// Add additional manifest files to transitive manifests.
additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
@@ -520,7 +531,7 @@
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.aapt.isLibrary = true
a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
- a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
+ a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil)
a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
@@ -587,16 +598,26 @@
// AAR (android library) prebuilts
//
+// Properties for android_library_import
type AARImportProperties struct {
+ // ARR (android library prebuilt) filepath. Exactly one ARR is required.
Aars []string `android:"path"`
-
- Sdk_version *string
+ // If not blank, set to the version of the sdk to compile against.
+ // Defaults to private.
+ // Values are of one of the following forms:
+ // 1) numerical API level, "current", "none", or "core_platform"
+ // 2) An SDK kind with an API level: "<sdk kind>_<API level>"
+ // See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
+ // If the SDK kind is empty, it will be set to public
+ Sdk_version *string
+ // If not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+ // Defaults to sdk_version if not set. See sdk_version for possible values.
Min_sdk_version *string
-
+ // List of java static libraries that the included ARR (android library prebuilts) has dependencies to.
Static_libs []string
- Libs []string
-
- // if set to true, run Jetifier against .aar file. Defaults to false.
+ // List of java libraries that the included ARR (android library prebuilts) has dependencies to.
+ Libs []string
+ // If set to true, run Jetifier against .aar file. Defaults to false.
Jetifier *bool
}
diff --git a/java/android_manifest.go b/java/android_manifest.go
index f29d8ad..a297b2c 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -28,13 +28,10 @@
var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
blueprint.RuleParams{
Command: `${config.ManifestFixerCmd} ` +
- `--minSdkVersion ${minSdkVersion} ` +
- `--targetSdkVersion ${targetSdkVersion} ` +
- `--raise-min-sdk-version ` +
`$args $in $out`,
CommandDeps: []string{"${config.ManifestFixerCmd}"},
},
- "minSdkVersion", "targetSdkVersion", "args")
+ "args")
var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
blueprint.RuleParams{
@@ -58,84 +55,109 @@
return targetSdkVersion
}
-// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext,
- classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
- useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
+type ManifestFixerParams struct {
+ SdkContext android.SdkContext
+ ClassLoaderContexts dexpreopt.ClassLoaderContextMap
+ IsLibrary bool
+ UseEmbeddedNativeLibs bool
+ UsesNonSdkApis bool
+ UseEmbeddedDex bool
+ HasNoCode bool
+ TestOnly bool
+ LoggingParent string
+}
+// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
+func ManifestFixer(ctx android.ModuleContext, manifest android.Path,
+ params ManifestFixerParams) android.Path {
var args []string
- if isLibrary {
+
+ if params.IsLibrary {
args = append(args, "--library")
- } else {
- minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
+ } else if params.SdkContext != nil {
+ minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
if minSdkVersion.FinalOrFutureInt() >= 23 {
- args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs))
- } else if useEmbeddedNativeLibs {
- ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
- minSdkVersion)
+ args = append(args, fmt.Sprintf("--extract-native-libs=%v", !params.UseEmbeddedNativeLibs))
+ } else if params.UseEmbeddedNativeLibs {
+ ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%s doesn't support it",
+ minSdkVersion.String())
}
}
- if usesNonSdkApis {
+ if params.UsesNonSdkApis {
args = append(args, "--uses-non-sdk-api")
}
- if useEmbeddedDex {
+ if params.UseEmbeddedDex {
args = append(args, "--use-embedded-dex")
}
- // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
- // explicitly via `uses_libs`/`optional_uses_libs`.
- requiredUsesLibs, optionalUsesLibs := classLoaderContexts.ImplicitUsesLibs()
- for _, usesLib := range requiredUsesLibs {
- args = append(args, "--uses-library", usesLib)
- }
- for _, usesLib := range optionalUsesLibs {
- args = append(args, "--optional-uses-library", usesLib)
+ if params.ClassLoaderContexts != nil {
+ // Libraries propagated via `uses_libs`/`optional_uses_libs` are also added (they may be
+ // propagated from dependencies).
+ requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.UsesLibs()
+
+ for _, usesLib := range requiredUsesLibs {
+ args = append(args, "--uses-library", usesLib)
+ }
+ for _, usesLib := range optionalUsesLibs {
+ args = append(args, "--optional-uses-library", usesLib)
+ }
}
- if hasNoCode {
+ if params.HasNoCode {
args = append(args, "--has-no-code")
}
- if loggingParent != "" {
- args = append(args, "--logging-parent", loggingParent)
+ if params.TestOnly {
+ args = append(args, "--test-only")
+ }
+
+ if params.LoggingParent != "" {
+ args = append(args, "--logging-parent", params.LoggingParent)
}
var deps android.Paths
- targetSdkVersion := targetSdkVersionForManifestFixer(ctx, sdkContext)
+ var argsMapper = make(map[string]string)
- if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
- targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
- deps = append(deps, ApiFingerprintPath(ctx))
- }
+ if params.SdkContext != nil {
+ targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params.SdkContext)
+ args = append(args, "--targetSdkVersion ", targetSdkVersion)
- minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
- if err != nil {
- ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
- }
- if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
- minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
- deps = append(deps, ApiFingerprintPath(ctx))
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+ targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+ deps = append(deps, ApiFingerprintPath(ctx))
+ }
+
+ minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
+ if err != nil {
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ }
+
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+ minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+ deps = append(deps, ApiFingerprintPath(ctx))
+ }
+
+ if err != nil {
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ }
+ args = append(args, "--minSdkVersion ", minSdkVersion)
+ args = append(args, "--raise-min-sdk-version")
}
fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
- if err != nil {
- ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
- }
+ argsMapper["args"] = strings.Join(args, " ")
+
ctx.Build(pctx, android.BuildParams{
Rule: manifestFixerRule,
Description: "fix manifest",
Input: manifest,
Implicits: deps,
Output: fixedManifest,
- Args: map[string]string{
- "minSdkVersion": minSdkVersion,
- "targetSdkVersion": targetSdkVersion,
- "args": strings.Join(args, " "),
- },
+ Args: argsMapper,
})
return fixedManifest.WithoutRel()
diff --git a/java/androidmk.go b/java/androidmk.go
index b930441..82ef413 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -132,6 +132,16 @@
return entriesList
}
+func (j *JavaFuzzLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := j.Library.AndroidMkEntries()
+ entries := &entriesList[0]
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "null-suite")
+ androidMkWriteTestData(j.jniFilePaths, entries)
+ })
+ return entriesList
+}
+
// Called for modules that are a component of a test suite.
func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string, perTestcaseDirectory bool) {
entries.SetString("LOCAL_MODULE_TAGS", "tests")
@@ -314,7 +324,7 @@
}
func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries {
- if app.hideApexVariantFromMake || app.appProperties.HideFromMake {
+ if app.hideApexVariantFromMake || app.IsHideFromMake() {
return []android.AndroidMkEntries{android.AndroidMkEntries{
Disabled: true,
}}
@@ -409,29 +419,13 @@
entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
},
},
- ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string) {
- if app.noticeOutputs.Merged.Valid() {
- fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
- app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
- }
- if app.noticeOutputs.TxtOutput.Valid() {
- fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
- app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt")
- }
- if app.noticeOutputs.HtmlOutput.Valid() {
- fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
- app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html")
- }
- },
- },
}}
}
func (a *AndroidApp) getOverriddenPackages() []string {
var overridden []string
- if len(a.appProperties.Overrides) > 0 {
- overridden = append(overridden, a.appProperties.Overrides...)
+ if len(a.overridableAppProperties.Overrides) > 0 {
+ overridden = append(overridden, a.overridableAppProperties.Overrides...)
}
// When APK name is overridden via PRODUCT_PACKAGE_NAME_OVERRIDES
// ensure that the original name is overridden.
@@ -548,6 +542,9 @@
if !outputFile.Valid() {
outputFile = android.OptionalPathForPath(dstubs.apiFile)
}
+ if !outputFile.Valid() {
+ outputFile = android.OptionalPathForPath(dstubs.apiVersionsXml)
+ }
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "JAVA_LIBRARIES",
OutputFile: outputFile,
@@ -626,6 +623,7 @@
if dstubs.apiLintReport != nil {
fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", dstubs.Name()+"-api-lint",
dstubs.apiLintReport.String(), "apilint/"+dstubs.Name()+"-lint-report.txt")
+ fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", dstubs.apiLintReport.String())
}
}
if dstubs.checkNullabilityWarningsTimestamp != nil {
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 246c0eb..197da4f 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -206,3 +206,49 @@
t.Errorf("Unexpected flag value - expected: %q, actual: %q", expected, actual)
}
}
+
+func TestGetOverriddenPackages(t *testing.T) {
+ ctx, _ := testJava(
+ t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ overrides: ["qux"]
+ }
+
+ override_android_app {
+ name: "foo_override",
+ base: "foo",
+ overrides: ["bar"]
+ }
+ `)
+
+ expectedVariants := []struct {
+ name string
+ moduleName string
+ variantName string
+ overrides []string
+ }{
+ {
+ name: "foo",
+ moduleName: "foo",
+ variantName: "android_common",
+ overrides: []string{"qux"},
+ },
+ {
+ name: "foo",
+ moduleName: "foo_override",
+ variantName: "android_common_foo_override",
+ overrides: []string{"bar", "foo"},
+ },
+ }
+
+ for _, expected := range expectedVariants {
+ mod := ctx.ModuleForTests(expected.name, expected.variantName).Module()
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+ actual := entries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
+
+ android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
+ }
+}
diff --git a/java/app.go b/java/app.go
index 7ae73f7..c61c4e5 100755
--- a/java/app.go
+++ b/java/app.go
@@ -19,7 +19,6 @@
import (
"path/filepath"
- "sort"
"strings"
"github.com/google/blueprint"
@@ -64,13 +63,6 @@
// list of resource labels to generate individual resource packages
Package_splits []string
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-
// list of native libraries that will be provided in or alongside the resulting jar
Jni_libs []string `android:"arch_variant"`
@@ -107,7 +99,6 @@
// cc.Coverage related properties
PreventInstall bool `blueprint:"mutated"`
- HideFromMake bool `blueprint:"mutated"`
IsCoverageVariant bool `blueprint:"mutated"`
// Whether this app is considered mainline updatable or not. When set to true, this will enforce
@@ -134,6 +125,13 @@
// Whether to rename the package in resources to the override name rather than the base name. Defaults to true.
Rename_resources_package *bool
+
+ // Names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
}
type AndroidApp struct {
@@ -164,8 +162,6 @@
additionalAaptFlags []string
- noticeOutputs android.NoticeOutputs
-
overriddenManifestPackageName string
android.ApexBundleDepsInfo
@@ -302,10 +298,6 @@
// If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
// This check is enforced for "updatable" APKs (including APK-in-APEX).
-// b/155209650: until min_sdk_version is properly supported, use sdk_version instead.
-// because, sdk_version is overridden by min_sdk_version (if set as smaller)
-// and sdkLinkType is checked with dependencies so we can be sure that the whole dependency tree
-// will meet the requirements.
func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
ctx.VisitDirectDeps(func(m android.Module) {
@@ -316,10 +308,10 @@
// The domain of cc.sdk_version is "current" and <number>
// We can rely on android.SdkSpec to convert it to <number> so that "current" is
// handled properly regardless of sdk finalization.
- jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.SdkVersion()).EffectiveVersion(ctx)
+ jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.MinSdkVersion()).EffectiveVersion(ctx)
if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
- ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
- dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
+ ctx.OtherModuleErrorf(dep, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+ dep.MinSdkVersion(), minSdkVersion, ctx.ModuleName())
return
}
@@ -425,7 +417,8 @@
a.aapt.splitNames = a.appProperties.Package_splits
a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
- a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
+ a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts,
+ a.usesLibraryProperties.Exclude_uses_libs, aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
@@ -522,53 +515,6 @@
return jniSymbols
}
-func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
- // Collect NOTICE files from all dependencies.
- seenModules := make(map[android.Module]bool)
- noticePathSet := make(map[android.Path]bool)
-
- ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
- // Have we already seen this?
- if _, ok := seenModules[child]; ok {
- return false
- }
- seenModules[child] = true
-
- // Skip host modules.
- if child.Target().Os.Class == android.Host {
- return false
- }
-
- paths := child.(android.Module).NoticeFiles()
- if len(paths) > 0 {
- for _, path := range paths {
- noticePathSet[path] = true
- }
- }
- return true
- })
-
- // If the app has one, add it too.
- if len(a.NoticeFiles()) > 0 {
- for _, path := range a.NoticeFiles() {
- noticePathSet[path] = true
- }
- }
-
- if len(noticePathSet) == 0 {
- return
- }
- var noticePaths []android.Path
- for path := range noticePathSet {
- noticePaths = append(noticePaths, path)
- }
- sort.Slice(noticePaths, func(i, j int) bool {
- return noticePaths[i].String() < noticePaths[j].String()
- })
-
- a.noticeOutputs = android.BuildNoticeOutput(ctx, a.installDir, a.installApkName+".apk", noticePaths)
-}
-
// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
// isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
@@ -635,11 +581,6 @@
}
a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir)
- a.noticeBuildActions(ctx)
- if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
- a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
- }
-
a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
// Process all building blocks, from AAPT to certificates.
@@ -680,7 +621,21 @@
}
certificates := processMainCert(a.ModuleBase, a.getCertString(ctx), certificateDeps, ctx)
- a.certificate = certificates[0]
+
+ // This can be reached with an empty certificate list if AllowMissingDependencies is set
+ // and the certificate property for this module is a module reference to a missing module.
+ if len(certificates) > 0 {
+ a.certificate = certificates[0]
+ } else {
+ if !ctx.Config().AllowMissingDependencies() && len(ctx.GetMissingDependencies()) > 0 {
+ panic("Should only get here if AllowMissingDependencies set and there are missing dependencies")
+ }
+ // Set a certificate to avoid panics later when accessing it.
+ a.certificate = Certificate{
+ Key: android.PathForModuleOut(ctx, "missing.pk8"),
+ Pem: android.PathForModuleOut(ctx, "missing.pem"),
+ }
+ }
// Build a final signed app package.
packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk")
@@ -699,6 +654,18 @@
a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
}
+ if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+ noticeFile := android.PathForModuleOut(ctx, "NOTICE.html.gz")
+ android.BuildNoticeHtmlOutputFromLicenseMetadata(ctx, noticeFile, "", "", a.outputFile.String())
+ noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("cp").
+ Input(noticeFile).
+ Output(noticeAssetPath)
+ builder.Build("notice_dir", "Building notice dir")
+ a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath)
+ }
+
for _, split := range a.aapt.splits {
// Sign the split APKs
packageFile := android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk")
@@ -870,6 +837,10 @@
return Bool(a.appProperties.Updatable)
}
+func (a *AndroidApp) SetUpdatable(val bool) {
+ a.appProperties.Updatable = &val
+}
+
func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string {
certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
if overridden {
@@ -908,10 +879,6 @@
a.appProperties.PreventInstall = true
}
-func (a *AndroidApp) HideFromMake() {
- a.appProperties.HideFromMake = true
-}
-
func (a *AndroidApp) MarkAsCoverageVariant(coverage bool) {
a.appProperties.IsCoverageVariant = coverage
}
@@ -928,6 +895,7 @@
module.Module.dexProperties.Optimize.Shrink = proptools.BoolPtr(true)
module.Module.properties.Instrument = true
+ module.Module.properties.Supports_static_instrumentation = true
module.Module.properties.Installable = proptools.BoolPtr(true)
module.addHostAndDeviceProperties()
@@ -940,7 +908,7 @@
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
- android.InitOverridableModule(module, &module.appProperties.Overrides)
+ android.InitOverridableModule(module, &module.overridableAppProperties.Overrides)
android.InitApexModule(module)
android.InitBazelModule(module)
@@ -1044,9 +1012,10 @@
func AndroidTestFactory() android.Module {
module := &AndroidTest{}
- module.Module.dexProperties.Optimize.EnabledByDefault = true
+ module.Module.dexProperties.Optimize.EnabledByDefault = false
module.Module.properties.Instrument = true
+ module.Module.properties.Supports_static_instrumentation = true
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
module.appProperties.AlwaysPackageNativeLibs = true
@@ -1063,7 +1032,7 @@
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
- android.InitOverridableModule(module, &module.appProperties.Overrides)
+ android.InitOverridableModule(module, &module.overridableAppProperties.Overrides)
return module
}
@@ -1097,6 +1066,7 @@
func AndroidTestHelperAppFactory() android.Module {
module := &AndroidTestHelperApp{}
+ // TODO(b/192032291): Disable by default after auditing downstream usage.
module.Module.dexProperties.Optimize.EnabledByDefault = true
module.Module.properties.Installable = proptools.BoolPtr(true)
@@ -1211,6 +1181,23 @@
// libraries, because SDK ones are automatically picked up by Soong. The <uses-library> name
// normally is the same as the module name, but there are exceptions.
Provides_uses_lib *string
+
+ // A list of shared library names to exclude from the classpath of the APK. Adding a library here
+ // will prevent it from being used when precompiling the APK and prevent it from being implicitly
+ // added to the APK's manifest's <uses-library> elements.
+ //
+ // Care must be taken when using this as it could result in runtime errors if the APK actually
+ // uses classes provided by the library and which are not provided in any other way.
+ //
+ // This is primarily intended for use by various CTS tests that check the runtime handling of the
+ // android.test.base shared library (and related libraries) but which depend on some common
+ // libraries that depend on the android.test.base library. Without this those tests will end up
+ // with a <uses-library android:name="android.test.base"/> in their manifest which would either
+ // render the tests worthless (as they would be testing the wrong behavior), or would break the
+ // test altogether by providing access to classes that the tests were not expecting. Those tests
+ // provide the android.test.base statically and use jarjar to rename them so they do not collide
+ // with the classes provided by the android.test.base library.
+ Exclude_uses_libs []string
}
// usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the
@@ -1236,28 +1223,17 @@
func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
- reqTag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false, false)
- ctx.AddVariationDependencies(nil, reqTag, u.usesLibraryProperties.Uses_libs...)
-
- optTag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true, false)
- ctx.AddVariationDependencies(nil, optTag, u.presentOptionalUsesLibs(ctx)...)
-
+ ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
+ ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...)
// Only add these extra dependencies if the module depends on framework libs. This avoids
// creating a cyclic dependency:
// e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
if hasFrameworkLibs {
- // Add implicit <uses-library> dependencies on compatibility libraries. Some of them are
- // optional, and some required --- this depends on the most common usage of the library
- // and may be wrong for some apps (they need explicit `uses_libs`/`optional_uses_libs`).
-
- compat28OptTag := makeUsesLibraryDependencyTag(28, true, true)
- ctx.AddVariationDependencies(nil, compat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
-
- compat29ReqTag := makeUsesLibraryDependencyTag(29, false, true)
- ctx.AddVariationDependencies(nil, compat29ReqTag, dexpreopt.CompatUsesLibs29...)
-
- compat30OptTag := makeUsesLibraryDependencyTag(30, true, true)
- ctx.AddVariationDependencies(nil, compat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
+ // Dexpreopt needs paths to the dex jars of these libraries in order to construct
+ // class loader context for dex2oat. Add them as a dependency with a special tag.
+ ctx.AddVariationDependencies(nil, usesLibCompat29ReqTag, dexpreopt.CompatUsesLibs29...)
+ ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
+ ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
}
}
}
@@ -1316,7 +1292,7 @@
replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
}
- clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional, tag.implicit,
+ clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(),
lib.ClassLoaderContexts())
} else if ctx.Config().AllowMissingDependencies() {
@@ -1425,24 +1401,34 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_app_certificate",
- Bzl_load_location: "//build/bazel/rules:android_app_certificate.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
}
type bazelAndroidAppAttributes struct {
- Srcs bazel.LabelListAttribute
- Manifest bazel.Label
- Custom_package *string
- Resource_files bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
+ *javaCommonAttributes
+ Deps bazel.LabelListAttribute
+ Manifest bazel.Label
+ Custom_package *string
+ Resource_files bazel.LabelListAttribute
+ Certificate *bazel.Label
+ Certificate_name *string
}
// ConvertWithBp2build is used to convert android_app to Bazel.
func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- //TODO(b/209577426): Support multiple arch variants
- srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, a.properties.Srcs, a.properties.Exclude_srcs))
+ commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
+
+ deps := depLabels.Deps
+ if !commonAttrs.Srcs.IsEmpty() {
+ deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them
+ } else if !deps.IsEmpty() || !depLabels.StaticDeps.IsEmpty() {
+ ctx.ModuleErrorf("android_app has dynamic or static dependencies but no sources." +
+ " Bazel does not allow direct dependencies without sources nor exported" +
+ " dependencies on android_binary rule.")
+ }
manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
@@ -1454,18 +1440,31 @@
resourceFiles.Includes = append(resourceFiles.Includes, files...)
}
- deps := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, a.properties.Static_libs))
+ var certificate *bazel.Label
+ certificateNamePtr := a.overridableAppProperties.Certificate
+ certificateName := proptools.StringDefault(certificateNamePtr, "")
+ certModule := android.SrcIsModule(certificateName)
+ if certModule != "" {
+ c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
+ certificate = &c
+ certificateNamePtr = nil
+ }
attrs := &bazelAndroidAppAttributes{
- Srcs: srcs,
- Manifest: android.BazelLabelForModuleSrcSingle(ctx, manifest),
+ commonAttrs,
+ deps,
+ android.BazelLabelForModuleSrcSingle(ctx, manifest),
// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
- Custom_package: a.overridableAppProperties.Package_name,
- Resource_files: bazel.MakeLabelListAttribute(resourceFiles),
- Deps: deps,
+ a.overridableAppProperties.Package_name,
+ bazel.MakeLabelListAttribute(resourceFiles),
+ certificate,
+ certificateNamePtr,
}
- props := bazel.BazelTargetModuleProperties{Rule_class: "android_binary",
- Bzl_load_location: "@rules_android//rules:rules.bzl"}
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "android_binary",
+ Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+ }
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
diff --git a/java/app_import.go b/java/app_import.go
index 3e5f972..b017eca 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -22,6 +22,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/provenance"
)
func init() {
@@ -57,6 +58,8 @@
installPath android.InstallPath
hideApexVariantFromMake bool
+
+ provenanceMetaDataFile android.OutputPath
}
type AndroidAppImportProperties struct {
@@ -343,6 +346,8 @@
if apexInfo.IsForPlatform() {
a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
+ artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
+ a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
}
// TODO: androidmk converter jni libs
@@ -368,6 +373,10 @@
return a.certificate
}
+func (a *AndroidAppImport) ProvenanceMetaDataFile() android.OutputPath {
+ return a.provenanceMetaDataFile
+}
+
var dpiVariantGroupType reflect.Type
var archVariantGroupType reflect.Type
var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
@@ -457,7 +466,7 @@
// apk: "prebuilts/example_xhdpi.apk",
// },
// },
-// certificate: "PRESIGNED",
+// presigned: true,
// }
func AndroidAppImportFactory() android.Module {
module := &AndroidAppImport{}
diff --git a/java/app_import_test.go b/java/app_import_test.go
index efa52c1..8f6c75f 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -53,6 +53,11 @@
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
@@ -74,6 +79,12 @@
variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
t.Errorf("dexpreopt shouldn't have run.")
}
+
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_Presigned(t *testing.T) {
@@ -102,6 +113,12 @@
if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
t.Errorf("can't find aligning rule")
}
+
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_SigningLineage(t *testing.T) {
@@ -137,6 +154,12 @@
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
+
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
@@ -163,6 +186,12 @@
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
+
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
@@ -192,6 +221,12 @@
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
+
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_DpiVariants(t *testing.T) {
@@ -214,40 +249,46 @@
}
`
testCases := []struct {
- name string
- aaptPreferredConfig *string
- aaptPrebuiltDPI []string
- expected string
+ name string
+ aaptPreferredConfig *string
+ aaptPrebuiltDPI []string
+ expected string
+ expectedProvenanceMetaDataArtifactPath string
}{
{
- name: "no preferred",
- aaptPreferredConfig: nil,
- aaptPrebuiltDPI: []string{},
- expected: "verify_uses_libraries/apk/app.apk",
+ name: "no preferred",
+ aaptPreferredConfig: nil,
+ aaptPrebuiltDPI: []string{},
+ expected: "verify_uses_libraries/apk/app.apk",
+ expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app.apk",
},
{
- name: "AAPTPreferredConfig matches",
- aaptPreferredConfig: proptools.StringPtr("xhdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
- expected: "verify_uses_libraries/apk/app_xhdpi.apk",
+ name: "AAPTPreferredConfig matches",
+ aaptPreferredConfig: proptools.StringPtr("xhdpi"),
+ aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
+ expected: "verify_uses_libraries/apk/app_xhdpi.apk",
+ expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xhdpi.apk",
},
{
- name: "AAPTPrebuiltDPI matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
- expected: "verify_uses_libraries/apk/app_xxhdpi.apk",
+ name: "AAPTPrebuiltDPI matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
+ expected: "verify_uses_libraries/apk/app_xxhdpi.apk",
+ expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xxhdpi.apk",
},
{
- name: "non-first AAPTPrebuiltDPI matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
- expected: "verify_uses_libraries/apk/app_xhdpi.apk",
+ name: "non-first AAPTPrebuiltDPI matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
+ expected: "verify_uses_libraries/apk/app_xhdpi.apk",
+ expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xhdpi.apk",
},
{
- name: "no matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
- expected: "verify_uses_libraries/apk/app.apk",
+ name: "no matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
+ expected: "verify_uses_libraries/apk/app.apk",
+ expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app.apk",
},
}
@@ -270,6 +311,12 @@
if strings.HasSuffix(matches[1], test.expected) {
t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
}
+
+ provenanceMetaDataRule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", test.expectedProvenanceMetaDataArtifactPath, provenanceMetaDataRule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", provenanceMetaDataRule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", provenanceMetaDataRule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", provenanceMetaDataRule.Args["install_path"])
}
}
@@ -290,16 +337,25 @@
`)
testCases := []struct {
- name string
- expected string
+ name string
+ expected string
+ onDevice string
+ expectedArtifactPath string
+ expectedMetaDataPath string
}{
{
- name: "foo",
- expected: "foo.apk",
+ name: "foo",
+ expected: "foo.apk",
+ onDevice: "/system/app/foo/foo.apk",
+ expectedArtifactPath: "prebuilts/apk/app.apk",
+ expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto",
},
{
- name: "bar",
- expected: "bar_sample.apk",
+ name: "bar",
+ expected: "bar_sample.apk",
+ onDevice: "/system/app/bar/bar_sample.apk",
+ expectedArtifactPath: "prebuilts/apk/app.apk",
+ expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/bar/provenance_metadata.textproto",
},
}
@@ -316,15 +372,23 @@
t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
actualValues, expectedValues)
}
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", test.expectedArtifactPath, rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", test.expectedMetaDataPath, rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", test.name, rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", test.onDevice, rule.Args["install_path"])
}
}
func TestAndroidAppImport_ArchVariants(t *testing.T) {
// The test config's target arch is ARM64.
testCases := []struct {
- name string
- bp string
- expected string
+ name string
+ bp string
+ expected string
+ artifactPath string
+ metaDataPath string
+ installPath string
}{
{
name: "matching arch",
@@ -343,7 +407,9 @@
},
}
`,
- expected: "verify_uses_libraries/apk/app_arm64.apk",
+ expected: "verify_uses_libraries/apk/app_arm64.apk",
+ artifactPath: "prebuilts/apk/app_arm64.apk",
+ installPath: "/system/app/foo/foo.apk",
},
{
name: "no matching arch",
@@ -362,7 +428,9 @@
},
}
`,
- expected: "verify_uses_libraries/apk/app.apk",
+ expected: "verify_uses_libraries/apk/app.apk",
+ artifactPath: "prebuilts/apk/app.apk",
+ installPath: "/system/app/foo/foo.apk",
},
{
name: "no matching arch without default",
@@ -380,7 +448,9 @@
},
}
`,
- expected: "",
+ expected: "",
+ artifactPath: "prebuilts/apk/app_arm.apk",
+ installPath: "/system/app/foo/foo.apk",
},
}
@@ -393,6 +463,8 @@
if variant.Module().Enabled() {
t.Error("module should have been disabled, but wasn't")
}
+ rule := variant.MaybeRule("genProvenanceMetaData")
+ android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
continue
}
jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
@@ -403,6 +475,11 @@
if strings.HasSuffix(matches[1], test.expected) {
t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
}
+ rule := variant.Rule("genProvenanceMetaData")
+ android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
+ android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+ android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+ android.AssertStringEquals(t, "Invalid args", test.installPath, rule.Args["install_path"])
}
}
diff --git a/java/app_test.go b/java/app_test.go
index 2322ef4..c4ac4df 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -27,7 +27,6 @@
"android/soong/android"
"android/soong/cc"
"android/soong/dexpreopt"
- "android/soong/genrule"
)
// testApp runs tests using the prepareForJavaTest
@@ -428,7 +427,8 @@
name: "libjni",
stl: "none",
system_shared_libs: [],
- sdk_version: "29",
+ sdk_version: "current",
+ min_sdk_version: "29",
}
`
fs := map[string][]byte{
@@ -482,12 +482,13 @@
name: "libjni",
stl: "none",
sdk_version: "current",
+ min_sdk_version: "current",
}
`
- testJavaError(t, `"libjni" .*: sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp)
+ testJavaError(t, `"libjni" .*: min_sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp)
}
-func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) {
+func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) {
bp := cc.GatherRequiredDepsForTest(android.Android) + `
android_app {
name: "foo",
@@ -504,6 +505,7 @@
shared_libs: ["libbar"],
system_shared_libs: [],
sdk_version: "27",
+ min_sdk_version: "27",
}
cc_library {
@@ -511,6 +513,7 @@
stl: "none",
system_shared_libs: [],
sdk_version: "current",
+ min_sdk_version: "current",
}
`
testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp)
@@ -1963,7 +1966,7 @@
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidApp)
- android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.appProperties.Overrides)
+ android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides)
// Test Overridable property: Logging_parent
logging_parent := mod.aapt.LoggingParent
@@ -1981,6 +1984,99 @@
}
}
+func TestOverrideAndroidAppOverrides(t *testing.T) {
+ ctx, _ := testJava(
+ t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ overrides: ["qux"]
+ }
+
+ android_app {
+ name: "bar",
+ srcs: ["b.java"],
+ sdk_version: "current",
+ overrides: ["foo"]
+ }
+
+ override_android_app {
+ name: "foo_override",
+ base: "foo",
+ overrides: ["bar"]
+ }
+ `)
+
+ expectedVariants := []struct {
+ name string
+ moduleName string
+ variantName string
+ overrides []string
+ }{
+ {
+ name: "foo",
+ moduleName: "foo",
+ variantName: "android_common",
+ overrides: []string{"qux"},
+ },
+ {
+ name: "bar",
+ moduleName: "bar",
+ variantName: "android_common",
+ overrides: []string{"foo"},
+ },
+ {
+ name: "foo",
+ moduleName: "foo_override",
+ variantName: "android_common_foo_override",
+ overrides: []string{"bar", "foo"},
+ },
+ }
+ for _, expected := range expectedVariants {
+ variant := ctx.ModuleForTests(expected.name, expected.variantName)
+
+ // Check if the overrides field values are correctly aggregated.
+ mod := variant.Module().(*AndroidApp)
+ android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides)
+ }
+}
+
+func TestOverrideAndroidAppWithPrebuilt(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+ t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ }
+
+ override_android_app {
+ name: "bar",
+ base: "foo",
+ }
+
+ android_app_import {
+ name: "bar",
+ prefer: true,
+ apk: "bar.apk",
+ presigned: true,
+ }
+ `)
+
+ // An app that has an override that also has a prebuilt should not be hidden.
+ foo := result.ModuleForTests("foo", "android_common")
+ if foo.Module().IsHideFromMake() {
+ t.Errorf("expected foo to have HideFromMake false")
+ }
+
+ // An override that also has a prebuilt should be hidden.
+ barOverride := result.ModuleForTests("foo", "android_common_bar")
+ if !barOverride.Module().IsHideFromMake() {
+ t.Errorf("expected bar override variant of foo to have HideFromMake true")
+ }
+}
+
func TestOverrideAndroidAppStem(t *testing.T) {
ctx, _ := testJava(t, `
android_app {
@@ -2161,9 +2257,9 @@
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidTest)
- if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
+ if !reflect.DeepEqual(expected.overrides, mod.overridableAppProperties.Overrides) {
t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
- expected.overrides, mod.appProperties.Overrides)
+ expected.overrides, mod.overridableAppProperties.Overrides)
}
// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
@@ -2506,13 +2602,21 @@
prebuilt := result.ModuleForTests("prebuilt", "android_common")
// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
- // This should not include explicit `uses_libs`/`optional_uses_libs` entries.
+ // These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be
+ // propagated from dependencies.
actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
expectManifestFixerArgs := `--extract-native-libs=true ` +
`--uses-library qux ` +
`--uses-library quuz ` +
- `--uses-library runtime-library`
- android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
+ `--uses-library foo ` +
+ `--uses-library com.non.sdk.lib ` +
+ `--uses-library runtime-library ` +
+ `--uses-library runtime-required-x ` +
+ `--uses-library runtime-required-y ` +
+ `--optional-uses-library bar ` +
+ `--optional-uses-library runtime-optional-x ` +
+ `--optional-uses-library runtime-optional-y`
+ android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs)
// Test that all libraries are verified (library order matters).
verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
@@ -2722,116 +2826,6 @@
}
}
-func TestEmbedNotice(t *testing.T) {
- result := android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModules,
- cc.PrepareForTestWithCcDefaultModules,
- genrule.PrepareForTestWithGenRuleBuildComponents,
- android.MockFS{
- "APP_NOTICE": nil,
- "GENRULE_NOTICE": nil,
- "LIB_NOTICE": nil,
- "TOOL_NOTICE": nil,
- }.AddToFixture(),
- ).RunTestWithBp(t, `
- android_app {
- name: "foo",
- srcs: ["a.java"],
- static_libs: ["javalib"],
- jni_libs: ["libjni"],
- notice: "APP_NOTICE",
- embed_notices: true,
- sdk_version: "current",
- }
-
- // No embed_notice flag
- android_app {
- name: "bar",
- srcs: ["a.java"],
- jni_libs: ["libjni"],
- notice: "APP_NOTICE",
- sdk_version: "current",
- }
-
- // No NOTICE files
- android_app {
- name: "baz",
- srcs: ["a.java"],
- embed_notices: true,
- sdk_version: "current",
- }
-
- cc_library {
- name: "libjni",
- system_shared_libs: [],
- stl: "none",
- notice: "LIB_NOTICE",
- sdk_version: "current",
- }
-
- java_library {
- name: "javalib",
- srcs: [
- ":gen",
- ],
- sdk_version: "current",
- }
-
- genrule {
- name: "gen",
- tools: ["gentool"],
- out: ["gen.java"],
- notice: "GENRULE_NOTICE",
- }
-
- java_binary_host {
- name: "gentool",
- srcs: ["b.java"],
- notice: "TOOL_NOTICE",
- }
- `)
-
- // foo has NOTICE files to process, and embed_notices is true.
- foo := result.ModuleForTests("foo", "android_common")
- // verify merge notices rule.
- mergeNotices := foo.Rule("mergeNoticesRule")
- noticeInputs := mergeNotices.Inputs.Strings()
- // TOOL_NOTICE should be excluded as it's a host module.
- if len(mergeNotices.Inputs) != 3 {
- t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
- }
- if !inList("APP_NOTICE", noticeInputs) {
- t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
- }
- if !inList("LIB_NOTICE", noticeInputs) {
- t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
- }
- if !inList("GENRULE_NOTICE", noticeInputs) {
- t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
- }
- // aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
- res := foo.Output("package-res.apk")
- aapt2Flags := res.Args["flags"]
- e := "-A out/soong/.intermediates/foo/android_common/NOTICE"
- android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e)
-
- // bar has NOTICE files to process, but embed_notices is not set.
- bar := result.ModuleForTests("bar", "android_common")
- res = bar.Output("package-res.apk")
- aapt2Flags = res.Args["flags"]
- e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
- android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
-
- // baz's embed_notice is true, but it doesn't have any NOTICE files.
- baz := result.ModuleForTests("baz", "android_common")
- res = baz.Output("package-res.apk")
- aapt2Flags = res.Args["flags"]
- e = "-A out/soong/.intermediates/baz/android_common/NOTICE"
- if strings.Contains(aapt2Flags, e) {
- t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
- }
-}
-
func TestUncompressDex(t *testing.T) {
testCases := []struct {
name string
@@ -3055,7 +3049,28 @@
result := fixture.RunTestWithBp(t, bp)
foo := result.ModuleForTests("foo", "android_common")
- manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args
- android.AssertStringEquals(t, testCase.name, testCase.targetSdkVersionExpected, manifestFixerArgs["targetSdkVersion"])
+ manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+ android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected)
}
}
+
+func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestWithAllowMissingDependencies,
+ android.PrepareForTestWithAndroidMk,
+ ).RunTestWithBp(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: ":missing_certificate",
+ sdk_version: "current",
+ }`)
+
+ foo := result.ModuleForTests("foo", "android_common")
+ fooApk := foo.Output("foo.apk")
+ if fooApk.Rule != android.ErrorRule {
+ t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
+ }
+ android.AssertStringDoesContain(t, "expected error rule message", fooApk.Args["error"], "missing dependencies: missing_certificate\n")
+}
diff --git a/java/base.go b/java/base.go
index a3eb8de..0900daa 100644
--- a/java/base.go
+++ b/java/base.go
@@ -170,6 +170,9 @@
}
Instrument bool `blueprint:"mutated"`
+ // If true, then the module supports statically including the jacocoagent
+ // into the library.
+ Supports_static_instrumentation bool `blueprint:"mutated"`
// List of files to include in the META-INF/services folder of the resulting jar.
Services []string `android:"path,arch_variant"`
@@ -185,12 +188,12 @@
// constructing a new module.
type DeviceProperties struct {
// If not blank, set to the version of the sdk to compile against.
- // Defaults to compiling against the current platform.
+ // Defaults to private.
// Values are of one of the following forms:
- // 1) numerical API level or "current"
- // 2) An SDK kind with an API level: "<sdk kind>_<API level>". See
- // build/soong/android/sdk_version.go for the complete and up to date list of
- // SDK kinds. If the SDK kind value is empty, it will be set to public.
+ // 1) numerical API level, "current", "none", or "core_platform"
+ // 2) An SDK kind with an API level: "<sdk kind>_<API level>"
+ // See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
+ // If the SDK kind is empty, it will be set to public.
Sdk_version *string
// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
@@ -207,7 +210,7 @@
// Whether to compile against the platform APIs instead of an SDK.
// If true, then sdk_version must be empty. The value of this field
- // is ignored when module's type isn't android_app.
+ // is ignored when module's type isn't android_app, android_test, or android_test_helper_app.
Platform_apis *bool
Aidl struct {
@@ -227,6 +230,12 @@
// whether to generate Binder#GetTransaction name method.
Generate_get_transaction_name *bool
+ // whether all interfaces should be annotated with required permissions.
+ Enforce_permissions *bool
+
+ // allowlist for interfaces that (temporarily) do not require annotation for permissions.
+ Enforce_permissions_exceptions []string `android:"path"`
+
// list of flags that will be passed to the AIDL compiler
Flags []string
}
@@ -418,7 +427,8 @@
outputFile android.Path
extraOutputFiles android.Paths
- exportAidlIncludeDirs android.Paths
+ exportAidlIncludeDirs android.Paths
+ ignoredAidlPermissionList android.Paths
logtagsSrcs android.Paths
@@ -435,9 +445,6 @@
// manifest file to use instead of properties.Manifest
overrideManifest android.OptionalPath
- // map of SDK version to class loader context
- classLoaderContexts dexpreopt.ClassLoaderContextMap
-
// list of plugins that this java module is exporting
exportedPluginJars android.Paths
@@ -474,6 +481,8 @@
sdkVersion android.SdkSpec
minSdkVersion android.SdkSpec
maxSdkVersion android.SdkSpec
+
+ sourceExtensions []string
}
func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -596,7 +605,8 @@
}
func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool {
- return j.shouldInstrument(ctx) &&
+ return j.properties.Supports_static_instrumentation &&
+ j.shouldInstrument(ctx) &&
(ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_STATIC") ||
ctx.Config().UnbundledBuild())
}
@@ -714,8 +724,10 @@
if component, ok := dep.(SdkLibraryComponentDependency); ok {
if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
// Add library as optional if it's one of the optional compatibility libs.
- optional := android.InList(*lib, dexpreopt.OptionalCompatUsesLibs)
- tag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, optional, true)
+ tag := usesLibReqTag
+ if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) {
+ tag = usesLibOptTag
+ }
ctx.AddVariationDependencies(nil, tag, *lib)
}
}
@@ -736,9 +748,7 @@
// Kotlin files
ctx.AddVariationDependencies(nil, kotlinStdlibTag,
"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
- if len(j.properties.Plugins) > 0 {
- ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
- }
+ ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
}
// Framework libraries need special handling in static coverage builds: they should not have
@@ -772,6 +782,17 @@
return hasSrcExt(j.properties.Srcs, ext)
}
+func (j *Module) individualAidlFlags(ctx android.ModuleContext, aidlFile android.Path) string {
+ var flags string
+
+ if Bool(j.deviceProperties.Aidl.Enforce_permissions) {
+ if !android.InList(aidlFile.String(), j.ignoredAidlPermissionList.Strings()) {
+ flags = "-Wmissing-permission-annotation -Werror"
+ }
+ }
+ return flags
+}
+
func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
aidlIncludeDirs android.Paths) (string, android.Paths) {
@@ -814,6 +835,11 @@
flags = append(flags, "--transaction_names")
}
+ if Bool(j.deviceProperties.Aidl.Enforce_permissions) {
+ exceptions := j.deviceProperties.Aidl.Enforce_permissions_exceptions
+ j.ignoredAidlPermissionList = android.PathsForModuleSrcExcludes(ctx, exceptions, nil)
+ }
+
aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String()
flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion)
@@ -839,7 +865,7 @@
}
errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
- flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
+ flags.errorProneExtraJavacFlags = "${config.ErrorProneHeapFlags} ${config.ErrorProneFlags} " +
"'" + strings.Join(errorProneFlags, " ") + "'"
flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
}
@@ -847,6 +873,7 @@
// classpath
flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
flags.classpath = append(flags.classpath, deps.classpath...)
+ flags.dexClasspath = append(flags.dexClasspath, deps.dexClasspath...)
flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...)
flags.processorPath = append(flags.processorPath, deps.processorPath...)
flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...)
@@ -959,6 +986,14 @@
return flags
}
+func (j *Module) AddJSONData(d *map[string]interface{}) {
+ (&j.ModuleBase).AddJSONData(d)
+ (*d)["Java"] = map[string]interface{}{
+ "SourceExtensions": j.sourceExtensions,
+ }
+
+}
+
func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
@@ -970,6 +1005,12 @@
}
srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+ j.sourceExtensions = []string{}
+ for _, ext := range []string{".kt", ".proto", ".aidl", ".java", ".logtags"} {
+ if hasSrcExt(srcFiles.Strings(), ext) {
+ j.sourceExtensions = append(j.sourceExtensions, ext)
+ }
+ }
if hasSrcExt(srcFiles.Strings(), ".proto") {
flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
}
@@ -979,6 +1020,7 @@
ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
}
+ nonGeneratedSrcJars := srcFiles.FilterByExt(".srcjar")
srcFiles = j.genSources(ctx, srcFiles, flags)
// Collect javac flags only after computing the full set of srcFiles to
@@ -1008,12 +1050,24 @@
}
}
+ // We don't currently run annotation processors in turbine, which means we can't use turbine
+ // generated header jars when an annotation processor that generates API is enabled. One
+ // exception (handled further below) is when kotlin sources are enabled, in which case turbine
+ // is used to run all of the annotation processors.
+ disableTurbine := deps.disableTurbine
+
// Collect .java files for AIDEGen
j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
var kotlinJars android.Paths
+ var kotlinHeaderJars android.Paths
if srcFiles.HasExt(".kt") {
+ // When using kotlin sources turbine is used to generate annotation processor sources,
+ // including for annotation processors that generate API, so we can use turbine for
+ // java sources too.
+ disableTurbine = false
+
// user defined kotlin flags.
kotlincFlags := j.properties.Kotlincflags
CheckKotlincFlags(ctx, kotlincFlags)
@@ -1067,18 +1121,27 @@
}
kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
- kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+ kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
+ kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
if ctx.Failed() {
return
}
// Make javac rule depend on the kotlinc rule
- flags.classpath = append(flags.classpath, kotlinJar)
+ flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...)
kotlinJars = append(kotlinJars, kotlinJar)
+ kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar)
+
// Jar kotlin classes into the final jar after javac
if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
+ kotlinJars = append(kotlinJars, deps.kotlinAnnotations...)
+ kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinStdlib...)
+ kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinAnnotations...)
+ } else {
+ flags.dexClasspath = append(flags.dexClasspath, deps.kotlinStdlib...)
+ flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...)
}
}
@@ -1090,7 +1153,7 @@
enableSharding := false
var headerJarFileWithoutDepsOrJarjar android.Path
- if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
+ if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !disableTurbine {
if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
enableSharding = true
// Formerly, there was a check here that prevented annotation processors
@@ -1100,7 +1163,7 @@
// with sharding enabled. See: b/77284273.
}
headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
- j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars)
+ j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
if ctx.Failed() {
return
}
@@ -1421,22 +1484,22 @@
}
if ctx.Device() {
- lintSDKVersionString := func(sdkSpec android.SdkSpec) string {
+ lintSDKVersion := func(sdkSpec android.SdkSpec) android.ApiLevel {
if v := sdkSpec.ApiLevel; !v.IsPreview() {
- return v.String()
+ return v
} else {
- return ctx.Config().DefaultAppTargetSdk(ctx).String()
+ return ctx.Config().DefaultAppTargetSdk(ctx)
}
}
j.linter.name = ctx.ModuleName()
- j.linter.srcs = srcFiles
- j.linter.srcJars = srcJars
+ j.linter.srcs = append(srcFiles, nonGeneratedSrcJars...)
+ j.linter.srcJars, _ = android.FilterPathList(srcJars, nonGeneratedSrcJars)
j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
j.linter.classes = j.implementationJarFile
- j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx))
- j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx))
- j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx))
+ j.linter.minSdkVersion = lintSDKVersion(j.MinSdkVersion(ctx))
+ j.linter.targetSdkVersion = lintSDKVersion(j.TargetSdkVersion(ctx))
+ j.linter.compileSdkVersion = lintSDKVersion(j.SdkVersion(ctx))
j.linter.compileSdkKind = j.SdkVersion(ctx).Kind
j.linter.javaLanguageLevel = flags.javaVersion.String()
j.linter.kotlinLanguageLevel = "1.3"
@@ -1634,6 +1697,8 @@
dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
}
dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
+ dpInfo.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...)
+ dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...)
}
func (j *Module) CompilerDeps() []string {
@@ -1803,6 +1868,7 @@
} else if sdkDep.useFiles {
// sdkDep.jar is actually equivalent to turbine header.jar.
deps.classpath = append(deps.classpath, sdkDep.jars...)
+ deps.dexClasspath = append(deps.dexClasspath, sdkDep.jars...)
deps.aidlPreprocess = sdkDep.aidl
} else {
deps.aidlPreprocess = sdkDep.aidl
@@ -1827,7 +1893,9 @@
if dep, ok := module.(SdkLibraryDependency); ok {
switch tag {
case libTag:
- deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+ depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
+ deps.classpath = append(deps.classpath, depHeaderJars...)
+ deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...)
case staticLibTag:
ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
}
@@ -1845,13 +1913,20 @@
case bootClasspathTag:
deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
case libTag, instrumentationForTag:
+ if _, ok := module.(*Plugin); ok {
+ ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a libs dependency", otherName)
+ }
deps.classpath = append(deps.classpath, dep.HeaderJars...)
+ deps.dexClasspath = append(deps.dexClasspath, dep.HeaderJars...)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
case java9LibTag:
deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
case staticLibTag:
+ if _, ok := module.(*Plugin); ok {
+ ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a static_libs dependency", otherName)
+ }
deps.classpath = append(deps.classpath, dep.HeaderJars...)
deps.staticJars = append(deps.staticJars, dep.ImplementationJars...)
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars...)
@@ -1913,6 +1988,7 @@
case libTag:
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
+ deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
case staticLibTag:
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
@@ -1969,7 +2045,7 @@
func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
switch ctx.ModuleType() {
- case "java_library", "java_library_host":
+ case "java_library", "java_library_host", "java_library_static":
if lib, ok := ctx.Module().(*Library); ok {
javaLibraryBp2Build(ctx, lib)
}
@@ -1978,5 +2054,4 @@
javaBinaryHostBp2Build(ctx, binary)
}
}
-
}
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 52ce77d..f4cef7f 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -84,6 +84,9 @@
}
}
+ target := ctx.Module().Target()
+ variations = append(variations, target.Variations()...)
+
addedDep := false
if ctx.OtherModuleDependencyVariantExists(variations, name) {
ctx.AddFarVariationDependencies(variations, tag, name)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 4794180..0591012 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "io"
"path/filepath"
"reflect"
"strings"
@@ -41,6 +42,7 @@
func registerBootclasspathFragmentBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("bootclasspath_fragment", bootclasspathFragmentFactory)
+ ctx.RegisterModuleType("bootclasspath_fragment_test", testBootclasspathFragmentFactory)
ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
}
@@ -139,7 +141,7 @@
BootclasspathFragmentsDepsProperties
}
-type SourceOnlyBootclasspathProperties struct {
+type HiddenApiPackageProperties struct {
Hidden_api struct {
// Contains prefixes of a package hierarchy that is provided solely by this
// bootclasspath_fragment.
@@ -148,6 +150,14 @@
// hidden API flags. See split_packages property for more details.
Package_prefixes []string
+ // A list of individual packages that are provided solely by this
+ // bootclasspath_fragment but which cannot be listed in package_prefixes
+ // because there are sub-packages which are provided by other modules.
+ //
+ // This should only be used for legacy packages. New packages should be
+ // covered by a package prefix.
+ Single_packages []string
+
// The list of split packages provided by this bootclasspath_fragment.
//
// A split package is one that contains classes which are provided by multiple
@@ -207,12 +217,20 @@
}
}
+type SourceOnlyBootclasspathProperties struct {
+ HiddenApiPackageProperties
+ Coverage HiddenApiPackageProperties
+}
+
type BootclasspathFragmentModule struct {
android.ModuleBase
android.ApexModuleBase
android.SdkBase
ClasspathFragmentBase
+ // True if this fragment is for testing purposes.
+ testFragment bool
+
properties bootclasspathFragmentProperties
sourceOnlyProperties SourceOnlyBootclasspathProperties
@@ -259,7 +277,7 @@
android.InitApexModule(m)
android.InitSdkAwareModule(m)
initClasspathFragment(m, BOOTCLASSPATH)
- android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
android.AddLoadHook(m, func(ctx android.LoadHookContext) {
// If code coverage has been enabled for the framework then append the properties with
@@ -270,6 +288,12 @@
ctx.PropertyErrorf("coverage", "error trying to append coverage specific properties: %s", err)
return
}
+
+ err = proptools.AppendProperties(&m.sourceOnlyProperties.HiddenApiPackageProperties, &m.sourceOnlyProperties.Coverage, nil)
+ if err != nil {
+ ctx.PropertyErrorf("coverage", "error trying to append hidden api coverage specific properties: %s", err)
+ return
+ }
}
// Initialize the contents property from the image_name.
@@ -278,6 +302,12 @@
return m
}
+func testBootclasspathFragmentFactory() android.Module {
+ m := bootclasspathFragmentFactory().(*BootclasspathFragmentModule)
+ m.testFragment = true
+ return m
+}
+
// bootclasspathFragmentInitContentsFromImage will initialize the contents property from the image_name if
// necessary.
func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, m *BootclasspathFragmentModule) {
@@ -588,6 +618,19 @@
// Provide the apex content info.
b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
}
+ } else {
+ // Versioned fragments are not needed by make.
+ b.HideFromMake()
+ }
+
+ // In order for information about bootclasspath_fragment modules to be added to module-info.json
+ // it is necessary to output an entry to Make. As bootclasspath_fragment modules are part of an
+ // APEX there can be multiple variants, including the default/platform variant and only one can
+ // be output to Make but it does not really matter which variant is output. The default/platform
+ // variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks
+ // the last variant (ctx.FinalModule()).
+ if ctx.Module() != ctx.FinalModule() {
+ b.HideFromMake()
}
}
@@ -717,7 +760,8 @@
// TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of
// their own.
if output.SignaturePatternsPath == nil {
- output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, []string{"*"}, nil)
+ output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
+ ctx, output.AllFlagsPath, []string{"*"}, nil, nil)
}
// Initialize a HiddenAPIInfo structure.
@@ -781,6 +825,26 @@
return input
}
+// isTestFragment returns true if the current module is a test bootclasspath_fragment.
+func (b *BootclasspathFragmentModule) isTestFragment() bool {
+ if b.testFragment {
+ return true
+ }
+
+ // TODO(b/194063708): Once test fragments all use bootclasspath_fragment_test
+ // Some temporary exceptions until all test fragments use the
+ // bootclasspath_fragment_test module type.
+ name := b.BaseModuleName()
+ if strings.HasPrefix(name, "test_") {
+ return true
+ }
+ if name == "apex.apexd_test_bootclasspath-fragment" {
+ return true
+ }
+
+ return false
+}
+
// produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files)
// for the fragment as well as encoding the flags in the boot dex jars.
func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
@@ -792,11 +856,20 @@
// signature patterns.
splitPackages := b.sourceOnlyProperties.Hidden_api.Split_packages
packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes
- if splitPackages != nil || packagePrefixes != nil {
- if splitPackages == nil {
- splitPackages = []string{"*"}
- }
- output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, splitPackages, packagePrefixes)
+ singlePackages := b.sourceOnlyProperties.Hidden_api.Single_packages
+ if splitPackages != nil || packagePrefixes != nil || singlePackages != nil {
+ output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
+ ctx, output.AllFlagsPath, splitPackages, packagePrefixes, singlePackages)
+ } else if !b.isTestFragment() {
+ ctx.ModuleErrorf(`Must specify at least one of the split_packages, package_prefixes and single_packages properties
+ If this is a new bootclasspath_fragment or you are unsure what to do add the
+ the following to the bootclasspath_fragment:
+ hidden_api: {split_packages: ["*"]},
+ and then run the following:
+ m analyze_bcpf && analyze_bcpf --bcpf %q
+ it will analyze the bootclasspath_fragment and provide hints as to what you
+ should specify here. If you are happy with its suggestions then you can add
+ the --fix option and it will fix them for you.`, b.BaseModuleName())
}
return output
@@ -849,7 +922,22 @@
}
func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
- var entriesList []android.AndroidMkEntries
+ // Use the generated classpath proto as the output.
+ outputFile := b.outputFilepath
+ // Create a fake entry that will cause this to be added to the module-info.json file.
+ entriesList := []android.AndroidMkEntries{{
+ Class: "FAKE",
+ OutputFile: android.OptionalPathForPath(outputFile),
+ Include: "$(BUILD_PHONY_PACKAGE)",
+ ExtraFooters: []android.AndroidMkExtraFootersFunc{
+ func(w io.Writer, name, prefix, moduleDir string) {
+ // Allow the bootclasspath_fragment to be built by simply passing its name on the command
+ // line.
+ fmt.Fprintln(w, ".PHONY:", b.Name())
+ fmt.Fprintln(w, b.Name()+":", outputFile.String())
+ },
+ },
+ }}
for _, install := range b.bootImageDeviceInstalls {
entriesList = append(entriesList, install.ToMakeEntries())
}
@@ -931,13 +1019,13 @@
All_flags_path android.OptionalPath `supported_build_releases:"S"`
// The path to the generated signature-patterns.csv file.
- Signature_patterns_path android.OptionalPath `supported_build_releases:"T+"`
+ Signature_patterns_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
// The path to the generated filtered-stub-flags.csv file.
- Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"T+"`
+ Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
// The path to the generated filtered-flags.csv file.
- Filtered_flags_path android.OptionalPath `supported_build_releases:"T+"`
+ Filtered_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
}
func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -1073,7 +1161,7 @@
// At the moment this is basically just a bootclasspath_fragment module that can be used as a
// prebuilt. Eventually as more functionality is migrated into the bootclasspath_fragment module
// type from the various singletons then this will diverge.
-type prebuiltBootclasspathFragmentModule struct {
+type PrebuiltBootclasspathFragmentModule struct {
BootclasspathFragmentModule
prebuilt android.Prebuilt
@@ -1081,16 +1169,16 @@
prebuiltProperties prebuiltBootclasspathFragmentProperties
}
-func (module *prebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt {
+func (module *PrebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt {
return &module.prebuilt
}
-func (module *prebuiltBootclasspathFragmentModule) Name() string {
+func (module *PrebuiltBootclasspathFragmentModule) Name() string {
return module.prebuilt.Name(module.ModuleBase.Name())
}
// produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
+func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
pathForOptionalSrc := func(src *string, defaultPath android.Path) android.Path {
if src == nil {
return defaultPath
@@ -1131,7 +1219,7 @@
}
// produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
+func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
return nil
}
@@ -1141,37 +1229,53 @@
return nil // An error has been reported by FindDeapexerProviderForModule.
}
- files := bootImageFilesByArch{}
- for _, variant := range imageConfig.apexVariants() {
- arch := variant.target.Arch.ArchType
- for _, toPath := range variant.imagesDeps {
- apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
- // Get the path to the file that the deapexer extracted from the prebuilt apex file.
- fromPath := di.PrebuiltExportPath(apexRelativePath)
-
- // Return the toPath as the calling code expects the paths in the returned map to be the
- // paths predefined in the bootImageConfig.
- files[arch] = append(files[arch], toPath)
-
- // Copy the file to the predefined location.
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: fromPath,
- Output: toPath,
- })
- }
+ profile := (android.WritablePath)(nil)
+ if imageConfig.profileInstallPathInApex != "" {
+ profile = di.PrebuiltExportPath(imageConfig.profileInstallPathInApex)
}
- // Build the boot image files for the host variants. These are built from the dex files provided
- // by the contents of this module as prebuilt versions of the host boot image files are not
- // available, i.e. there is no host specific prebuilt apex containing them. This has to be built
- // without a profile as the prebuilt modules do not provide a profile.
- buildBootImageVariantsForBuildOs(ctx, imageConfig, nil)
+ // Build the boot image files for the host variants. These are always built from the dex files
+ // provided by the contents of this module as prebuilt versions of the host boot image files are
+ // not available, i.e. there is no host specific prebuilt apex containing them. This has to be
+ // built without a profile as the prebuilt modules do not provide a profile.
+ buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
- return files
+ if imageConfig.shouldInstallInApex() {
+ // If the boot image files for the android variants are in the prebuilt apex, we must use those
+ // rather than building new ones because those boot image files are going to be used on device.
+ files := bootImageFilesByArch{}
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, toPath := range variant.imagesDeps {
+ apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
+ // Get the path to the file that the deapexer extracted from the prebuilt apex file.
+ fromPath := di.PrebuiltExportPath(apexRelativePath)
+
+ // Return the toPath as the calling code expects the paths in the returned map to be the
+ // paths predefined in the bootImageConfig.
+ files[arch] = append(files[arch], toPath)
+
+ // Copy the file to the predefined location.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: fromPath,
+ Output: toPath,
+ })
+ }
+ }
+ return files
+ } else {
+ if profile == nil {
+ ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
+ return nil
+ }
+ // Build boot image files for the android variants from the dex files provided by the contents
+ // of this module.
+ return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+ }
}
-var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
+var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil)
// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
// files that a module requires from the prebuilt .apex file.
@@ -1185,16 +1289,22 @@
//
// If there is no image config associated with this fragment then it returns nil. Otherwise, it
// returns the files that are listed in the image config.
-func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
imageConfig := module.getImageConfig(ctx)
if imageConfig != nil {
- // Add the boot image files, e.g. .art, .oat and .vdex files.
files := []string{}
- for _, variant := range imageConfig.apexVariants() {
- arch := variant.target.Arch.ArchType
- for _, path := range variant.imagesDeps.Paths() {
- base := path.Base()
- files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+ if imageConfig.profileInstallPathInApex != "" {
+ // Add the boot image profile.
+ files = append(files, imageConfig.profileInstallPathInApex)
+ }
+ if imageConfig.shouldInstallInApex() {
+ // Add the boot image files, e.g. .art, .oat and .vdex files.
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, path := range variant.imagesDeps.Paths() {
+ base := path.Base()
+ files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+ }
}
}
return files
@@ -1206,10 +1316,10 @@
return filepath.Join("javalib", arch.String(), base)
}
-var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil)
func prebuiltBootclasspathFragmentFactory() android.Module {
- m := &prebuiltBootclasspathFragmentModule{}
+ m := &PrebuiltBootclasspathFragmentModule{}
m.AddProperties(&m.properties, &m.prebuiltProperties)
// This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs
// array.
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index d3de675..83beb6d 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -121,6 +121,9 @@
],
},
},
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -201,6 +204,9 @@
core_platform_api: {
stub_libs: ["mycoreplatform.stubs"],
},
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -278,3 +284,64 @@
android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
}
+
+func TestBootclasspathFragment_Test(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootclasspathFragment,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("mysdklibrary"),
+ ).RunTestWithBp(t, `
+ bootclasspath_fragment {
+ name: "myfragment",
+ contents: ["mysdklibrary"],
+ hidden_api: {
+ split_packages: [],
+ },
+ }
+
+ bootclasspath_fragment {
+ name: "test_fragment",
+ contents: ["mysdklibrary"],
+ hidden_api: {
+ split_packages: [],
+ },
+ }
+
+ bootclasspath_fragment {
+ name: "apex.apexd_test_bootclasspath-fragment",
+ contents: ["mysdklibrary"],
+ hidden_api: {
+ split_packages: [],
+ },
+ }
+
+ bootclasspath_fragment_test {
+ name: "a_test_fragment",
+ contents: ["mysdklibrary"],
+ hidden_api: {
+ split_packages: [],
+ },
+ }
+
+
+ java_sdk_library {
+ name: "mysdklibrary",
+ srcs: ["a.java"],
+ shared_library: false,
+ public: {enabled: true},
+ system: {enabled: true},
+ }
+ `)
+
+ fragment := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
+ android.AssertBoolEquals(t, "not a test fragment", false, fragment.isTestFragment())
+
+ fragment = result.Module("test_fragment", "android_common").(*BootclasspathFragmentModule)
+ android.AssertBoolEquals(t, "is a test fragment by prefix", true, fragment.isTestFragment())
+
+ fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
+ android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
+
+ fragment = result.Module("apex.apexd_test_bootclasspath-fragment", "android_common").(*BootclasspathFragmentModule)
+ android.AssertBoolEquals(t, "is a test fragment by name", true, fragment.isTestFragment())
+}
diff --git a/java/builder.go b/java/builder.go
index e64a61f..c0fadd4 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -131,31 +131,28 @@
turbine, turbineRE = pctx.RemoteStaticRules("turbine",
blueprint.RuleParams{
- Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
- `--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` +
+ Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` +
+ `--sources @$out.rsp --source_jars $srcJars ` +
`--javacopts ${config.CommonJdkFlags} ` +
- `$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
- `${config.Ziptime} $out.tmp && ` +
- `(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`,
+ `$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` +
+ `(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`,
CommandDeps: []string{
"${config.TurbineJar}",
"${config.JavaCmd}",
- "${config.Ziptime}",
},
Rspfile: "$out.rsp",
RspfileContent: "$in",
Restat: true,
},
&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
- ExecStrategy: "${config.RETurbineExecStrategy}",
- Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
- RSPFiles: []string{"${out}.rsp"},
- OutputFiles: []string{"$out.tmp"},
- OutputDirectories: []string{"$outDir"},
- ToolchainInputs: []string{"${config.JavaCmd}"},
- Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
- }, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"})
+ ExecStrategy: "${config.RETurbineExecStrategy}",
+ Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
+ RSPFiles: []string{"${out}.rsp"},
+ OutputFiles: []string{"$rbeOutputs"},
+ ToolchainInputs: []string{"${config.JavaCmd}"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ },
+ []string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs", "srcJars"}, []string{"implicits"})
jar, jarRE = pctx.RemoteStaticRules("jar",
blueprint.RuleParams{
@@ -247,16 +244,33 @@
}
type javaBuilderFlags struct {
- javacFlags string
- bootClasspath classpath
- classpath classpath
+ javacFlags string
+
+ // bootClasspath is the list of jars that form the boot classpath (generally the java.* and
+ // android.* classes) for tools that still use it. javac targeting 1.9 or higher uses
+ // systemModules and java9Classpath instead.
+ bootClasspath classpath
+
+ // classpath is the list of jars that form the classpath for javac and kotlinc rules. It
+ // contains header jars for all static and non-static dependencies.
+ classpath classpath
+
+ // dexClasspath is the list of jars that form the classpath for d8 and r8 rules. It contains
+ // header jars for all non-static dependencies. Static dependencies have already been
+ // combined into the program jar.
+ dexClasspath classpath
+
+ // java9Classpath is the list of jars that will be added to the classpath when targeting
+ // 1.9 or higher. It generally contains the android.* classes, while the java.* classes
+ // are provided by systemModules.
java9Classpath classpath
- processorPath classpath
- processors []string
- systemModules *systemModules
- aidlFlags string
- aidlDeps android.Paths
- javaVersion javaVersion
+
+ processorPath classpath
+ processors []string
+ systemModules *systemModules
+ aidlFlags string
+ aidlDeps android.Paths
+ javaVersion javaVersion
errorProneExtraJavacFlags string
errorProneProcessorPath classpath
@@ -341,11 +355,8 @@
})
}
-func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
- srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
-
+func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags) (string, android.Paths) {
var deps android.Paths
- deps = append(deps, srcJars...)
classpath := flags.classpath
@@ -367,20 +378,31 @@
}
deps = append(deps, classpath...)
- deps = append(deps, flags.processorPath...)
+ turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ")
+
+ return turbineFlags, deps
+}
+
+func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
+
+ turbineFlags, deps := turbineFlags(ctx, flags)
+
+ deps = append(deps, srcJars...)
rule := turbine
args := map[string]string{
- "javacFlags": flags.javacFlags,
- "bootClasspath": bootClasspath,
- "srcJars": strings.Join(srcJars.Strings(), " "),
- "classpath": classpath.FormTurbineClassPath("--classpath "),
- "outDir": android.PathForModuleOut(ctx, "turbine", "classes").String(),
- "javaVersion": flags.javaVersion.String(),
+ "javacFlags": flags.javacFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "javaVersion": flags.javaVersion.String(),
+ "turbineFlags": turbineFlags,
+ "outputFlags": "--output " + outputFile.String() + ".tmp",
+ "outputs": outputFile.String(),
}
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
rule = turbineRE
args["implicits"] = strings.Join(deps.Strings(), ",")
+ args["rbeOutputs"] = outputFile.String() + ".tmp"
}
ctx.Build(pctx, android.BuildParams{
Rule: rule,
@@ -392,6 +414,47 @@
})
}
+// TurbineApt produces a rule to run annotation processors using turbine.
+func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
+ srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
+
+ turbineFlags, deps := turbineFlags(ctx, flags)
+
+ deps = append(deps, srcJars...)
+
+ deps = append(deps, flags.processorPath...)
+ turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ")
+ turbineFlags += " --processors " + strings.Join(flags.processors, " ")
+
+ outputs := android.WritablePaths{outputSrcJar, outputResJar}
+ outputFlags := "--gensrc_output " + outputSrcJar.String() + ".tmp " +
+ "--resource_output " + outputResJar.String() + ".tmp"
+
+ rule := turbine
+ args := map[string]string{
+ "javacFlags": flags.javacFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "javaVersion": flags.javaVersion.String(),
+ "turbineFlags": turbineFlags,
+ "outputFlags": outputFlags,
+ "outputs": strings.Join(outputs.Strings(), " "),
+ }
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
+ rule = turbineRE
+ args["implicits"] = strings.Join(deps.Strings(), ",")
+ args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp"
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Description: "turbine apt",
+ Output: outputs[0],
+ ImplicitOutputs: outputs[1:],
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: args,
+ })
+}
+
// transformJavaToClasses takes source files and converts them to a jar containing .class files.
// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
// sources. flags contains various command line flags to be passed to the compiler.
@@ -653,6 +716,6 @@
} else if forceEmpty {
return `--bootclasspath ""`, nil
} else {
- return "", nil
+ return "--system ${config.JavaHome}", nil
}
}
diff --git a/java/config/config.go b/java/config/config.go
index ea2f934..e728b7d 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -26,7 +26,8 @@
)
var (
- pctx = android.NewPackageContext("android/soong/java/config")
+ pctx = android.NewPackageContext("android/soong/java/config")
+ exportedVars = android.NewExportedVariables(pctx)
LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
LegacyCorePlatformSystemModules = "legacy-core-platform-api-stubs-system-modules"
@@ -50,27 +51,55 @@
"core-icu4j",
"core-oj",
"core-libart",
- // TODO: Could this be all updatable bootclasspath jars?
- "updatable-media",
- "framework-mediaprovider",
- "framework-sdkextensions",
- "android.net.ipsec.ike",
}
)
-const (
- JavaVmFlags = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
- JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1`
+var (
+ JavacVmFlags = strings.Join(javacVmFlagsList, " ")
+ javaVmFlagsList = []string{
+ `-XX:OnError="cat hs_err_pid%p.log"`,
+ "-XX:CICompilerCount=6",
+ "-XX:+UseDynamicNumberOfGCThreads",
+ }
+ javacVmFlagsList = []string{
+ `-J-XX:OnError="cat hs_err_pid%p.log"`,
+ "-J-XX:CICompilerCount=6",
+ "-J-XX:+UseDynamicNumberOfGCThreads",
+ "-J-XX:+TieredCompilation",
+ "-J-XX:TieredStopAtLevel=1",
+ }
+ dexerJavaVmFlagsList = []string{
+ `-JXX:OnError="cat hs_err_pid%p.log"`,
+ "-JXX:CICompilerCount=6",
+ "-JXX:+UseDynamicNumberOfGCThreads",
+ }
)
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
- pctx.StaticVariable("JavacHeapSize", "2048M")
- pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
- pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
+ exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M")
+ exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
- pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
+ // ErrorProne can use significantly more memory than javac alone, give it a higher heap
+ // size (b/221480398).
+ exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M")
+ exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
+
+ // D8 invocations are shorter lived, so we restrict their JIT tiering relative to R8.
+ // Note that the `-JXX` prefix syntax is specific to the R8/D8 invocation wrappers.
+ exportedVars.ExportStringListStaticVariable("D8Flags", append([]string{
+ "-JXmx2048M",
+ "-JXX:+TieredCompilation",
+ "-JXX:TieredStopAtLevel=1",
+ }, dexerJavaVmFlagsList...))
+ exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
+ "-JXmx2048M",
+ // Disable this optimization as it can impact weak reference semantics. See b/233432839.
+ "-JDcom.android.tools.r8.disableEnqueuerDeferredTracing=true",
+ }, dexerJavaVmFlagsList...))
+
+ exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
`-Xmaxerrs 9999999`,
`-encoding UTF-8`,
`-sourcepath ""`,
@@ -84,10 +113,10 @@
// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
`-XDstringConcat=inline`,
- }, " "))
+ })
- pctx.StaticVariable("JavaVmFlags", JavaVmFlags)
- pctx.StaticVariable("JavacVmFlags", JavacVmFlags)
+ exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList)
+ exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList)
pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
@@ -99,7 +128,12 @@
if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
return override
}
- return "11"
+ switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
+ case "true":
+ return "17"
+ default:
+ return "11"
+ }
})
pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
@@ -178,6 +212,10 @@
hostJNIToolVariableWithSdkToolsPrebuilt("SignapkJniLibrary", "libconscrypt_openjdk_jni")
}
+func BazelJavaToolchainVars(config android.Config) string {
+ return android.BazelToolchainVars(config, exportedVars)
+}
+
func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) {
pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
if ctx.Config().AlwaysUsePrebuiltSdks() {
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index 48681b5..5f853c8 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -16,8 +16,6 @@
import (
"strings"
-
- "android/soong/android"
)
var (
@@ -31,23 +29,23 @@
)
// Wrapper that grabs value of val late so it can be initialized by a later module's init function
-func errorProneVar(name string, val *[]string, sep string) {
- pctx.VariableFunc(name, func(android.PackageVarContext) string {
+func errorProneVar(val *[]string, sep string) func() string {
+ return func() string {
return strings.Join(*val, sep)
- })
+ }
}
func init() {
- errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":")
- errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ")
- errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ")
- errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ")
- errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ")
- errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ")
- pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{
+ exportedVars.ExportVariableFuncVariable("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":"))
+ exportedVars.ExportVariableFuncVariable("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " "))
+ exportedVars.ExportVariableFuncVariable("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " "))
+ exportedVars.ExportVariableFuncVariable("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " "))
+ exportedVars.ExportVariableFuncVariable("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " "))
+ exportedVars.ExportVariableFuncVariable("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " "))
+ exportedVars.ExportStringListStaticVariable("ErrorProneChecks", []string{
"${ErrorProneChecksOff}",
"${ErrorProneChecksError}",
"${ErrorProneChecksWarning}",
"${ErrorProneChecksDefaultDisabled}",
- }, " "))
+ })
}
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 6cb61f3..fc63f4d 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -34,6 +34,7 @@
pctx.SourcePathVariable("KotlinKaptJar", "external/kotlinc/lib/kotlin-annotation-processing.jar")
pctx.SourcePathVariable("KotlinAnnotationJar", "external/kotlinc/lib/annotations-13.0.jar")
pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
+ pctx.SourcePathVariable("KotlinAbiGenPluginJar", "external/kotlinc/lib/jvm-abi-gen.jar")
// These flags silence "Illegal reflective access" warnings when running kapt in OpenJDK9+
pctx.StaticVariable("KaptSuppressJDK9Warnings", strings.Join([]string{
@@ -47,4 +48,9 @@
pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{
"-J--add-opens=java.base/java.util=ALL-UNNAMED", // https://youtrack.jetbrains.com/issue/KT-43704
}, " "))
+
+ pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{
+ // b/222162908: prevent kotlinc from reading /tmp/build.txt
+ "-Didea.plugins.compatible.build=999.SNAPSHOT",
+ }, " "))
}
diff --git a/java/config/makevars.go b/java/config/makevars.go
index df447a1..273aca0 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -43,9 +43,10 @@
ctx.Strict("JAVADOC", "${JavadocCmd}")
ctx.Strict("COMMON_JDK_FLAGS", "${CommonJdkFlags}")
- ctx.Strict("DX", "${D8Cmd}")
- ctx.Strict("DX_COMMAND", "${D8Cmd} -JXms16M -JXmx2048M")
- ctx.Strict("R8_COMPAT_PROGUARD", "${R8Cmd}")
+ ctx.Strict("D8", "${D8Cmd}")
+ ctx.Strict("R8", "${R8Cmd}")
+ ctx.Strict("D8_COMMAND", "${D8Cmd} ${D8Flags}")
+ ctx.Strict("R8_COMMAND", "${R8Cmd} ${R8Flags}")
ctx.Strict("TURBINE", "${TurbineJar}")
@@ -78,8 +79,6 @@
ctx.Strict("CLASS2NONSDKLIST", "${Class2NonSdkList}")
ctx.Strict("HIDDENAPI", "${HiddenAPI}")
- ctx.Strict("DEX_FLAGS", "${DexFlags}")
-
ctx.Strict("AIDL", "${AidlCmd}")
ctx.Strict("AAPT2", "${Aapt2Cmd}")
ctx.Strict("ZIPALIGN", "${ZipAlign}")
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index cf39746..513c606 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -138,11 +138,29 @@
},
}
+// Same as core-module-lib-stubs-for-system-modules, but android annotations are
+// stripped. This is used by the Java toolchain, while the annotated stub is to
+// be used by Kotlin one.
+java_library {
+ name: "core-module-lib-stubs-for-system-modules-no-annotations",
+ visibility: ["//visibility:private"],
+ static_libs: [
+ "core-module-lib-stubs-for-system-modules",
+ ],
+ sdk_version: "none",
+ system_modules: "none",
+ dist: {
+ dest: "system-modules/module-lib/core-for-system-modules-no-annotations.jar",
+ targets: dist_targets,
+ },
+ jarjar_rules: "jarjar-strip-annotations-rules.txt",
+}
+
// Used when compiling higher-level code with sdk_version "module_current"
java_system_modules {
name: "core-module-lib-stubs-system-modules",
libs: [
- "core-module-lib-stubs-for-system-modules",
+ "core-module-lib-stubs-for-system-modules-no-annotations",
],
visibility: ["//visibility:public"],
}
@@ -174,6 +192,24 @@
patch_module: "java.base",
}
+// Same as legacy.core.platform.api.stubs, but android annotations are
+// stripped. This is used by the Java toolchain, while the annotated stub is to
+// be used by Kotlin one.
+java_library {
+ name: "legacy.core.platform.api.no.annotations.stubs",
+ visibility: core_platform_visibility,
+ hostdex: true,
+ compile_dex: true,
+
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: [
+ "legacy.core.platform.api.stubs",
+ ],
+ patch_module: "java.base",
+ jarjar_rules: "jarjar-strip-annotations-rules.txt",
+}
+
java_library {
name: "stable.core.platform.api.stubs",
visibility: core_platform_visibility,
@@ -191,12 +227,30 @@
patch_module: "java.base",
}
+// Same as stable.core.platform.api.stubs, but android annotations are
+// stripped. This is used by the Java toolchain, while the annotated stub is to
+// be used by Kotlin one.
+java_library {
+ name: "stable.core.platform.api.no.annotations.stubs",
+ visibility: core_platform_visibility,
+ hostdex: true,
+ compile_dex: true,
+
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: [
+ "stable.core.platform.api.stubs",
+ ],
+ patch_module: "java.base",
+ jarjar_rules: "jarjar-strip-annotations-rules.txt",
+}
+
// Used when compiling higher-level code against *.core.platform.api.stubs.
java_system_modules {
name: "legacy-core-platform-api-stubs-system-modules",
visibility: core_platform_visibility,
libs: [
- "legacy.core.platform.api.stubs",
+ "legacy.core.platform.api.no.annotations.stubs",
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
@@ -212,7 +266,7 @@
name: "stable-core-platform-api-stubs-system-modules",
visibility: core_platform_visibility,
libs: [
- "stable.core.platform.api.stubs",
+ "stable.core.platform.api.no.annotations.stubs",
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
diff --git a/java/core-libraries/jarjar-strip-annotations-rules.txt b/java/core-libraries/jarjar-strip-annotations-rules.txt
new file mode 100644
index 0000000..a1c261b
--- /dev/null
+++ b/java/core-libraries/jarjar-strip-annotations-rules.txt
@@ -0,0 +1,4 @@
+strip-annotation android.annotation.NotNull
+strip-annotation android.annotation.Nullable
+strip-annotation androidx.annotation.RecentlyNonNull
+strip-annotation androidx.annotation.RecentlyNullable
diff --git a/java/dex.go b/java/dex.go
index 474694a..c943938 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -36,8 +36,8 @@
Main_dex_rules []string `android:"path"`
Optimize struct {
- // If false, disable all optimization. Defaults to true for android_app and android_test
- // modules, false for java_library and java_test modules.
+ // If false, disable all optimization. Defaults to true for android_app and
+ // android_test_helper_app modules, false for android_test, java_library, and java_test modules.
Enabled *bool
// True if the module containing this has it set by default.
EnabledByDefault bool `blueprint:"mutated"`
@@ -95,7 +95,7 @@
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
`mkdir -p $$(dirname $tmpJar) && ` +
`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
- `$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` +
+ `$d8Template${config.D8Cmd} ${config.D8Flags} --output $outDir $d8Flags $tmpJar && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
@@ -128,16 +128,19 @@
`mkdir -p $$(dirname ${outUsage}) && ` +
`mkdir -p $$(dirname $tmpJar) && ` +
`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
- `$r8Template${config.R8Cmd} ${config.DexFlags} -injars $tmpJar --output $outDir ` +
+ `$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
`--no-data-resources ` +
`-printmapping ${outDict} ` +
`-printusage ${outUsage} ` +
+ `--deps-file ${out}.d ` +
`$r8Flags && ` +
`touch "${outDict}" "${outUsage}" && ` +
`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
`rm -rf ${outUsageDir} && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
CommandDeps: []string{
"${config.R8Cmd}",
"${config.Zip2ZipCmd}",
@@ -205,10 +208,10 @@
func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
- d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
+ d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...)
d8Deps = append(d8Deps, flags.bootClasspath...)
- d8Deps = append(d8Deps, flags.classpath...)
+ d8Deps = append(d8Deps, flags.dexClasspath...)
return d8Flags, d8Deps
}
@@ -231,11 +234,11 @@
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
- r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
+ r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, proguardRaiseDeps...)
r8Deps = append(r8Deps, flags.bootClasspath...)
- r8Deps = append(r8Deps, flags.classpath...)
+ r8Deps = append(r8Deps, flags.dexClasspath...)
flagFiles := android.Paths{
android.PathForSource(ctx, "build/make/core/proguard.flags"),
diff --git a/java/dex_test.go b/java/dex_test.go
new file mode 100644
index 0000000..fbdccb6
--- /dev/null
+++ b/java/dex_test.go
@@ -0,0 +1,103 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestR8(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ android_app {
+ name: "app",
+ srcs: ["foo.java"],
+ libs: ["lib"],
+ static_libs: ["static_lib"],
+ platform_apis: true,
+ }
+
+ java_library {
+ name: "lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "static_lib",
+ srcs: ["foo.java"],
+ }
+ `)
+
+ app := result.ModuleForTests("app", "android_common")
+ lib := result.ModuleForTests("lib", "android_common")
+ staticLib := result.ModuleForTests("static_lib", "android_common")
+
+ appJavac := app.Rule("javac")
+ appR8 := app.Rule("r8")
+ libHeader := lib.Output("turbine-combined/lib.jar").Output
+ staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+
+ android.AssertStringDoesContain(t, "expected lib header jar in app javac classpath",
+ appJavac.Args["classpath"], libHeader.String())
+ android.AssertStringDoesContain(t, "expected static_lib header jar in app javac classpath",
+ appJavac.Args["classpath"], staticLibHeader.String())
+
+ android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
+ appR8.Args["r8Flags"], libHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app javac classpath",
+ appR8.Args["r8Flags"], staticLibHeader.String())
+}
+
+func TestD8(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: ["foo.java"],
+ libs: ["lib"],
+ static_libs: ["static_lib"],
+ installable: true,
+ }
+
+ java_library {
+ name: "lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "static_lib",
+ srcs: ["foo.java"],
+ }
+ `)
+
+ foo := result.ModuleForTests("foo", "android_common")
+ lib := result.ModuleForTests("lib", "android_common")
+ staticLib := result.ModuleForTests("static_lib", "android_common")
+
+ fooJavac := foo.Rule("javac")
+ fooD8 := foo.Rule("d8")
+ libHeader := lib.Output("turbine-combined/lib.jar").Output
+ staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+
+ android.AssertStringDoesContain(t, "expected lib header jar in foo javac classpath",
+ fooJavac.Args["classpath"], libHeader.String())
+ android.AssertStringDoesContain(t, "expected static_lib header jar in foo javac classpath",
+ fooJavac.Args["classpath"], staticLibHeader.String())
+
+ android.AssertStringDoesContain(t, "expected lib header jar in foo d8 classpath",
+ fooD8.Args["d8Flags"], libHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in foo javac classpath",
+ fooD8.Args["d8Flags"], staticLibHeader.String())
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 7c5f055..0adaf99 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -259,10 +259,6 @@
isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
bootImage := defaultBootImageConfig(ctx)
- if global.UseArtImage {
- bootImage = artBootImageConfig(ctx)
- }
-
dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
targets := ctx.MultiTargets()
diff --git a/java/dexpreopt.go_v1 b/java/dexpreopt.go_v1
new file mode 100644
index 0000000..0adaf99
--- /dev/null
+++ b/java/dexpreopt.go_v1
@@ -0,0 +1,404 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "path/filepath"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+)
+
+type DexpreopterInterface interface {
+ IsInstallable() bool // Structs that embed dexpreopter must implement this.
+ dexpreoptDisabled(ctx android.BaseModuleContext) bool
+ DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+ AndroidMkEntriesForApex() []android.AndroidMkEntries
+}
+
+type dexpreopterInstall struct {
+ // A unique name to distinguish an output from others for the same java library module. Usually in
+ // the form of `<arch>-<encoded-path>.odex/vdex/art`.
+ name string
+
+ // The name of the input java module.
+ moduleName string
+
+ // The path to the dexpreopt output on host.
+ outputPathOnHost android.Path
+
+ // The directory on the device for the output to install to.
+ installDirOnDevice android.InstallPath
+
+ // The basename (the last segment of the path) for the output to install as.
+ installFileOnDevice string
+}
+
+// The full module name of the output in the makefile.
+func (install *dexpreopterInstall) FullModuleName() string {
+ return install.moduleName + install.SubModuleName()
+}
+
+// The sub-module name of the output in the makefile (the name excluding the java module name).
+func (install *dexpreopterInstall) SubModuleName() string {
+ return "-dexpreopt-" + install.name
+}
+
+// Returns Make entries for installing the file.
+//
+// This function uses a value receiver rather than a pointer receiver to ensure that the object is
+// safe to use in `android.AndroidMkExtraEntriesFunc`.
+func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
+ return android.AndroidMkEntries{
+ Class: "ETC",
+ SubName: install.SubModuleName(),
+ OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+ entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+ },
+ },
+ }
+}
+
+type dexpreopter struct {
+ dexpreoptProperties DexpreoptProperties
+
+ installPath android.InstallPath
+ uncompressedDex bool
+ isSDKLibrary bool
+ isApp bool
+ isTest bool
+ isPresignedPrebuilt bool
+ preventInstall bool
+
+ manifestFile android.Path
+ statusFile android.WritablePath
+ enforceUsesLibs bool
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
+
+ // See the `dexpreopt` function for details.
+ builtInstalled string
+ builtInstalledForApex []dexpreopterInstall
+
+ // The config is used for two purposes:
+ // - Passing dexpreopt information about libraries from Soong to Make. This is needed when
+ // a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
+ // Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
+ // - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
+ // dexpreopt another partition).
+ configPath android.WritablePath
+}
+
+type DexpreoptProperties struct {
+ Dex_preopt struct {
+ // If false, prevent dexpreopting. Defaults to true.
+ Enabled *bool
+
+ // If true, generate an app image (.art file) for this module.
+ App_image *bool
+
+ // If true, use a checked-in profile to guide optimization. Defaults to false unless
+ // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
+ // that matches the name of this module, in which case it is defaulted to true.
+ Profile_guided *bool
+
+ // If set, provides the path to profile relative to the Android.bp file. If not set,
+ // defaults to searching for a file that matches the name of this module in the default
+ // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
+ Profile *string `android:"path"`
+ }
+}
+
+func init() {
+ dexpreopt.DexpreoptRunningInSoong = true
+}
+
+func isApexVariant(ctx android.BaseModuleContext) bool {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ return !apexInfo.IsForPlatform()
+}
+
+func forPrebuiltApex(ctx android.BaseModuleContext) bool {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ return apexInfo.ForPrebuiltApex
+}
+
+func moduleName(ctx android.BaseModuleContext) string {
+ // Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
+ // expected by dexpreopter.
+ return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+}
+
+func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
+ if !ctx.Device() {
+ return true
+ }
+
+ if d.isTest {
+ return true
+ }
+
+ if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
+ return true
+ }
+
+ // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
+ // dexpreopted.
+ if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
+ return true
+ }
+
+ if !android.IsModulePreferred(ctx.Module()) {
+ return true
+ }
+
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ if global.DisablePreopt {
+ return true
+ }
+
+ if inList(moduleName(ctx), global.DisablePreoptModules) {
+ return true
+ }
+
+ isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+ if isApexVariant(ctx) {
+ // Don't preopt APEX variant module unless the module is an APEX system server jar and we are
+ // building the entire system image.
+ if !isApexSystemServerJar || ctx.Config().UnbundledBuild() {
+ return true
+ }
+ } else {
+ // Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
+ if isApexSystemServerJar {
+ return true
+ }
+ }
+
+ // TODO: contains no java code
+
+ return false
+}
+
+func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
+ if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+ return
+ }
+ dexpreopt.RegisterToolDeps(ctx)
+}
+
+func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
+ return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+}
+
+// Returns the install path of the dex jar of a module.
+//
+// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
+// than the `name` in the path `/apex/<name>` as suggested in its comment.
+//
+// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
+// system server jar, which is fine because we currently only preopt system server jars for APEXes.
+func (d *dexpreopter) getInstallPath(
+ ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) {
+ dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx))
+ return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
+ }
+ if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
+ filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
+ ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
+ }
+ return defaultInstallPath
+}
+
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ // TODO(b/148690468): The check on d.installPath is to bail out in cases where
+ // the dexpreopter struct hasn't been fully initialized before we're called,
+ // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
+ // disabled, even if installable is true.
+ if d.installPath.Base() == "." {
+ return
+ }
+
+ dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
+
+ providesUsesLib := moduleName(ctx)
+ if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
+ name := ulib.ProvidesUsesLib()
+ if name != nil {
+ providesUsesLib = *name
+ }
+ }
+
+ // If it is test, make config files regardless of its dexpreopt setting.
+ // The config files are required for apps defined in make which depend on the lib.
+ if d.isTest && d.dexpreoptDisabled(ctx) {
+ return
+ }
+
+ isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+
+ bootImage := defaultBootImageConfig(ctx)
+ dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
+
+ targets := ctx.MultiTargets()
+ if len(targets) == 0 {
+ // assume this is a java library, dexpreopt for all arches for now
+ for _, target := range ctx.Config().Targets[android.Android] {
+ if target.NativeBridge == android.NativeBridgeDisabled {
+ targets = append(targets, target)
+ }
+ }
+ if isSystemServerJar && !d.isSDKLibrary {
+ // If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
+ targets = targets[:1]
+ }
+ }
+
+ var archs []android.ArchType
+ var images android.Paths
+ var imagesDeps []android.OutputPaths
+ for _, target := range targets {
+ archs = append(archs, target.Arch.ArchType)
+ variant := bootImage.getVariant(target)
+ images = append(images, variant.imagePathOnHost)
+ imagesDeps = append(imagesDeps, variant.imagesDeps)
+ }
+ // The image locations for all Android variants are identical.
+ hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
+
+ var profileClassListing android.OptionalPath
+ var profileBootListing android.OptionalPath
+ profileIsTextListing := false
+ if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
+ // If dex_preopt.profile_guided is not set, default it based on the existence of the
+ // dexprepot.profile option or the profile class listing.
+ if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
+ profileClassListing = android.OptionalPathForPath(
+ android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+ profileBootListing = android.ExistentPathForSource(ctx,
+ ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
+ profileIsTextListing = true
+ } else if global.ProfileDir != "" {
+ profileClassListing = android.ExistentPathForSource(ctx,
+ global.ProfileDir, moduleName(ctx)+".prof")
+ }
+ }
+
+ // Full dexpreopt config, used to create dexpreopt build rules.
+ dexpreoptConfig := &dexpreopt.ModuleConfig{
+ Name: moduleName(ctx),
+ DexLocation: dexLocation,
+ BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
+ DexPath: dexJarFile,
+ ManifestPath: android.OptionalPathForPath(d.manifestFile),
+ UncompressedDex: d.uncompressedDex,
+ HasApkLibraries: false,
+ PreoptFlags: nil,
+
+ ProfileClassListing: profileClassListing,
+ ProfileIsTextListing: profileIsTextListing,
+ ProfileBootListing: profileBootListing,
+
+ EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
+ EnforceUsesLibraries: d.enforceUsesLibs,
+ ProvidesUsesLibrary: providesUsesLib,
+ ClassLoaderContexts: d.classLoaderContexts,
+
+ Archs: archs,
+ DexPreoptImagesDeps: imagesDeps,
+ DexPreoptImageLocationsOnHost: hostImageLocations,
+ DexPreoptImageLocationsOnDevice: deviceImageLocations,
+
+ PreoptBootClassPathDexFiles: dexFiles.Paths(),
+ PreoptBootClassPathDexLocations: dexLocations,
+
+ PreoptExtractedApk: false,
+
+ NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
+ ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
+
+ PresignedPrebuilt: d.isPresignedPrebuilt,
+ }
+
+ d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
+ dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
+
+ if d.dexpreoptDisabled(ctx) {
+ return
+ }
+
+ globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
+ if err != nil {
+ ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
+ return
+ }
+
+ dexpreoptRule.Build("dexpreopt", "dexpreopt")
+
+ isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+
+ for _, install := range dexpreoptRule.Installs() {
+ // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
+ installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+ installBase := filepath.Base(install.To)
+ arch := filepath.Base(installDir)
+ installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+
+ if isApexSystemServerJar {
+ // APEX variants of java libraries are hidden from Make, so their dexpreopt
+ // outputs need special handling. Currently, for APEX variants of java
+ // libraries, only those in the system server classpath are handled here.
+ // Preopting of boot classpath jars in the ART APEX are handled in
+ // java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
+ // The installs will be handled by Make as sub-modules of the java library.
+ d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+ name: arch + "-" + installBase,
+ moduleName: moduleName(ctx),
+ outputPathOnHost: install.From,
+ installDirOnDevice: installPath,
+ installFileOnDevice: installBase,
+ })
+ } else if !d.preventInstall {
+ ctx.InstallFile(installPath, installBase, install.From)
+ }
+ }
+
+ if !isApexSystemServerJar {
+ d.builtInstalled = dexpreoptRule.Installs().String()
+ }
+}
+
+func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
+ return d.builtInstalledForApex
+}
+
+func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
+ var entries []android.AndroidMkEntries
+ for _, install := range d.builtInstalledForApex {
+ entries = append(entries, install.ToMakeEntries())
+ }
+ return entries
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index cad9c33..7c4da3e 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -213,12 +213,6 @@
// writes out a few DEXPREOPT_IMAGE_* variables for Make; these variables contain boot image names,
// paths and so on.
//
-// 2.5. JIT-Zygote configuration
-// -----------------------------
-//
-// One special configuration is JIT-Zygote build, when the primary ART image is used for compiling
-// apps instead of the Framework boot image extension (see DEXPREOPT_USE_ART_IMAGE and UseArtImage).
-//
var artApexNames = []string{
"com.android.art",
@@ -276,11 +270,17 @@
// Rules which should be used in make to install the outputs.
profileInstalls android.RuleBuilderInstalls
+ // Path to the license metadata file for the module that built the profile.
+ profileLicenseMetadataFile android.OptionalPath
+
// Path to the image profile file on host (or empty, if profile is not generated).
profilePathOnHost android.Path
// Target-dependent fields.
variants []*bootImageVariant
+
+ // Path of the preloaded classes file.
+ preloadedClassesFile string
}
// Target-dependent description of a boot image.
@@ -320,6 +320,9 @@
// Rules which should be used in make to install the outputs on device.
deviceInstalls android.RuleBuilderInstalls
+
+ // Path to the license metadata file for the module that built the image.
+ licenseMetadataFile android.OptionalPath
}
// Get target-specific boot image variant for the given boot image config and target.
@@ -680,6 +683,13 @@
cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
}
+ // We always expect a preloaded classes file to be available. However, if we cannot find it, it's
+ // OK to not pass the flag to dex2oat.
+ preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
+ if preloadedClassesPath.Valid() {
+ cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
+ }
+
cmd.
FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
FlagForEachArg("--dex-location=", image.dexLocations).
@@ -759,6 +769,7 @@
image.vdexInstalls = vdexInstalls
image.unstrippedInstalls = unstrippedInstalls
image.deviceInstalls = deviceInstalls
+ image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
}
const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
@@ -807,6 +818,7 @@
if image == defaultBootImageConfig(ctx) {
rule.Install(profile, "/system/etc/boot-image.prof")
image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
}
rule.Build("bootJarsProfile", "profile boot jars")
@@ -844,6 +856,7 @@
rule.Install(profile, "/system/etc/boot-image.bprof")
rule.Build("bootFrameworkProfile", "profile boot framework jars")
image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
return profile
}
@@ -909,6 +922,9 @@
image := d.defaultBootImage
if image != nil {
ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
+ if image.profileLicenseMetadataFile.Valid() {
+ ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
+ }
global := dexpreopt.GetGlobalConfig(ctx)
dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
@@ -916,11 +932,8 @@
ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
var imageNames []string
- // TODO: the primary ART boot image should not be exposed to Make, as it is installed in a
- // different way as a part of the ART APEX. However, there is a special JIT-Zygote build
- // configuration which uses the primary ART image instead of the Framework boot image
- // extension, and it relies on the ART image being exposed to Make. To fix this, it is
- // necessary to rework the logic in makefiles.
+ // The primary ART boot image is exposed to Make for testing (gtests) and benchmarking
+ // (golem) purposes.
for _, current := range append(d.otherImages, image) {
imageNames = append(imageNames, current.name)
for _, variant := range current.variants {
@@ -934,6 +947,9 @@
ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
+ if variant.licenseMetadataFile.Valid() {
+ ctx.Strict("DEXPREOPT_IMAGE_LICENSE_METADATA_"+sfx, variant.licenseMetadataFile.String())
+ }
}
imageLocationsOnHost, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":"))
diff --git a/java/dexpreopt_bootjars.go_v1 b/java/dexpreopt_bootjars.go_v1
new file mode 100644
index 0000000..07a357b
--- /dev/null
+++ b/java/dexpreopt_bootjars.go_v1
@@ -0,0 +1,952 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "path/filepath"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// =================================================================================================
+// WIP - see http://b/177892522 for details
+//
+// The build support for boot images is currently being migrated away from singleton to modules so
+// the documentation may not be strictly accurate. Rather than update the documentation at every
+// step which will create a lot of churn the changes that have been made will be listed here and the
+// documentation will be updated once it is closer to the final result.
+//
+// Changes:
+// 1) dex_bootjars is now a singleton module and not a plain singleton.
+// 2) Boot images are now represented by the boot_image module type.
+// 3) The art boot image is called "art-boot-image", the framework boot image is called
+// "framework-boot-image".
+// 4) They are defined in art/build/boot/Android.bp and frameworks/base/boot/Android.bp
+// respectively.
+// 5) Each boot_image retrieves the appropriate boot image configuration from the map returned by
+// genBootImageConfigs() using the image_name specified in the boot_image module.
+// =================================================================================================
+
+// This comment describes:
+// 1. ART boot images in general (their types, structure, file layout, etc.)
+// 2. build system support for boot images
+//
+// 1. ART boot images
+// ------------------
+//
+// A boot image in ART is a set of files that contain AOT-compiled native code and a heap snapshot
+// of AOT-initialized classes for the bootclasspath Java libraries. A boot image is compiled from a
+// set of DEX jars by the dex2oat compiler. A boot image is used for two purposes: 1) it is
+// installed on device and loaded at runtime, and 2) other Java libraries and apps are compiled
+// against it (compilation may take place either on host, known as "dexpreopt", or on device, known
+// as "dexopt").
+//
+// A boot image is not a single file, but a collection of interrelated files. Each boot image has a
+// number of components that correspond to the Java libraries that constitute it. For each component
+// there are multiple files:
+// - *.oat or *.odex file with native code (architecture-specific, one per instruction set)
+// - *.art file with pre-initialized Java classes (architecture-specific, one per instruction set)
+// - *.vdex file with verification metadata for the DEX bytecode (architecture independent)
+//
+// *.vdex files for the boot images do not contain the DEX bytecode itself, because the
+// bootclasspath DEX files are stored on disk in uncompressed and aligned form. Consequently a boot
+// image is not self-contained and cannot be used without its DEX files. To simplify the management
+// of boot image files, ART uses a certain naming scheme and associates the following metadata with
+// each boot image:
+// - A stem, which is a symbolic name that is prepended to boot image file names.
+// - A location (on-device path to the boot image files).
+// - A list of boot image locations (on-device paths to dependency boot images).
+// - A set of DEX locations (on-device paths to the DEX files, one location for one DEX file used
+// to compile the boot image).
+//
+// There are two kinds of boot images:
+// - primary boot images
+// - boot image extensions
+//
+// 1.1. Primary boot images
+// ------------------------
+//
+// A primary boot image is compiled for a core subset of bootclasspath Java libraries. It does not
+// depend on any other images, and other boot images may depend on it.
+//
+// For example, assuming that the stem is "boot", the location is /apex/com.android.art/javalib/,
+// the set of core bootclasspath libraries is A B C, and the boot image is compiled for ARM targets
+// (32 and 64 bits), it will have three components with the following files:
+// - /apex/com.android.art/javalib/{arm,arm64}/boot.{art,oat,vdex}
+// - /apex/com.android.art/javalib/{arm,arm64}/boot-B.{art,oat,vdex}
+// - /apex/com.android.art/javalib/{arm,arm64}/boot-C.{art,oat,vdex}
+//
+// The files of the first component are special: they do not have the component name appended after
+// the stem. This naming convention dates back to the times when the boot image was not split into
+// components, and there were just boot.oat and boot.art. The decision to split was motivated by
+// licensing reasons for one of the bootclasspath libraries.
+//
+// As of November 2020 the only primary boot image in Android is the image in the ART APEX
+// com.android.art. The primary ART boot image contains the Core libraries that are part of the ART
+// module. When the ART module gets updated, the primary boot image will be updated with it, and all
+// dependent images will get invalidated (the checksum of the primary image stored in dependent
+// images will not match), unless they are updated in sync with the ART module.
+//
+// 1.2. Boot image extensions
+// --------------------------
+//
+// A boot image extension is compiled for a subset of bootclasspath Java libraries (in particular,
+// this subset does not include the Core bootclasspath libraries that go into the primary boot
+// image). A boot image extension depends on the primary boot image and optionally some other boot
+// image extensions. Other images may depend on it. In other words, boot image extensions can form
+// acyclic dependency graphs.
+//
+// The motivation for boot image extensions comes from the Mainline project. Consider a situation
+// when the list of bootclasspath libraries is A B C, and both A and B are parts of the Android
+// platform, but C is part of an updatable APEX com.android.C. When the APEX is updated, the Java
+// code for C might have changed compared to the code that was used to compile the boot image.
+// Consequently, the whole boot image is obsolete and invalidated (even though the code for A and B
+// that does not depend on C is up to date). To avoid this, the original monolithic boot image is
+// split in two parts: the primary boot image that contains A B, and the boot image extension that
+// contains C and depends on the primary boot image (extends it).
+//
+// For example, assuming that the stem is "boot", the location is /system/framework, the set of
+// bootclasspath libraries is D E (where D is part of the platform and is located in
+// /system/framework, and E is part of a non-updatable APEX com.android.E and is located in
+// /apex/com.android.E/javalib), and the boot image is compiled for ARM targets (32 and 64 bits),
+// it will have two components with the following files:
+// - /system/framework/{arm,arm64}/boot-D.{art,oat,vdex}
+// - /system/framework/{arm,arm64}/boot-E.{art,oat,vdex}
+//
+// As of November 2020 the only boot image extension in Android is the Framework boot image
+// extension. It extends the primary ART boot image and contains Framework libraries and other
+// bootclasspath libraries from the platform and non-updatable APEXes that are not included in the
+// ART image. The Framework boot image extension is updated together with the platform. In the
+// future other boot image extensions may be added for some updatable modules.
+//
+//
+// 2. Build system support for boot images
+// ---------------------------------------
+//
+// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
+// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
+// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
+// core libraries as they are already part of the primary ART boot image.
+//
+// 2.1. Libraries that go in the boot images
+// -----------------------------------------
+//
+// The contents of each boot image are determined by the PRODUCT variables. The primary ART APEX
+// boot image contains libraries listed in the ART_APEX_JARS variable in the AOSP makefiles. The
+// Framework boot image extension contains libraries specified in the PRODUCT_BOOT_JARS and
+// PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries,
+// but more product-specific libraries can be added in the product makefiles.
+//
+// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is a
+// colon-separated pair <apex>:<library>, where <apex> is the variant name of a non-updatable APEX,
+// "platform" if the library is a part of the platform in the system partition, or "system_ext" if
+// it's in the system_ext partition.
+//
+// In these variables APEXes are identified by their "variant names", i.e. the names they get
+// mounted as in /apex on device. In Soong modules that is the name set in the "apex_name"
+// properties, which default to the "name" values. For example, many APEXes have both
+// com.android.xxx and com.google.android.xxx modules in Soong, but take the same place
+// /apex/com.android.xxx at runtime. In these cases the variant name is always com.android.xxx,
+// regardless which APEX goes into the product. See also android.ApexInfo.ApexVariationName and
+// apex.apexBundleProperties.Apex_name.
+//
+// A related variable PRODUCT_APEX_BOOT_JARS contains bootclasspath libraries that are in APEXes.
+// They are not included in the boot image. The only exception here are ART jars and core-icu4j.jar
+// that have been historically part of the boot image and are now in apexes; they are in boot images
+// and core-icu4j.jar is generally treated as being part of PRODUCT_BOOT_JARS.
+//
+// One exception to the above rules are "coverage" builds (a special build flavor which requires
+// setting environment variable EMMA_INSTRUMENT_FRAMEWORK=true). In coverage builds the Java code in
+// boot image libraries is instrumented, which means that the instrumentation library (jacocoagent)
+// needs to be added to the list of bootclasspath DEX jars.
+//
+// In general, there is a requirement that the source code for a boot image library must be
+// available at build time (e.g. it cannot be a stub that has a separate implementation library).
+//
+// 2.2. Static configs
+// -------------------
+//
+// Because boot images are used to dexpreopt other Java modules, the paths to boot image files must
+// be known by the time dexpreopt build rules for the dependent modules are generated. Boot image
+// configs are constructed very early during the build, before build rule generation. The configs
+// provide predefined paths to boot image files (these paths depend only on static build
+// configuration, such as PRODUCT variables, and use hard-coded directory names).
+//
+// 2.3. Singleton
+// --------------
+//
+// Build rules for the boot images are generated with a Soong singleton. Because a singleton has no
+// dependencies on other modules, it has to find the modules for the DEX jars using VisitAllModules.
+// Soong loops through all modules and compares each module against a list of bootclasspath library
+// names. Then it generates build rules that copy DEX jars from their intermediate module-specific
+// locations to the hard-coded locations predefined in the boot image configs.
+//
+// It would be possible to use a module with proper dependencies instead, but that would require
+// changes in the way Soong generates variables for Make: a singleton can use one MakeVars() method
+// that writes variables to out/soong/make_vars-*.mk, which is included early by the main makefile,
+// but module(s) would have to use out/soong/Android-*.mk which has a group of LOCAL_* variables
+// for each module, and is included later.
+//
+// 2.4. Install rules
+// ------------------
+//
+// The primary boot image and the Framework extension are installed in different ways. The primary
+// boot image is part of the ART APEX: it is copied into the APEX intermediate files, packaged
+// together with other APEX contents, extracted and mounted on device. The Framework boot image
+// extension is installed by the rules defined in makefiles (make/core/dex_preopt_libart.mk). Soong
+// writes out a few DEXPREOPT_IMAGE_* variables for Make; these variables contain boot image names,
+// paths and so on.
+//
+
+var artApexNames = []string{
+ "com.android.art",
+ "com.android.art.debug",
+ "com.android.art.testing",
+ "com.google.android.art",
+ "com.google.android.art.debug",
+ "com.google.android.art.testing",
+}
+
+func init() {
+ RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
+}
+
+// Target-independent description of a boot image.
+type bootImageConfig struct {
+ // If this image is an extension, the image that it extends.
+ extends *bootImageConfig
+
+ // Image name (used in directory names and ninja rule names).
+ name string
+
+ // Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}.
+ stem string
+
+ // Output directory for the image files.
+ dir android.OutputPath
+
+ // Output directory for the image files with debug symbols.
+ symbolsDir android.OutputPath
+
+ // Subdirectory where the image files are installed.
+ installDirOnHost string
+
+ // Subdirectory where the image files on device are installed.
+ installDirOnDevice string
+
+ // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
+ // needed.
+ profileInstallPathInApex string
+
+ // A list of (location, jar) pairs for the Java modules in this image.
+ modules android.ConfiguredJarList
+
+ // File paths to jars.
+ dexPaths android.WritablePaths // for this image
+ dexPathsDeps android.WritablePaths // for the dependency images and in this image
+
+ // Map from module name (without prebuilt_ prefix) to the predefined build path.
+ dexPathsByModule map[string]android.WritablePath
+
+ // File path to a zip archive with all image files (or nil, if not needed).
+ zip android.WritablePath
+
+ // Rules which should be used in make to install the outputs.
+ profileInstalls android.RuleBuilderInstalls
+
+ // Path to the license metadata file for the module that built the profile.
+ profileLicenseMetadataFile android.OptionalPath
+
+ // Path to the image profile file on host (or empty, if profile is not generated).
+ profilePathOnHost android.Path
+
+ // Target-dependent fields.
+ variants []*bootImageVariant
+
+ // Path of the preloaded classes file.
+ preloadedClassesFile string
+}
+
+// Target-dependent description of a boot image.
+type bootImageVariant struct {
+ *bootImageConfig
+
+ // Target for which the image is generated.
+ target android.Target
+
+ // The "locations" of jars.
+ dexLocations []string // for this image
+ dexLocationsDeps []string // for the dependency images and in this image
+
+ // Paths to image files.
+ imagePathOnHost android.OutputPath // first image file path on host
+ imagePathOnDevice string // first image file path on device
+
+ // All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
+ imagesDeps android.OutputPaths
+
+ // The path to the primary image variant's imagePathOnHost field, where primary image variant
+ // means the image variant that this extends.
+ //
+ // This is only set for a variant of an image that extends another image.
+ primaryImages android.OutputPath
+
+ // The paths to the primary image variant's imagesDeps field, where primary image variant
+ // means the image variant that this extends.
+ //
+ // This is only set for a variant of an image that extends another image.
+ primaryImagesDeps android.Paths
+
+ // Rules which should be used in make to install the outputs on host.
+ installs android.RuleBuilderInstalls
+ vdexInstalls android.RuleBuilderInstalls
+ unstrippedInstalls android.RuleBuilderInstalls
+
+ // Rules which should be used in make to install the outputs on device.
+ deviceInstalls android.RuleBuilderInstalls
+
+ // Path to the license metadata file for the module that built the image.
+ licenseMetadataFile android.OptionalPath
+}
+
+// Get target-specific boot image variant for the given boot image config and target.
+func (image bootImageConfig) getVariant(target android.Target) *bootImageVariant {
+ for _, variant := range image.variants {
+ if variant.target.Os == target.Os && variant.target.Arch.ArchType == target.Arch.ArchType {
+ return variant
+ }
+ }
+ return nil
+}
+
+// Return any (the first) variant which is for the device (as opposed to for the host).
+func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant {
+ for _, variant := range image.variants {
+ if variant.target.Os == android.Android {
+ return variant
+ }
+ }
+ return nil
+}
+
+// Return the name of a boot image module given a boot image config and a component (module) index.
+// A module name is a combination of the Java library name, and the boot image stem (that is stored
+// in the config).
+func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string {
+ // The first module of the primary boot image is special: its module name has only the stem, but
+ // not the library name. All other module names are of the form <stem>-<library name>
+ m := image.modules.Jar(idx)
+ name := image.stem
+ if idx != 0 || image.extends != nil {
+ name += "-" + android.ModuleStem(m)
+ }
+ return name
+}
+
+// Return the name of the first boot image module, or stem if the list of modules is empty.
+func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
+ if image.modules.Len() > 0 {
+ return image.moduleName(ctx, 0)
+ } else {
+ return image.stem
+ }
+}
+
+// Return filenames for the given boot image component, given the output directory and a list of
+// extensions.
+func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
+ ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts))
+ for i := 0; i < image.modules.Len(); i++ {
+ name := image.moduleName(ctx, i)
+ for _, ext := range exts {
+ ret = append(ret, dir.Join(ctx, name+ext))
+ }
+ }
+ return ret
+}
+
+// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
+func (image *bootImageConfig) apexVariants() []*bootImageVariant {
+ variants := []*bootImageVariant{}
+ for _, variant := range image.variants {
+ // We also generate boot images for host (for testing), but we don't need those in the apex.
+ // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+ if variant.target.Os == android.Android {
+ variants = append(variants, variant)
+ }
+ }
+ return variants
+}
+
+// Returns true if the boot image should be installed in the APEX.
+func (image *bootImageConfig) shouldInstallInApex() bool {
+ return strings.HasPrefix(image.installDirOnDevice, "apex/")
+}
+
+// Return boot image locations (as a list of symbolic paths).
+//
+// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
+// exist on the device. Typically it is /apex/com.android.art/javalib/boot.art and should be the
+// same for all supported architectures on the device. The concrete architecture specific files
+// actually end up in architecture-specific sub-directory such as arm, arm64, x86, or x86_64.
+//
+// For example a physical file /apex/com.android.art/javalib/x86/boot.art has "image location"
+// /apex/com.android.art/javalib/boot.art (which is not an actual file).
+//
+// For a primary boot image the list of locations has a single element.
+//
+// For a boot image extension the list of locations contains a location for all dependency images
+// (including the primary image) and the location of the extension itself. For example, for the
+// Framework boot image extension that depends on the primary ART boot image the list contains two
+// elements.
+//
+// The location is passed as an argument to the ART tools like dex2oat instead of the real path.
+// ART tools will then reconstruct the architecture-specific real path.
+//
+func (image *bootImageVariant) imageLocations() (imageLocationsOnHost []string, imageLocationsOnDevice []string) {
+ if image.extends != nil {
+ imageLocationsOnHost, imageLocationsOnDevice = image.extends.getVariant(image.target).imageLocations()
+ }
+ return append(imageLocationsOnHost, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType)),
+ append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType))
+}
+
+func dexpreoptBootJarsFactory() android.SingletonModule {
+ m := &dexpreoptBootJars{}
+ android.InitAndroidModule(m)
+ return m
+}
+
+func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+}
+
+func SkipDexpreoptBootJars(ctx android.PathContext) bool {
+ return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
+}
+
+// Singleton module for generating boot image build rules.
+type dexpreoptBootJars struct {
+ android.SingletonModuleBase
+
+ // Default boot image config (currently always the Framework boot image extension). It should be
+ // noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
+ // but the switch is handled not here, but in the makefiles (triggered with
+ // DEXPREOPT_USE_ART_IMAGE=true).
+ defaultBootImage *bootImageConfig
+
+ // Build path to a config file that Soong writes for Make (to be used in makefiles that install
+ // the default boot image).
+ dexpreoptConfigForMake android.WritablePath
+}
+
+// Provide paths to boot images for use by modules that depend upon them.
+//
+// The build rules are created in GenerateSingletonBuildActions().
+func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Placeholder for now.
+}
+
+// Generate build rules for boot images.
+func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+ if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
+ // No module has enabled dexpreopting, so we assume there will be no boot image to make.
+ return
+ }
+
+ d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config")
+ writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
+
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if !shouldBuildBootImages(ctx.Config(), global) {
+ return
+ }
+
+ defaultImageConfig := defaultBootImageConfig(ctx)
+ d.defaultBootImage = defaultImageConfig
+}
+
+// shouldBuildBootImages determines whether boot images should be built.
+func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig) bool {
+ // Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
+ // and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
+ // Note: this is technically incorrect. Compiled code contains stack checks which may depend
+ // on ASAN settings.
+ if len(config.SanitizeDevice()) == 1 && config.SanitizeDevice()[0] == "address" && global.SanitizeLite {
+ return false
+ }
+ return true
+}
+
+// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
+// paths in the global config.
+func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
+ // Create the super set of module names.
+ names := []string{}
+ names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
+ names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
+ names = android.SortedUniqueStrings(names)
+ for _, name := range names {
+ src := srcBootDexJarsByModule[name]
+ dst := dstBootJarsByModule[name]
+
+ if src == nil {
+ // A dex boot jar should be provided by the source java module. It needs to be installable or
+ // have compile_dex=true - cf. assignments to java.Module.dexJarFile.
+ //
+ // However, the source java module may be either replaced or overridden (using prefer:true) by
+ // a prebuilt java module with the same name. In that case the dex boot jar needs to be
+ // provided by the corresponding prebuilt APEX module. That APEX is the one that refers
+ // through a exported_(boot|systemserver)classpath_fragments property to a
+ // prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt
+ // java module in the contents property. If that chain is broken then this dependency will
+ // fail.
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name)
+ } else {
+ ctx.AddMissingDependencies([]string{name})
+ }
+ } else if dst == nil {
+ ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
+ } else {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: src,
+ Output: dst,
+ })
+ }
+ }
+}
+
+// buildBootImageVariantsForAndroidOs generates rules to build the boot image variants for the
+// android.Android OsType and returns a map from the architectures to the paths of the generated
+// boot image files.
+//
+// The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX.
+func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch {
+ return buildBootImageForOsType(ctx, image, profile, android.Android)
+}
+
+// buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the
+// config.BuildOS OsType, i.e. the type of OS on which the build is being running.
+//
+// The files need to be generated into their predefined location because they are used from there
+// both within Soong and outside, e.g. for ART based host side testing and also for use by some
+// cloud based tools. However, they are not needed by callers of this function and so the paths do
+// not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func.
+func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
+ buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
+}
+
+// buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType
+// boot image files are required for and it creates rules to build the boot image
+// files for all the required architectures for them.
+//
+// It returns a map from android.ArchType to the predefined paths of the boot image files.
+func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageFilesByArch {
+ filesByArch := bootImageFilesByArch{}
+ for _, variant := range image.variants {
+ if variant.target.Os == requiredOsType {
+ buildBootImageVariant(ctx, variant, profile)
+ filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
+ }
+ }
+
+ return filesByArch
+}
+
+// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files.
+//
+// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it
+// is a map from android.ArchType to the predefined locations.
+func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) {
+ if filesByArch == nil {
+ return
+ }
+
+ // Compute the list of files from all the architectures.
+ zipFiles := android.Paths{}
+ for _, archType := range android.ArchTypeList() {
+ zipFiles = append(zipFiles, filesByArch[archType]...)
+ }
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("soong_zip").
+ FlagWithOutput("-o ", image.zip).
+ FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
+ FlagWithInputList("-f ", zipFiles, " -f ")
+
+ rule.Build("zip_"+image.name, "zip "+image.name+" image")
+}
+
+// Generate boot image build rules for a specific target.
+func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) {
+
+ globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ arch := image.target.Arch.ArchType
+ os := image.target.Os.String() // We need to distinguish host-x86 and device-x86.
+ symbolsDir := image.symbolsDir.Join(ctx, os, image.installDirOnHost, arch.String())
+ symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
+ outputDir := image.dir.Join(ctx, os, image.installDirOnHost, arch.String())
+ outputPath := outputDir.Join(ctx, image.stem+".oat")
+ oatLocation := dexpreopt.PathToLocation(outputPath, arch)
+ imagePath := outputPath.ReplaceExtension(ctx, "art")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
+ rule.Command().Text("rm").Flag("-f").
+ Flag(symbolsDir.Join(ctx, "*.art").String()).
+ Flag(symbolsDir.Join(ctx, "*.oat").String()).
+ Flag(symbolsDir.Join(ctx, "*.invocation").String())
+ rule.Command().Text("rm").Flag("-f").
+ Flag(outputDir.Join(ctx, "*.art").String()).
+ Flag(outputDir.Join(ctx, "*.oat").String()).
+ Flag(outputDir.Join(ctx, "*.invocation").String())
+
+ cmd := rule.Command()
+
+ extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS")
+ if extraFlags == "" {
+ // Use ANDROID_LOG_TAGS to suppress most logging by default...
+ cmd.Text(`ANDROID_LOG_TAGS="*:e"`)
+ } else {
+ // ...unless the boot image is generated specifically for testing, then allow all logging.
+ cmd.Text(`ANDROID_LOG_TAGS="*:v"`)
+ }
+
+ invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
+
+ cmd.Tool(globalSoong.Dex2oat).
+ Flag("--avoid-storing-invocation").
+ FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
+ Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
+ Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx)
+
+ if profile != nil {
+ cmd.FlagWithInput("--profile-file=", profile)
+ }
+
+ dirtyImageFile := "frameworks/base/config/dirty-image-objects"
+ dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
+ if dirtyImagePath.Valid() {
+ cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path())
+ }
+
+ if image.extends != nil {
+ // It is a boot image extension, so it needs the boot image it depends on (in this case the
+ // primary ART APEX image).
+ artImage := image.primaryImages
+ cmd.
+ Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
+ Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
+ // Add the path to the first file in the boot image with the arch specific directory removed,
+ // dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
+ // to the file cannot be passed to the command make sure to add the actual path as an Implicit
+ // dependency to ensure that it is built before the command runs.
+ FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage).
+ // Similarly, the dex2oat tool will automatically find the paths to other files in the base
+ // boot image so make sure to add them as implicit dependencies to ensure that they are built
+ // before this command is run.
+ Implicits(image.primaryImagesDeps)
+ } else {
+ // It is a primary image, so it needs a base address.
+ cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
+ }
+
+ // We always expect a preloaded classes file to be available. However, if we cannot find it, it's
+ // OK to not pass the flag to dex2oat.
+ preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
+ if preloadedClassesPath.Valid() {
+ cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
+ }
+
+ cmd.
+ FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
+ FlagForEachArg("--dex-location=", image.dexLocations).
+ Flag("--generate-debug-info").
+ Flag("--generate-build-id").
+ Flag("--image-format=lz4hc").
+ FlagWithArg("--oat-symbols=", symbolsFile.String()).
+ Flag("--strip").
+ FlagWithArg("--oat-file=", outputPath.String()).
+ FlagWithArg("--oat-location=", oatLocation).
+ FlagWithArg("--image=", imagePath.String()).
+ FlagWithArg("--instruction-set=", arch.String()).
+ FlagWithArg("--android-root=", global.EmptyDirectory).
+ FlagWithArg("--no-inline-from=", "core-oj.jar").
+ Flag("--force-determinism").
+ Flag("--abort-on-hard-verifier-error")
+
+ // Use the default variant/features for host builds.
+ // The map below contains only device CPU info (which might be x86 on some devices).
+ if image.target.Os == android.Android {
+ cmd.FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch])
+ cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
+ }
+
+ if global.BootFlags != "" {
+ cmd.Flag(global.BootFlags)
+ }
+
+ if extraFlags != "" {
+ cmd.Flag(extraFlags)
+ }
+
+ cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
+
+ installDir := filepath.Join("/", image.installDirOnHost, arch.String())
+
+ var vdexInstalls android.RuleBuilderInstalls
+ var unstrippedInstalls android.RuleBuilderInstalls
+ var deviceInstalls android.RuleBuilderInstalls
+
+ for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
+ cmd.ImplicitOutput(artOrOat)
+
+ // Install the .oat and .art files
+ rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base()))
+ }
+
+ for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") {
+ cmd.ImplicitOutput(vdex)
+
+ // Note that the vdex files are identical between architectures.
+ // Make rules will create symlinks to share them between architectures.
+ vdexInstalls = append(vdexInstalls,
+ android.RuleBuilderInstall{vdex, filepath.Join(installDir, vdex.Base())})
+ }
+
+ for _, unstrippedOat := range image.moduleFiles(ctx, symbolsDir, ".oat") {
+ cmd.ImplicitOutput(unstrippedOat)
+
+ // Install the unstripped oat files. The Make rules will put these in $(TARGET_OUT_UNSTRIPPED)
+ unstrippedInstalls = append(unstrippedInstalls,
+ android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
+ }
+
+ if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
+ installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
+ for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
+ deviceInstalls = append(deviceInstalls,
+ android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
+ }
+ }
+
+ rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
+
+ // save output and installed files for makevars
+ image.installs = rule.Installs()
+ image.vdexInstalls = vdexInstalls
+ image.unstrippedInstalls = unstrippedInstalls
+ image.deviceInstalls = deviceInstalls
+ image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
+}
+
+const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
+It is likely that the boot classpath is inconsistent.
+Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
+
+func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+ globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ if global.DisableGenerateProfile {
+ return nil
+ }
+
+ defaultProfile := "frameworks/base/config/boot-image-profile.txt"
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ var bootImageProfile android.Path
+ if len(global.BootImageProfiles) > 1 {
+ combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
+ rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile)
+ bootImageProfile = combinedBootImageProfile
+ } else if len(global.BootImageProfiles) == 1 {
+ bootImageProfile = global.BootImageProfiles[0]
+ } else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
+ bootImageProfile = path.Path()
+ } else {
+ // No profile (not even a default one, which is the case on some branches
+ // like master-art-host that don't have frameworks/base).
+ // Return nil and continue without profile.
+ return nil
+ }
+
+ profile := image.dir.Join(ctx, "boot.prof")
+
+ rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(globalSoong.Profman).
+ Flag("--output-profile-type=boot").
+ FlagWithInput("--create-profile-from=", bootImageProfile).
+ FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
+ FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
+ FlagWithOutput("--reference-profile-file=", profile)
+
+ if image == defaultBootImageConfig(ctx) {
+ rule.Install(profile, "/system/etc/boot-image.prof")
+ image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
+ }
+
+ rule.Build("bootJarsProfile", "profile boot jars")
+
+ image.profilePathOnHost = profile
+
+ return profile
+}
+
+// bootFrameworkProfileRule generates the rule to create the boot framework profile and
+// returns a path to the generated file.
+func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+ globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
+ return nil
+ }
+
+ defaultProfile := "frameworks/base/config/boot-profile.txt"
+ bootFrameworkProfile := android.PathForSource(ctx, defaultProfile)
+
+ profile := image.dir.Join(ctx, "boot.bprof")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(globalSoong.Profman).
+ Flag("--output-profile-type=bprof").
+ FlagWithInput("--create-profile-from=", bootFrameworkProfile).
+ FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
+ FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
+ FlagWithOutput("--reference-profile-file=", profile)
+
+ rule.Install(profile, "/system/etc/boot-image.bprof")
+ rule.Build("bootFrameworkProfile", "profile boot framework jars")
+ image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
+
+ return profile
+}
+
+func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
+ var allPhonies android.Paths
+ for _, image := range image.variants {
+ arch := image.target.Arch.ArchType
+ suffix := arch.String()
+ // Host and target might both use x86 arch. We need to ensure the names are unique.
+ if image.target.Os.Class == android.Host {
+ suffix = "host-" + suffix
+ }
+ // Create a rule to call oatdump.
+ output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt")
+ rule := android.NewRuleBuilder(pctx, ctx)
+ imageLocationsOnHost, _ := image.imageLocations()
+ rule.Command().
+ BuiltTool("oatdump").
+ FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
+ FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
+ FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()).
+ FlagWithOutput("--output=", output).
+ FlagWithArg("--instruction-set=", arch.String())
+ rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
+
+ // Create a phony rule that depends on the output file and prints the path.
+ phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix)
+ rule = android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Implicit(output).
+ ImplicitOutput(phony).
+ Text("echo").FlagWithArg("Output in ", output.String())
+ rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
+
+ allPhonies = append(allPhonies, phony)
+ }
+
+ phony := android.PathForPhony(ctx, "dump-oat-boot")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Phony,
+ Output: phony,
+ Inputs: allPhonies,
+ Description: "dump-oat-boot",
+ })
+}
+
+func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
+ data := dexpreopt.GetGlobalConfigRawData(ctx)
+
+ android.WriteFileRule(ctx, path, string(data))
+}
+
+// Define Make variables for boot image names, paths, etc. These variables are used in makefiles
+// (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
+// correct output directories.
+func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
+ if d.dexpreoptConfigForMake != nil {
+ ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
+ ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
+ }
+
+ image := d.defaultBootImage
+ if image == nil {
+ return
+ }
+
+ ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
+ if image.profileLicenseMetadataFile.Valid() {
+ ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
+ }
+
+ global := dexpreopt.GetGlobalConfig(ctx)
+ dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
+
+ for _, variant := range image.variants {
+ suffix := ""
+ if variant.target.Os.Class == android.Host {
+ suffix = "_host"
+ }
+ sfx := suffix + "_" + variant.target.Arch.ArchType.String()
+ ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, variant.vdexInstalls.String())
+ ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.imagePathOnHost.String())
+ ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
+ ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
+ ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
+ if variant.licenseMetadataFile.Valid() {
+ ctx.Strict("DEXPREOPT_IMAGE_LICENSE_METADATA_"+sfx, variant.licenseMetadataFile.String())
+ }
+ }
+ imageLocationsOnHost, imageLocationsOnDevice := image.getAnyAndroidVariant().imageLocations()
+ ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST", strings.Join(imageLocationsOnHost, ":"))
+ ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE", strings.Join(imageLocationsOnDevice, ":"))
+ ctx.Strict("DEXPREOPT_IMAGE_ZIP", image.zip.String())
+
+ // There used to be multiple images for JIT-Zygote mode, not there's only one.
+ ctx.Strict("DEXPREOPT_IMAGE_NAMES", image.name)
+}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index df8d8c8..4d0bd09 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -59,21 +59,23 @@
name: artBootImageName,
stem: "boot",
installDirOnHost: "apex/art_boot_images/javalib",
- installDirOnDevice: "apex/com.android.art/javalib",
+ installDirOnDevice: "system/framework",
profileInstallPathInApex: "etc/boot-image.prof",
modules: artModules,
+ preloadedClassesFile: "art/build/boot/preloaded-classes",
}
// Framework config for the boot image extension.
// It includes framework libraries and depends on the ART config.
frameworkSubdir := "system/framework"
frameworkCfg := bootImageConfig{
- extends: &artCfg,
- name: frameworkBootImageName,
- stem: "boot",
- installDirOnHost: frameworkSubdir,
- installDirOnDevice: frameworkSubdir,
- modules: frameworkModules,
+ extends: &artCfg,
+ name: frameworkBootImageName,
+ stem: "boot",
+ installDirOnHost: frameworkSubdir,
+ installDirOnDevice: frameworkSubdir,
+ modules: frameworkModules,
+ preloadedClassesFile: "frameworks/base/config/preloaded-classes",
}
return map[string]*bootImageConfig{
diff --git a/java/dexpreopt_config.go_v1 b/java/dexpreopt_config.go_v1
new file mode 100644
index 0000000..d71e2bb
--- /dev/null
+++ b/java/dexpreopt_config.go_v1
@@ -0,0 +1,215 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "path/filepath"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+)
+
+// dexpreoptTargets returns the list of targets that are relevant to dexpreopting, which excludes architectures
+// supported through native bridge.
+func dexpreoptTargets(ctx android.PathContext) []android.Target {
+ var targets []android.Target
+ for _, target := range ctx.Config().Targets[android.Android] {
+ if target.NativeBridge == android.NativeBridgeDisabled {
+ targets = append(targets, target)
+ }
+ }
+ // We may also need the images on host in order to run host-based tests.
+ for _, target := range ctx.Config().Targets[ctx.Config().BuildOS] {
+ targets = append(targets, target)
+ }
+
+ return targets
+}
+
+var (
+ bootImageConfigKey = android.NewOnceKey("bootImageConfig")
+ bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw")
+ artBootImageName = "art"
+ frameworkBootImageName = "boot"
+)
+
+func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
+ return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ artModules := global.ArtApexJars
+ frameworkModules := global.BootJars.RemoveList(artModules)
+
+ // ART config for the primary boot image in the ART apex.
+ // It includes the Core Libraries.
+ artCfg := bootImageConfig{
+ name: artBootImageName,
+ stem: "boot",
+ installDirOnHost: "apex/art_boot_images/javalib",
+ installDirOnDevice: "system/framework",
+ profileInstallPathInApex: "etc/boot-image.prof",
+ modules: artModules,
+ preloadedClassesFile: "art/build/boot/preloaded-classes",
+ }
+
+ // Framework config for the boot image extension.
+ // It includes framework libraries and depends on the ART config.
+ frameworkSubdir := "system/framework"
+ frameworkCfg := bootImageConfig{
+ extends: &artCfg,
+ name: frameworkBootImageName,
+ stem: "boot",
+ installDirOnHost: frameworkSubdir,
+ installDirOnDevice: frameworkSubdir,
+ modules: frameworkModules,
+ preloadedClassesFile: "frameworks/base/config/preloaded-classes",
+ }
+
+ return map[string]*bootImageConfig{
+ artBootImageName: &artCfg,
+ frameworkBootImageName: &frameworkCfg,
+ }
+ }).(map[string]*bootImageConfig)
+}
+
+// Construct the global boot image configs.
+func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
+ return ctx.Config().Once(bootImageConfigKey, func() interface{} {
+ targets := dexpreoptTargets(ctx)
+ deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
+
+ configs := genBootImageConfigRaw(ctx)
+ artCfg := configs[artBootImageName]
+ frameworkCfg := configs[frameworkBootImageName]
+
+ // common to all configs
+ for _, c := range configs {
+ c.dir = deviceDir.Join(ctx, "dex_"+c.name+"jars")
+ c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped")
+
+ // expands to <stem>.art for primary image and <stem>-<1st module>.art for extension
+ imageName := c.firstModuleNameOrStem(ctx) + ".art"
+
+ // The path to bootclasspath dex files needs to be known at module
+ // GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled.
+ // Set up known paths for them, the singleton rules will copy them there.
+ // TODO(b/143682396): use module dependencies instead
+ inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
+ c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
+ c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir)
+ c.dexPathsDeps = c.dexPaths
+
+ // Create target-specific variants.
+ for _, target := range targets {
+ arch := target.Arch.ArchType
+ imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String())
+ variant := &bootImageVariant{
+ bootImageConfig: c,
+ target: target,
+ imagePathOnHost: imageDir.Join(ctx, imageName),
+ imagePathOnDevice: filepath.Join("/", c.installDirOnDevice, arch.String(), imageName),
+ imagesDeps: c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
+ dexLocations: c.modules.DevicePaths(ctx.Config(), target.Os),
+ }
+ variant.dexLocationsDeps = variant.dexLocations
+ c.variants = append(c.variants, variant)
+ }
+
+ c.zip = c.dir.Join(ctx, c.name+".zip")
+ }
+
+ // specific to the framework config
+ frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
+ for i := range targets {
+ frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
+ frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths()
+ frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
+ }
+
+ return configs
+ }).(map[string]*bootImageConfig)
+}
+
+func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig {
+ return genBootImageConfigs(ctx)[frameworkBootImageName]
+}
+
+// Apex boot config allows to access build/install paths of apex boot jars without going
+// through the usual trouble of registering dependencies on those modules and extracting build paths
+// from those dependencies.
+type apexBootConfig struct {
+ // A list of apex boot jars.
+ modules android.ConfiguredJarList
+
+ // A list of predefined build paths to apex boot jars. They are configured very early,
+ // before the modules for these jars are processed and the actual paths are generated, and
+ // later on a singleton adds commands to copy actual jars to the predefined paths.
+ dexPaths android.WritablePaths
+
+ // Map from module name (without prebuilt_ prefix) to the predefined build path.
+ dexPathsByModule map[string]android.WritablePath
+
+ // A list of dex locations (a.k.a. on-device paths) to the boot jars.
+ dexLocations []string
+}
+
+var updatableBootConfigKey = android.NewOnceKey("apexBootConfig")
+
+// Returns apex boot config.
+func GetApexBootConfig(ctx android.PathContext) apexBootConfig {
+ return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
+ apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
+
+ dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "apex_bootjars")
+ dexPaths := apexBootJars.BuildPaths(ctx, dir)
+ dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir)
+
+ dexLocations := apexBootJars.DevicePaths(ctx.Config(), android.Android)
+
+ return apexBootConfig{apexBootJars, dexPaths, dexPathsByModuleName, dexLocations}
+ }).(apexBootConfig)
+}
+
+// Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be
+// passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat).
+func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) {
+ // Non-updatable boot jars (they are used both in the boot image and in dexpreopt).
+ bootImage := defaultBootImageConfig(ctx)
+ dexPaths := bootImage.dexPathsDeps
+ // The dex locations for all Android variants are identical.
+ dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
+
+ if withUpdatable {
+ // Apex boot jars (they are used only in dexpreopt, but not in the boot image).
+ apexBootConfig := GetApexBootConfig(ctx)
+ dexPaths = append(dexPaths, apexBootConfig.dexPaths...)
+ dexLocations = append(dexLocations, apexBootConfig.dexLocations...)
+ }
+
+ return dexPaths, dexLocations
+}
+
+var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
+
+var copyOf = android.CopyOf
+
+func init() {
+ android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars)
+}
+
+func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
+ ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index c84a15c..023d619 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -330,7 +330,7 @@
// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
if len(aidlSrcs) > 0 {
- srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+ srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, nil, flags.aidlDeps)
outSrcFiles = append(outSrcFiles, srcJarFiles...)
}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 7ad316f..932fb19 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -135,6 +135,9 @@
// if set to true, Metalava will allow framework SDK to contain API levels annotations.
Api_levels_annotations_enabled *bool
+ // Apply the api levels database created by this module rather than generating one in this droidstubs.
+ Api_levels_module *string
+
// the dirs which Metalava extracts API levels annotations from.
Api_levels_annotations_dirs []string
@@ -234,6 +237,7 @@
var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
+var metalavaAPILevelsModuleTag = dependencyTag{name: "metalava-api-levels-module-tag"}
func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
d.Javadoc.addDeps(ctx)
@@ -255,6 +259,10 @@
ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
}
}
+
+ if d.properties.Api_levels_module != nil {
+ ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module))
+ }
}
func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
@@ -334,7 +342,11 @@
// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
FlagWithArg("--hide ", "SuperfluousPrefix").
- FlagWithArg("--hide ", "AnnotationExtraction")
+ FlagWithArg("--hide ", "AnnotationExtraction").
+ // b/222738070
+ FlagWithArg("--hide ", "BannedThrow").
+ // b/223382732
+ FlagWithArg("--hide ", "ChangedDefault")
}
}
@@ -361,21 +373,35 @@
}
func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- if !Bool(d.properties.Api_levels_annotations_enabled) {
- return
+ var apiVersions android.Path
+ if proptools.Bool(d.properties.Api_levels_annotations_enabled) {
+ d.apiLevelsGenerationFlags(ctx, cmd)
+ apiVersions = d.apiVersionsXml
+ } else {
+ ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) {
+ if s, ok := m.(*Droidstubs); ok {
+ apiVersions = s.apiVersionsXml
+ } else {
+ ctx.PropertyErrorf("api_levels_module",
+ "module %q is not a droidstubs module", ctx.OtherModuleName(m))
+ }
+ })
}
+ if apiVersions != nil {
+ cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+ cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
+ cmd.FlagWithInput("--apply-api-levels ", apiVersions)
+ }
+}
- d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
-
+func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
if len(d.properties.Api_levels_annotations_dirs) == 0 {
ctx.PropertyErrorf("api_levels_annotations_dirs",
"has to be non-empty if api levels annotations was enabled!")
}
+ d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
- cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
- cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
- cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
@@ -429,6 +455,10 @@
}
}
+func metalavaUseRbe(ctx android.ModuleContext) bool {
+ return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA")
+}
+
func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
rule.Command().Text("rm -rf").Flag(homeDir.String())
@@ -437,7 +467,7 @@
cmd := rule.Command()
cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
- if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
+ if metalavaUseRbe(ctx) {
rule.Remoteable(android.RemoteRuleSupports{RBE: true})
execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
labels := map[string]string{"type": "tool", "name": "metalava"}
@@ -472,7 +502,10 @@
Flag("--quiet").
Flag("--format=v2").
FlagWithArg("--repeat-errors-max ", "10").
- FlagWithArg("--hide ", "UnresolvedImport")
+ FlagWithArg("--hide ", "UnresolvedImport").
+ FlagWithArg("--hide ", "InvalidNullabilityOverride").
+ // b/223382732
+ FlagWithArg("--hide ", "ChangedDefault")
return cmd
}
@@ -658,91 +691,22 @@
}
// TODO(b/183630617): rewrapper doesn't support restat rules
- // rule.Restat()
+ if !metalavaUseRbe(ctx) {
+ rule.Restat()
+ }
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build("metalava", "metalava merged")
-
if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
+ d.generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx)
- if len(d.Javadoc.properties.Out) > 0 {
- ctx.PropertyErrorf("out", "out property may not be combined with check_api")
- }
-
- apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
- removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
-
- if baselineFile.Valid() {
- ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
- }
-
- d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- // Diff command line.
- // -F matches the closest "opening" line, such as "package android {"
- // and " public class Intent {".
- diff := `diff -u -F '{ *$'`
-
- rule.Command().Text("( true")
- rule.Command().
- Text(diff).
- Input(apiFile).Input(d.apiFile)
-
- rule.Command().
- Text(diff).
- Input(removedApiFile).Input(d.removedApiFile)
-
- msg := fmt.Sprintf(`\n******************************\n`+
- `You have tried to change the API from what has been previously approved.\n\n`+
- `To make these errors go away, you have two choices:\n`+
- ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
- ` to the new methods, etc. shown in the above diff.\n\n`+
- ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
- ` m %s-update-current-api\n\n`+
- ` To submit the revised current.txt to the main Android repository,\n`+
- ` you will need approval.\n`+
- `******************************\n`, ctx.ModuleName())
-
- rule.Command().
- Text("touch").Output(d.checkCurrentApiTimestamp).
- Text(") || (").
- Text("echo").Flag("-e").Flag(`"` + msg + `"`).
- Text("; exit 38").
- Text(")")
-
- rule.Build("metalavaCurrentApiCheck", "check current API")
-
- d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
-
- // update API rule
- rule = android.NewRuleBuilder(pctx, ctx)
-
- rule.Command().Text("( true")
-
- rule.Command().
- Text("cp").Flag("-f").
- Input(d.apiFile).Flag(apiFile.String())
-
- rule.Command().
- Text("cp").Flag("-f").
- Input(d.removedApiFile).Flag(removedApiFile.String())
-
- msg = "failed to update public API"
-
- rule.Command().
- Text("touch").Output(d.updateCurrentApiTimestamp).
- Text(") || (").
- Text("echo").Flag("-e").Flag(`"` + msg + `"`).
- Text("; exit 38").
- Text(")")
-
- rule.Build("metalavaCurrentApiUpdate", "update current API")
+ // Make sure that whenever the API stubs are generated that the current checked in API files are
+ // checked to make sure that they are up-to-date.
+ cmd.Validation(d.checkCurrentApiTimestamp)
}
+ rule.Build("metalava", "metalava merged")
+
if String(d.properties.Check_nullability_warnings) != "" {
if d.nullabilityWarningsFile == nil {
ctx.PropertyErrorf("check_nullability_warnings",
@@ -779,6 +743,84 @@
}
}
+func (d *Droidstubs) generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx android.ModuleContext) {
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+
+ if baselineFile.Valid() {
+ ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
+ }
+
+ d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ // Diff command line.
+ // -F matches the closest "opening" line, such as "package android {"
+ // and " public class Intent {".
+ diff := `diff -u -F '{ *$'`
+
+ rule.Command().Text("( true")
+ rule.Command().
+ Text(diff).
+ Input(apiFile).Input(d.apiFile)
+
+ rule.Command().
+ Text(diff).
+ Input(removedApiFile).Input(d.removedApiFile)
+
+ msg := fmt.Sprintf(`\n******************************\n`+
+ `You have tried to change the API from what has been previously approved.\n\n`+
+ `To make these errors go away, you have two choices:\n`+
+ ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
+ ` to the new methods, etc. shown in the above diff.\n\n`+
+ ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
+ ` m %s-update-current-api\n\n`+
+ ` To submit the revised current.txt to the main Android repository,\n`+
+ ` you will need approval.\n`+
+ `******************************\n`, ctx.ModuleName())
+
+ rule.Command().
+ Text("touch").Output(d.checkCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build("metalavaCurrentApiCheck", "check current API")
+
+ d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
+
+ // update API rule
+ rule = android.NewRuleBuilder(pctx, ctx)
+
+ rule.Command().Text("( true")
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.apiFile).Flag(apiFile.String())
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.removedApiFile).Flag(removedApiFile.String())
+
+ msg = "failed to update public API"
+
+ rule.Command().
+ Text("touch").Output(d.updateCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build("metalavaCurrentApiUpdate", "update current API")
+}
+
func StubsDefaultsFactory() android.Module {
module := &DocDefaults{}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 10d99f3..9fdfdde 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -46,6 +46,12 @@
api_levels_annotations_enabled: true,
api_levels_jar_filename: "android.other.jar",
}
+
+ droidstubs {
+ name: "stubs-applying-api-versions",
+ srcs: ["bar-doc/a.java"],
+ api_levels_module: "bar-stubs-other",
+ }
`,
map[string][]byte{
"bar-doc/a.java": nil,
@@ -53,26 +59,37 @@
testcases := []struct {
moduleName string
expectedJarFilename string
+ generate_xml bool
high_mem bool
}{
{
moduleName: "bar-stubs",
+ generate_xml: true,
expectedJarFilename: "android.jar",
high_mem: false,
},
{
moduleName: "bar-stubs-other",
+ generate_xml: true,
expectedJarFilename: "android.other.jar",
high_mem: true,
},
+ {
+ moduleName: "stubs-applying-api-versions",
+ generate_xml: false,
+ },
}
for _, c := range testcases {
m := ctx.ModuleForTests(c.moduleName, "android_common")
manifest := m.Output("metalava.sbox.textproto")
sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
- expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
- if actual := String(sboxProto.Commands[0].Command); !strings.Contains(actual, expected) {
- t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual)
+ cmdline := String(sboxProto.Commands[0].Command)
+ android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml)
+ if c.expectedJarFilename != "" {
+ expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+ if !strings.Contains(cmdline, expected) {
+ t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, cmdline)
+ }
}
metalava := m.Rule("metalava")
diff --git a/java/fuzz.go b/java/fuzz.go
index 257f343..b306991 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -15,14 +15,25 @@
package java
import (
- "github.com/google/blueprint/proptools"
"sort"
"strings"
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
+ "android/soong/cc"
"android/soong/fuzz"
)
+type jniProperties struct {
+ // list of jni libs
+ Jni_libs []string
+
+ // sanitization
+ Sanitizers []string
+}
+
func init() {
RegisterJavaFuzzBuildComponents(android.InitRegistrationContext)
}
@@ -35,11 +46,42 @@
type JavaFuzzLibrary struct {
Library
fuzzPackagedModule fuzz.FuzzPackagedModule
+ jniProperties jniProperties
+ jniFilePaths android.Paths
+}
+
+// IsSanitizerEnabledForJni implemented to make JavaFuzzLibrary implement
+// cc.JniSanitizeable. It returns a bool for whether a cc dependency should be
+// sanitized for the given sanitizer or not.
+func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool {
+ // TODO: once b/231370928 is resolved, please uncomment the loop
+ // for _, s := range j.jniProperties.Sanitizers {
+ // if sanitizerName == s {
+ // return true
+ // }
+ // }
+ return false
+}
+
+func (j *JavaFuzzLibrary) DepsMutator(mctx android.BottomUpMutatorContext) {
+ if len(j.jniProperties.Jni_libs) > 0 {
+ if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil {
+ config := &fuzz.FuzzConfig{}
+ j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config
+ }
+ // this will be used by the ingestion pipeline to determine the version
+ // of jazzer to add to the fuzzer package
+ j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true)
+
+ for _, target := range mctx.MultiTargets() {
+ sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
+ mctx.AddFarVariationDependencies(sharedLibVariations, cc.JniFuzzLibTag, j.jniProperties.Jni_libs...)
+ }
+ }
+ j.Library.DepsMutator(mctx)
}
func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- j.Library.GenerateAndroidBuildActions(ctx)
-
if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
}
@@ -55,6 +97,23 @@
android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
j.fuzzPackagedModule.Config = configPath
}
+
+ ctx.VisitDirectDepsWithTag(cc.JniFuzzLibTag, func(dep android.Module) {
+ sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
+ if sharedLibInfo.SharedLibrary != nil {
+ libPath := android.PathForModuleOut(ctx, sharedLibInfo.SharedLibrary.Base())
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: sharedLibInfo.SharedLibrary,
+ Output: libPath,
+ })
+ j.jniFilePaths = append(j.jniFilePaths, libPath)
+ } else {
+ ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+ }
+ })
+
+ j.Library.GenerateAndroidBuildActions(ctx)
}
// java_fuzz builds and links sources into a `.jar` file for the host.
@@ -65,7 +124,8 @@
module := &JavaFuzzLibrary{}
module.addHostProperties()
- module.Module.properties.Installable = proptools.BoolPtr(false)
+ module.AddProperties(&module.jniProperties)
+ module.Module.properties.Installable = proptools.BoolPtr(true)
module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
// java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
@@ -83,7 +143,7 @@
module.initModuleAndImport(module)
android.InitSdkAwareModule(module)
- InitJavaModule(module, android.HostSupported)
+ InitJavaModuleMultiTargets(module, android.HostSupported)
return module
}
@@ -106,26 +166,26 @@
ctx.VisitAllModules(func(module android.Module) {
// Discard non-fuzz targets.
- javaModule, ok := module.(*JavaFuzzLibrary)
+ javaFuzzModule, ok := module.(*JavaFuzzLibrary)
if !ok {
return
}
fuzzModuleValidator := fuzz.FuzzModule{
- javaModule.ModuleBase,
- javaModule.DefaultableModuleBase,
- javaModule.ApexModuleBase,
+ javaFuzzModule.ModuleBase,
+ javaFuzzModule.DefaultableModuleBase,
+ javaFuzzModule.ApexModuleBase,
}
- if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable {
+ if ok := fuzz.IsValid(fuzzModuleValidator); !ok {
return
}
hostOrTargetString := "target"
- if javaModule.Host() {
+ if javaFuzzModule.Host() {
hostOrTargetString = "host"
}
- archString := javaModule.Arch().ArchType.String()
+ archString := javaFuzzModule.Arch().ArchType.String()
archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
@@ -134,12 +194,17 @@
builder := android.NewRuleBuilder(pctx, ctx)
// Package the artifacts (data, corpus, config and dictionary into a zipfile.
- files = s.PackageArtifacts(ctx, module, javaModule.fuzzPackagedModule, archDir, builder)
+ files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
// Add .jar
- files = append(files, fuzz.FileToZip{javaModule.outputFile, ""})
+ files = append(files, fuzz.FileToZip{javaFuzzModule.outputFile, ""})
- archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs)
+ // Add jni .so files
+ for _, fPath := range javaFuzzModule.jniFilePaths {
+ files = append(files, fuzz.FileToZip{fPath, ""})
+ }
+
+ archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
if !ok {
return
}
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index cf063eb..0a2c945 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -15,13 +15,17 @@
package java
import (
- "android/soong/android"
"path/filepath"
+ "runtime"
"testing"
+
+ "android/soong/android"
+ "android/soong/cc"
)
var prepForJavaFuzzTest = android.GroupFixturePreparers(
PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcBuildComponents,
android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents),
)
@@ -32,6 +36,13 @@
srcs: ["a.java"],
libs: ["bar"],
static_libs: ["baz"],
+ jni_libs: [
+ "libjni",
+ ],
+ sanitizers: [
+ "address",
+ "fuzzer",
+ ],
}
java_library_host {
@@ -42,11 +53,21 @@
java_library_host {
name: "baz",
srcs: ["c.java"],
- }`)
+ }
+
+ cc_library_shared {
+ name: "libjni",
+ host_supported: true,
+ device_supported: false,
+ stl: "none",
+ }
+ `)
osCommonTarget := result.Config.BuildOSCommonTarget.String()
- javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac")
- combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac")
+
+ osCommonTargetWithSan := osCommonTarget + "_asan" + "_fuzzer"
+ javac := result.ModuleForTests("foo", osCommonTargetWithSan).Rule("javac")
+ combineJar := result.ModuleForTests("foo", osCommonTargetWithSan).Description("for javac")
if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
@@ -62,4 +83,18 @@
if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
}
+
+ ctx := result.TestContext
+ foo := ctx.ModuleForTests("foo", osCommonTargetWithSan).Module().(*JavaFuzzLibrary)
+
+ expected := "libjni.so"
+ if runtime.GOOS == "darwin" {
+ expected = "libjni.dylib"
+ }
+
+ fooJniFilePaths := foo.jniFilePaths
+ if len(fooJniFilePaths) != 1 || fooJniFilePaths[0].Rel() != expected {
+ t.Errorf(`expected foo test data relative path [%q], got %q`,
+ expected, fooJniFilePaths.Strings())
+ }
}
diff --git a/java/gen.go b/java/gen.go
index 445a2d8..1572bf0 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -44,7 +44,7 @@
})
)
-func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags string, deps android.Paths) android.Paths {
+func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlGlobalFlags string, aidlIndividualFlags map[string]string, deps android.Paths) android.Paths {
// Shard aidl files into groups of 50 to avoid having to recompile all of them if one changes and to avoid
// hitting command line length limits.
shards := android.ShardPaths(aidlFiles, 50)
@@ -61,15 +61,17 @@
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
- rule.Command().Text("FLAGS=' " + aidlFlags + "'")
+ rule.Command().Text("FLAGS=' " + aidlGlobalFlags + "'")
for _, aidlFile := range shard {
+ localFlag := aidlIndividualFlags[aidlFile.String()]
depFile := srcJarFile.InSameDir(ctx, aidlFile.String()+".d")
javaFile := outDir.Join(ctx, pathtools.ReplaceExtension(aidlFile.String(), "java"))
rule.Command().
Tool(ctx.Config().HostToolPath(ctx, "aidl")).
FlagWithDepFile("-d", depFile).
Flag("$FLAGS").
+ Flag(localFlag).
Input(aidlFile).
Output(javaFile).
Implicits(deps)
@@ -159,7 +161,14 @@
// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
if len(aidlSrcs) > 0 {
- srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+ individualFlags := make(map[string]string)
+ for _, aidlSrc := range aidlSrcs {
+ flags := j.individualAidlFlags(ctx, aidlSrc)
+ if flags != "" {
+ individualFlags[aidlSrc.String()] = flags
+ }
+ }
+ srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps)
outSrcFiles = append(outSrcFiles, srcJarFiles...)
}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 3af5f1c..cf9c7ad 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -65,6 +65,8 @@
type hiddenAPIModule interface {
android.Module
hiddenAPIIntf
+
+ MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
}
type hiddenAPIIntf interface {
@@ -148,7 +150,7 @@
// Create a copy of the dex jar which has been encoded with hiddenapi flags.
flagsCSV := hiddenAPISingletonPaths(ctx).flags
outputDir := android.PathForModuleOut(ctx, "hiddenapi").OutputPath
- encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, outputDir)
+ encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, android.SdkSpecNone, outputDir)
// Use the encoded dex jar from here onwards.
return encodedDex
@@ -246,7 +248,7 @@
// The encode dex rule requires unzipping, encoding and rezipping the classes.dex files along with
// all the resources from the input jar. It also ensures that if it was uncompressed in the input
// it stays uncompressed in the output.
-func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, outputDir android.OutputPath) android.OutputPath {
+func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, minSdkVersion android.SdkSpec, outputDir android.OutputPath) android.OutputPath {
// The output file has the same name as the input file and is in the output directory.
output := outputDir.Join(ctx, dexInput.Base())
@@ -274,6 +276,15 @@
hiddenapiFlags = "--no-force-assign-all"
}
+ // If the library is targeted for Q and/or R then make sure that they do not
+ // have any S+ flags encoded as that will break the runtime.
+ minApiLevel := minSdkVersion.ApiLevel
+ if !minApiLevel.IsNone() {
+ if minApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, "R")) {
+ hiddenapiFlags = hiddenapiFlags + " --max-hiddenapi-level=max-target-r"
+ }
+ }
+
ctx.Build(pctx, android.BuildParams{
Rule: hiddenAPIEncodeDexRule,
Description: "hiddenapi encode dex",
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 0cc960d..c90b2ff 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -295,6 +295,12 @@
return dexJar.Path()
}
+// HIDDENAPI_STUB_FLAGS_IMPL_FLAGS is the set of flags that identify implementation only signatures,
+// i.e. those signatures that are not part of any API (including the hidden API).
+var HIDDENAPI_STUB_FLAGS_IMPL_FLAGS = []string{}
+
+var HIDDENAPI_FLAGS_CSV_IMPL_FLAGS = []string{"blocked"}
+
// buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
//
// The rule is initialized but not built so that the caller can modify it and select an appropriate
@@ -345,7 +351,8 @@
// If there are stub flag files that have been generated by fragments on which this depends then
// use them to validate the stub flag file generated by the rules created by this method.
if len(stubFlagSubsets) > 0 {
- validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets)
+ validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets,
+ HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)
// Add the file that indicates that the file generated by this is valid.
//
@@ -904,7 +911,8 @@
// If there are flag files that have been generated by fragments on which this depends then use
// them to validate the flag file generated by the rules created by this method.
if len(flagSubsets) > 0 {
- validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets)
+ validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets,
+ HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)
// Add the file that indicates that the file generated by this is valid.
//
@@ -943,7 +951,9 @@
// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
// patterns that will select a subset of the monolithic flags.
-func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path, splitPackages []string, packagePrefixes []string) android.Path {
+func buildRuleSignaturePatternsFile(
+ ctx android.ModuleContext, flagsPath android.Path,
+ splitPackages []string, packagePrefixes []string, singlePackages []string) android.Path {
patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv")
// Create a rule to validate the output from the following rule.
rule := android.NewRuleBuilder(pctx, ctx)
@@ -959,19 +969,36 @@
FlagWithInput("--flags ", flagsPath).
FlagForEachArg("--split-package ", quotedSplitPackages).
FlagForEachArg("--package-prefix ", packagePrefixes).
+ FlagForEachArg("--single-package ", singlePackages).
FlagWithOutput("--output ", patternsFile)
rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns")
return patternsFile
}
-// buildRuleRemoveBlockedFlag creates a rule that will remove entries from the input path which
-// only have blocked flags. It will not remove entries that have blocked as well as other flags,
-// e.g. blocked,core-platform-api.
-func buildRuleRemoveBlockedFlag(ctx android.BuilderContext, name string, desc string, inputPath android.Path, filteredPath android.WritablePath) {
+// buildRuleRemoveSignaturesWithImplementationFlags creates a rule that will remove signatures from
+// the input flags file which have only the implementation flags, i.e. are not part of an API.
+//
+// The implementationFlags specifies the set of default flags that identifies the signature of a
+// private, implementation only, member. Signatures that match those flags are removed from the
+// flags as they are implementation only.
+//
+// This is used to remove implementation only signatures from the signature files that are persisted
+// in the sdk snapshot as the sdk snapshots should not include implementation details. The
+// signatures generated by this method will be compared by the buildRuleValidateOverlappingCsvFiles
+// method which treats any missing signatures as if they were implementation only signatures.
+func buildRuleRemoveSignaturesWithImplementationFlags(ctx android.BuilderContext,
+ name string, desc string, inputPath android.Path, filteredPath android.WritablePath,
+ implementationFlags []string) {
+
rule := android.NewRuleBuilder(pctx, ctx)
+ implementationFlagPattern := ""
+ for _, implementationFlag := range implementationFlags {
+ implementationFlagPattern = implementationFlagPattern + "," + implementationFlag
+ }
rule.Command().
- Text(`grep -vE "^[^,]+,blocked$"`).Input(inputPath).Text(">").Output(filteredPath).
+ Text(`grep -vE "^[^,]+` + implementationFlagPattern + `$"`).Input(inputPath).
+ Text(">").Output(filteredPath).
// Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
// something and 1 (build failure) when it does not and 2 (when it encounters an error).
// However, while it is unlikely it is not an error if this does not find any matches. The
@@ -983,7 +1010,14 @@
// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
-func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets) android.WritablePath {
+//
+// The implementationFlags specifies the set of default flags that identifies the signature of a
+// private, implementation only, member. A signature which is present in a monolithic flags subset
+// defined by SignatureCsvSubset but which is not present in the flags file from the corresponding
+// module is assumed to be an implementation only member and so must have these flags.
+func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string,
+ monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets,
+ implementationFlags []string) android.WritablePath {
// The file which is used to record that the flags file is valid.
validFile := pathForValidation(ctx, monolithicFilePath)
@@ -991,14 +1025,19 @@
rule := android.NewRuleBuilder(pctx, ctx)
command := rule.Command().
BuiltTool("verify_overlaps").
- Input(monolithicFilePath)
+ FlagWithInput("--monolithic-flags ", monolithicFilePath)
for _, subset := range csvSubsets {
command.
+ Flag("--module-flags ").
Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile).
Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile)
}
+ for _, implementationFlag := range implementationFlags {
+ command.FlagWithArg("--implementation-flag ", implementationFlag)
+ }
+
// If validation passes then update the file that records that.
command.Text("&& touch").Output(validFile)
rule.Build(name+"Validation", desc+" validation")
@@ -1065,19 +1104,23 @@
for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
bootDexInfo := bootDexInfoByModule[name]
unencodedDex := bootDexInfo.path
- encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir)
+ encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, bootDexInfo.minSdkVersion, outputDir)
encodedBootDexJarsByModule[name] = encodedDex
}
// Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
// compared against the monolithic stub flags.
filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
- buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredStubFlags", "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV)
+ buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredStubFlags",
+ "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV,
+ HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)
// Generate the filtered-flags.csv file which contains the filtered flags that will be compared
// against the monolithic flags.
filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
- buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredFlags", "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV)
+ buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredFlags",
+ "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV,
+ HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)
// Store the paths in the info for use by other modules and sdk snapshot generation.
output := HiddenAPIOutput{
@@ -1145,6 +1188,9 @@
// Indicates whether the dex jar needs uncompressing before encoding.
uncompressDex bool
+
+ // The minimum sdk version that the dex jar will be used on.
+ minSdkVersion android.SdkSpec
}
// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
@@ -1170,6 +1216,7 @@
bootDexJarsByModule[module.Name()] = bootDexInfo{
path: bootDexJar,
uncompressDex: *hiddenAPIModule.uncompressDex(),
+ minSdkVersion: hiddenAPIModule.MinSdkVersion(ctx),
}
}
diff --git a/java/java.go b/java/java.go
index fef9912..0dfb968 100644
--- a/java/java.go
+++ b/java/java.go
@@ -300,19 +300,11 @@
type usesLibraryDependencyTag struct {
dependencyTag
-
- // SDK version in which the library appared as a standalone library.
- sdkVersion int
-
- // If the dependency is optional or required.
- optional bool
-
- // Whether this is an implicit dependency inferred by Soong, or an explicit one added via
- // `uses_libs`/`optional_uses_libs` properties.
- implicit bool
+ sdkVersion int // SDK version in which the library appared as a standalone library.
+ optional bool // If the dependency is optional or required.
}
-func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag {
+func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDependencyTag {
return usesLibraryDependencyTag{
dependencyTag: dependencyTag{
name: fmt.Sprintf("uses-library-%d", sdkVersion),
@@ -320,7 +312,6 @@
},
sdkVersion: sdkVersion,
optional: optional,
- implicit: implicit,
}
}
@@ -351,6 +342,11 @@
syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
jniInstallTag = installDependencyTag{name: "jni install"}
binaryInstallTag = installDependencyTag{name: "binary install"}
+ usesLibReqTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false)
+ usesLibOptTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true)
+ usesLibCompat28OptTag = makeUsesLibraryDependencyTag(28, true)
+ usesLibCompat29ReqTag = makeUsesLibraryDependencyTag(29, false)
+ usesLibCompat30OptTag = makeUsesLibraryDependencyTag(30, true)
)
func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -421,9 +417,25 @@
}
type deps struct {
- classpath classpath
- java9Classpath classpath
- bootClasspath classpath
+ // bootClasspath is the list of jars that form the boot classpath (generally the java.* and
+ // android.* classes) for tools that still use it. javac targeting 1.9 or higher uses
+ // systemModules and java9Classpath instead.
+ bootClasspath classpath
+
+ // classpath is the list of jars that form the classpath for javac and kotlinc rules. It
+ // contains header jars for all static and non-static dependencies.
+ classpath classpath
+
+ // dexClasspath is the list of jars that form the classpath for d8 and r8 rules. It contains
+ // header jars for all non-static dependencies. Static dependencies have already been
+ // combined into the program jar.
+ dexClasspath classpath
+
+ // java9Classpath is the list of jars that will be added to the classpath when targeting
+ // 1.9 or higher. It generally contains the android.* classes, while the java.* classes
+ // are provided by systemModules.
+ java9Classpath classpath
+
processorPath classpath
errorProneProcessorPath classpath
processorClasses []string
@@ -456,6 +468,12 @@
return normalizeJavaVersion(ctx, javaVersion)
} else if ctx.Device() {
return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
+ } else if ctx.Config().TargetsJava17() {
+ // Temporary experimental flag to be able to try and build with
+ // java version 17 options. The flag, if used, just sets Java
+ // 17 as the default version, leaving any components that
+ // target an older version intact.
+ return JAVA_VERSION_17
} else {
return JAVA_VERSION_11
}
@@ -470,6 +488,7 @@
JAVA_VERSION_8 = 8
JAVA_VERSION_9 = 9
JAVA_VERSION_11 = 11
+ JAVA_VERSION_17 = 17
)
func (v javaVersion) String() string {
@@ -484,6 +503,8 @@
return "1.9"
case JAVA_VERSION_11:
return "11"
+ case JAVA_VERSION_17:
+ return "17"
default:
return "unsupported"
}
@@ -506,8 +527,10 @@
return JAVA_VERSION_9
case "11":
return JAVA_VERSION_11
- case "10":
- ctx.PropertyErrorf("java_version", "Java language levels 10 is not supported")
+ case "17":
+ return JAVA_VERSION_11
+ case "10", "12", "13", "14", "15", "16":
+ ctx.PropertyErrorf("java_version", "Java language level %s is not supported", javaVersion)
return JAVA_VERSION_UNSUPPORTED
default:
ctx.PropertyErrorf("java_version", "Unrecognized Java language level")
@@ -580,12 +603,14 @@
}
j.checkSdkVersions(ctx)
- j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
- ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
- j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
- setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
- j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
- j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+ if ctx.Device() {
+ j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+ ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
+ j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
+ setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
+ j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+ j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+ }
j.compile(ctx, nil)
// Collect the module directory for IDE info in java/jdeps.go.
@@ -1233,10 +1258,10 @@
}
func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
- if ctx.Arch().ArchType == android.Common || ctx.BazelConversionMode() {
+ if ctx.Arch().ArchType == android.Common {
j.deps(ctx)
}
- if ctx.Arch().ArchType != android.Common || ctx.BazelConversionMode() {
+ if ctx.Arch().ArchType != android.Common {
// These dependencies ensure the host installation rules will install the jar file and
// the jni libraries when the wrapper is installed.
ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
@@ -1458,7 +1483,10 @@
if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
switch tag {
- case libTag, staticLibTag:
+ case libTag:
+ flags.classpath = append(flags.classpath, dep.HeaderJars...)
+ flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
+ case staticLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars...)
case bootClasspathTag:
flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
@@ -1706,6 +1734,7 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
android.InitApexModule(module)
+ android.InitBazelModule(module)
InitJavaModule(module, android.HostSupported)
return module
}
@@ -1976,10 +2005,8 @@
depTag := ctx.OtherModuleDependencyTag(depModule)
if depTag == libTag {
// Ok, propagate <uses-library> through non-static library dependencies.
- } else if tag, ok := depTag.(usesLibraryDependencyTag); ok &&
- tag.sdkVersion == dexpreopt.AnySdkVersion && tag.implicit {
- // Ok, propagate <uses-library> through non-compatibility implicit <uses-library>
- // dependencies.
+ } else if tag, ok := depTag.(usesLibraryDependencyTag); ok && tag.sdkVersion == dexpreopt.AnySdkVersion {
+ // Ok, propagate <uses-library> through non-compatibility <uses-library> dependencies.
} else if depTag == staticLibTag {
// Propagate <uses-library> through static library dependencies, unless it is a component
// library (such as stubs). Component libraries have a dependency on their SDK library,
@@ -1997,31 +2024,193 @@
// <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies
// from its CLC should be added to the current CLC.
if sdkLib != nil {
- clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, true,
+ clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false,
dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
} else {
clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
}
}
-type javaLibraryAttributes struct {
+type javaResourcesAttributes struct {
+ Resources bazel.LabelListAttribute
+ Resource_strip_prefix *string
+}
+
+func (m *Library) convertJavaResourcesAttributes(ctx android.TopDownMutatorContext) *javaResourcesAttributes {
+ var resources bazel.LabelList
+ var resourceStripPrefix *string
+
+ if m.properties.Java_resources != nil {
+ resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources))
+ }
+
+ //TODO(b/179889880) handle case where glob includes files outside package
+ resDeps := ResourceDirsToFiles(
+ ctx,
+ m.properties.Java_resource_dirs,
+ m.properties.Exclude_java_resource_dirs,
+ m.properties.Exclude_java_resources,
+ )
+
+ for i, resDep := range resDeps {
+ dir, files := resDep.dir, resDep.files
+
+ resources.Append(bazel.MakeLabelList(android.RootToModuleRelativePaths(ctx, files)))
+
+ // Bazel includes the relative path from the WORKSPACE root when placing the resource
+ // inside the JAR file, so we need to remove that prefix
+ resourceStripPrefix = proptools.StringPtr(dir.String())
+ if i > 0 {
+ // TODO(b/226423379) allow multiple resource prefixes
+ ctx.ModuleErrorf("bp2build does not support more than one directory in java_resource_dirs (b/226423379)")
+ }
+ }
+
+ return &javaResourcesAttributes{
+ Resources: bazel.MakeLabelListAttribute(resources),
+ Resource_strip_prefix: resourceStripPrefix,
+ }
+}
+
+type javaCommonAttributes struct {
+ *javaResourcesAttributes
Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
+ Plugins bazel.LabelListAttribute
Javacopts bazel.StringListAttribute
}
-func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
- srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
- attrs := &javaLibraryAttributes{
- Srcs: srcs,
+type javaDependencyLabels struct {
+ // Dependencies which DO NOT contribute to the API visible to upstream dependencies.
+ Deps bazel.LabelListAttribute
+ // Dependencies which DO contribute to the API visible to upstream dependencies.
+ StaticDeps bazel.LabelListAttribute
+}
+
+// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules
+// and also separates dependencies into dynamic dependencies and static dependencies.
+// Each corresponding Bazel target type, can have a different method for handling
+// dynamic vs. static dependencies, and so these are returned to the calling function.
+type eventLogTagsAttributes struct {
+ Srcs bazel.LabelListAttribute
+}
+
+func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) {
+ var srcs bazel.LabelListAttribute
+ archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*CommonProperties); ok {
+ archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Srcs, archProps.Exclude_srcs)
+ srcs.SetSelectValue(axis, config, archSrcs)
+ }
+ }
}
+ javaSrcPartition := "java"
+ protoSrcPartition := "proto"
+ logtagSrcPartition := "logtag"
+ srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
+ javaSrcPartition: bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
+ logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}},
+ protoSrcPartition: android.ProtoSrcLabelPartition,
+ })
+
+ javaSrcs := srcPartitions[javaSrcPartition]
+
+ var logtagsSrcs bazel.LabelList
+ if !srcPartitions[logtagSrcPartition].IsEmpty() {
+ logtagsLibName := m.Name() + "_logtags"
+ logtagsSrcs = bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}})
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "event_log_tags",
+ Bzl_load_location: "//build/make/tools:event_log_tags.bzl",
+ },
+ android.CommonAttributes{Name: logtagsLibName},
+ &eventLogTagsAttributes{
+ Srcs: srcPartitions[logtagSrcPartition],
+ },
+ )
+ }
+ javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs))
+
+ var javacopts []string
if m.properties.Javacflags != nil {
- attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
+ javacopts = append(javacopts, m.properties.Javacflags...)
+ }
+ if m.properties.Java_version != nil {
+ javaVersion := normalizeJavaVersion(ctx, *m.properties.Java_version).String()
+ javacopts = append(javacopts, fmt.Sprintf("-source %s -target %s", javaVersion, javaVersion))
}
+ epEnabled := m.properties.Errorprone.Enabled
+ //TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
+ if Bool(epEnabled) {
+ javacopts = append(javacopts, m.properties.Errorprone.Javacflags...)
+ }
+
+ commonAttrs := &javaCommonAttributes{
+ Srcs: javaSrcs,
+ javaResourcesAttributes: m.convertJavaResourcesAttributes(ctx),
+ Plugins: bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
+ ),
+ Javacopts: bazel.MakeStringListAttribute(javacopts),
+ }
+
+ depLabels := &javaDependencyLabels{}
+
+ var deps bazel.LabelList
if m.properties.Libs != nil {
- attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
+ deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
+ }
+
+ var staticDeps bazel.LabelList
+ if m.properties.Static_libs != nil {
+ staticDeps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
+ }
+
+ protoDepLabel := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition])
+ // Soong does not differentiate between a java_library and the Bazel equivalent of
+ // a java_proto_library + proto_library pair. Instead, in Soong proto sources are
+ // listed directly in the srcs of a java_library, and the classes produced
+ // by protoc are included directly in the resulting JAR. Thus upstream dependencies
+ // that depend on a java_library with proto sources can link directly to the protobuf API,
+ // and so this should be a static dependency.
+ staticDeps.Add(protoDepLabel)
+
+ depLabels.Deps = bazel.MakeLabelListAttribute(deps)
+ depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
+
+ return commonAttrs, depLabels
+}
+
+type javaLibraryAttributes struct {
+ *javaCommonAttributes
+ Deps bazel.LabelListAttribute
+ Exports bazel.LabelListAttribute
+}
+
+func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
+ commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
+
+ deps := depLabels.Deps
+ if !commonAttrs.Srcs.IsEmpty() {
+ deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them
+
+ sdkVersion := m.SdkVersion(ctx)
+ if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
+ // TODO(b/220869005) remove forced dependency on current public android.jar
+ deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import"))
+ }
+ } else if !depLabels.Deps.IsEmpty() {
+ ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
+ }
+
+ attrs := &javaLibraryAttributes{
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ Exports: depLabels.StaticDeps,
}
props := bazel.BazelTargetModuleProperties{
@@ -2033,15 +2222,30 @@
}
type javaBinaryHostAttributes struct {
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Main_class string
- Jvm_flags bazel.StringListAttribute
- Javacopts bazel.StringListAttribute
+ *javaCommonAttributes
+ Deps bazel.LabelListAttribute
+ Runtime_deps bazel.LabelListAttribute
+ Main_class string
+ Jvm_flags bazel.StringListAttribute
}
// JavaBinaryHostBp2Build is for java_binary_host bp2build.
func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
+ commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
+
+ deps := depLabels.Deps
+ deps.Append(depLabels.StaticDeps)
+ if m.binaryProperties.Jni_libs != nil {
+ deps.Append(bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs)))
+ }
+
+ var runtimeDeps bazel.LabelListAttribute
+ if commonAttrs.Srcs.IsEmpty() {
+ // if there are no sources, then the dependencies can only be used at runtime
+ runtimeDeps = deps
+ deps = bazel.LabelListAttribute{}
+ }
+
mainClass := ""
if m.binaryProperties.Main_class != nil {
mainClass = *m.binaryProperties.Main_class
@@ -2053,26 +2257,12 @@
}
mainClass = mainClassInManifest
}
- srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+
attrs := &javaBinaryHostAttributes{
- Srcs: srcs,
- Main_class: mainClass,
- }
-
- if m.properties.Javacflags != nil {
- attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
- }
-
- // Attribute deps
- deps := []string{}
- if m.properties.Static_libs != nil {
- deps = append(deps, m.properties.Static_libs...)
- }
- if m.binaryProperties.Jni_libs != nil {
- deps = append(deps, m.binaryProperties.Jni_libs...)
- }
- if len(deps) > 0 {
- attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, deps))
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ Runtime_deps: runtimeDeps,
+ Main_class: mainClass,
}
// Attribute jvm_flags
@@ -2115,8 +2305,16 @@
// java_import bp2Build converter.
func (i *Import) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- //TODO(b/209577426): Support multiple arch variants
- jars := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, i.properties.Jars, []string(nil)))
+ var jars bazel.LabelListAttribute
+ archVariantProps := i.GetArchVariantProperties(ctx, &ImportProperties{})
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*ImportProperties); ok {
+ archJars := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Jars, []string(nil))
+ jars.SetSelectValue(axis, config, archJars)
+ }
+ }
+ }
attrs := &bazelJavaImportAttributes{
Jars: jars,
diff --git a/java/java_resources.go b/java/java_resources.go
index 787d74a..b0dc5a1 100644
--- a/java/java_resources.go
+++ b/java/java_resources.go
@@ -33,8 +33,13 @@
"**/*~",
}
-func ResourceDirsToJarArgs(ctx android.ModuleContext,
- resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (args []string, deps android.Paths) {
+type resourceDeps struct {
+ dir android.Path
+ files android.Paths
+}
+
+func ResourceDirsToFiles(ctx android.BaseModuleContext,
+ resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (deps []resourceDeps) {
var excludeDirs []string
var excludeFiles []string
@@ -55,21 +60,36 @@
dirs := ctx.Glob(android.PathForSource(ctx, ctx.ModuleDir()).Join(ctx, resourceDir).String(), excludeDirs)
for _, dir := range dirs {
files := ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), excludeFiles)
+ deps = append(deps, resourceDeps{
+ dir: dir,
+ files: files,
+ })
+ }
+ }
+ return deps
+}
+
+func ResourceDirsToJarArgs(ctx android.ModuleContext,
+ resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (args []string, deps android.Paths) {
+ resDeps := ResourceDirsToFiles(ctx, resourceDirs, excludeResourceDirs, excludeResourceFiles)
+
+ for _, resDep := range resDeps {
+ dir, files := resDep.dir, resDep.files
+
+ if len(files) > 0 {
+ args = append(args, "-C", dir.String())
deps = append(deps, files...)
- if len(files) > 0 {
- args = append(args, "-C", dir.String())
-
- for _, f := range files {
- path := f.String()
- if !strings.HasPrefix(path, dir.String()) {
- panic(fmt.Errorf("path %q does not start with %q", path, dir))
- }
- args = append(args, "-f", pathtools.MatchEscape(path))
+ for _, f := range files {
+ path := f.String()
+ if !strings.HasPrefix(path, dir.String()) {
+ panic(fmt.Errorf("path %q does not start with %q", path, dir))
}
+ args = append(args, "-f", pathtools.MatchEscape(path))
}
}
+
}
return args, deps
diff --git a/java/java_test.go b/java/java_test.go
index 21c76b6..56a4248 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -723,9 +723,9 @@
t.Errorf("atestNoOptimize should not optimize APK")
}
- atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("r8")
+ atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("d8")
if atestDefault.Output == nil {
- t.Errorf("atestDefault should optimize APK")
+ t.Errorf("atestDefault should not optimize APK")
}
}
@@ -973,7 +973,7 @@
fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
- android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["classpath"], fooHeaderJar)
+ android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["turbineFlags"], fooHeaderJar)
android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
android.AssertPathsRelativeToTopEquals(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs)
android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar")
@@ -1333,6 +1333,42 @@
}
}
+func TestAidlEnforcePermissions(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl"],
+ aidl: { enforce_permissions: true },
+ }
+ `)
+
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "-Wmissing-permission-annotation -Werror"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
+func TestAidlEnforcePermissionsException(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl", "aidl/foo/IFoo2.aidl"],
+ aidl: { enforce_permissions: true, enforce_permissions_exceptions: ["aidl/foo/IFoo2.aidl"] },
+ }
+ `)
+
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "$$FLAGS -Wmissing-permission-annotation -Werror aidl/foo/IFoo.aidl"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+ expectedAidlFlag = "$$FLAGS aidl/foo/IFoo2.aidl"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
func TestDataNativeBinaries(t *testing.T) {
ctx, _ := testJava(t, `
java_test_host {
diff --git a/java/jdeps.go b/java/jdeps.go
index eff9a31..3734335 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -71,6 +71,8 @@
dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars)
dpInfo.Paths = android.FirstUniqueStrings(dpInfo.Paths)
+ dpInfo.Static_libs = android.FirstUniqueStrings(dpInfo.Static_libs)
+ dpInfo.Libs = android.FirstUniqueStrings(dpInfo.Libs)
moduleInfos[name] = dpInfo
mkProvider, ok := module.(android.AndroidMkDataProvider)
diff --git a/java/kotlin.go b/java/kotlin.go
index e4f1bc1..903c624 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -28,16 +28,20 @@
var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports{Goma: true},
blueprint.RuleParams{
- Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` +
- `mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` +
+ Command: `rm -rf "$classesDir" "$headerClassesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` +
+ `mkdir -p "$classesDir" "$headerClassesDir" "$srcJarDir" "$emptyDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
- `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
- `$kotlincFlags -jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile ` +
- `-kotlin-home $emptyDir && ` +
- `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` +
+ `${config.KotlincCmd} ${config.KotlincGlobalFlags} ` +
+ ` ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
+ ` $kotlincFlags -jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile ` +
+ ` -kotlin-home $emptyDir ` +
+ ` -Xplugin=${config.KotlinAbiGenPluginJar} ` +
+ ` -P plugin:org.jetbrains.kotlin.jvm.abi:outputDir=$headerClassesDir && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir -write_if_changed && ` +
+ `${config.SoongZipCmd} -jar -o $headerJar -C $headerClassesDir -D $headerClassesDir -write_if_changed && ` +
`rm -rf "$srcJarDir"`,
CommandDeps: []string{
"${config.KotlincCmd}",
@@ -48,15 +52,17 @@
"${config.KotlinStdlibJar}",
"${config.KotlinTrove4jJar}",
"${config.KotlinAnnotationJar}",
+ "${config.KotlinAbiGenPluginJar}",
"${config.GenKotlinBuildFileCmd}",
"${config.SoongZipCmd}",
"${config.ZipSyncCmd}",
},
Rspfile: "$out.rsp",
RspfileContent: `$in`,
+ Restat: true,
},
"kotlincFlags", "classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "classesDir",
- "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name")
+ "headerClassesDir", "headerJar", "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name")
func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Paths) android.OptionalPath {
if len(commonSrcFiles) > 0 {
@@ -75,7 +81,7 @@
}
// kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile.
-func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath,
+func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath,
srcFiles, commonSrcFiles, srcJars android.Paths,
flags javaBuilderFlags) {
@@ -96,17 +102,20 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: kotlinc,
- Description: "kotlinc",
- Output: outputFile,
- Inputs: srcFiles,
- Implicits: deps,
+ Rule: kotlinc,
+ Description: "kotlinc",
+ Output: outputFile,
+ ImplicitOutput: headerOutputFile,
+ Inputs: srcFiles,
+ Implicits: deps,
Args: map[string]string{
"classpath": flags.kotlincClasspath.FormJavaClassPath(""),
"kotlincFlags": flags.kotlincFlags,
"commonSrcFilesArg": commonSrcFilesArg,
"srcJars": strings.Join(srcJars.Strings(), " "),
"classesDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
+ "headerClassesDir": android.PathForModuleOut(ctx, "kotlinc", "header_classes").String(),
+ "headerJar": headerOutputFile.String(),
"srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
"kotlinBuildFile": android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
"emptyDir": android.PathForModuleOut(ctx, "kotlinc", "empty").String(),
@@ -117,7 +126,7 @@
})
}
-var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true},
+var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupports{Goma: true},
blueprint.RuleParams{
Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` +
`mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` +
@@ -125,19 +134,19 @@
`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
` --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
- `${config.KotlincCmd} ${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` +
+ `${config.KotlincCmd} ${config.KotlincGlobalFlags} ` +
+ `${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` +
`${config.JavacHeapFlags} $kotlincFlags -Xplugin=${config.KotlinKaptJar} ` +
`-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` +
`-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` +
`-P plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptDir/stubs ` +
`-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true ` +
- `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubs ` +
`-P plugin:org.jetbrains.kotlin.kapt3:javacArguments=$encodedJavacFlags ` +
`$kaptProcessorPath ` +
`$kaptProcessor ` +
`-Xbuild-file=$kotlinBuildFile && ` +
- `${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources && ` +
- `${config.SoongZipCmd} -jar -o $classesJarOut -C $kaptDir/classes -D $kaptDir/classes && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
`rm -rf "$srcJarDir"`,
CommandDeps: []string{
"${config.KotlincCmd}",
@@ -166,6 +175,7 @@
var deps android.Paths
deps = append(deps, flags.kotlincClasspath...)
+ deps = append(deps, flags.kotlincDeps...)
deps = append(deps, srcJars...)
deps = append(deps, flags.processorPath...)
deps = append(deps, commonSrcFiles...)
@@ -195,13 +205,14 @@
kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName())
kotlinName = strings.ReplaceAll(kotlinName, "/", "__")
+ // First run kapt to generate .java stubs from .kt files
+ kaptStubsJar := android.PathForModuleOut(ctx, "kapt", "stubs.jar")
ctx.Build(pctx, android.BuildParams{
- Rule: kapt,
- Description: "kapt",
- Output: srcJarOutputFile,
- ImplicitOutput: resJarOutputFile,
- Inputs: srcFiles,
- Implicits: deps,
+ Rule: kaptStubs,
+ Description: "kapt stubs",
+ Output: kaptStubsJar,
+ Inputs: srcFiles,
+ Implicits: deps,
Args: map[string]string{
"classpath": flags.kotlincClasspath.FormJavaClassPath(""),
"kotlincFlags": flags.kotlincFlags,
@@ -217,6 +228,11 @@
"classesJarOut": resJarOutputFile.String(),
},
})
+
+ // Then run turbine to perform annotation processing on the stubs and any .java srcFiles.
+ javaSrcFiles := srcFiles.FilterByExt(".java")
+ turbineSrcJars := append(android.Paths{kaptStubsJar}, srcJars...)
+ TurbineApt(ctx, srcJarOutputFile, resJarOutputFile, javaSrcFiles, turbineSrcJars, flags)
}
// kapt converts a list of key, value pairs into a base64 encoded Java serialization, which is what kapt expects.
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index cac0af3..491ce29 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -42,9 +42,18 @@
}
`)
+ kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
+ Output("turbine-combined/kotlin-stdlib.jar").Output
+ kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
+ Output("turbine-combined/kotlin-annotations.jar").Output
+
fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
+ fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
+
+ fooKotlincClasses := fooKotlinc.Output
+ fooKotlincHeaderClasses := fooKotlinc.ImplicitOutput
if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
fooKotlinc.Inputs[1].String() != "b.kt" {
@@ -55,17 +64,31 @@
t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
}
- if !strings.Contains(fooJavac.Args["classpath"], fooKotlinc.Output.String()) {
+ if !strings.Contains(fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) {
t.Errorf("foo classpath %v does not contain %q",
- fooJavac.Args["classpath"], fooKotlinc.Output.String())
+ fooJavac.Args["classpath"], fooKotlincHeaderClasses.String())
}
- if !inList(fooKotlinc.Output.String(), fooJar.Inputs.Strings()) {
+ if !inList(fooKotlincClasses.String(), fooJar.Inputs.Strings()) {
t.Errorf("foo jar inputs %v does not contain %q",
- fooJar.Inputs.Strings(), fooKotlinc.Output.String())
+ fooJar.Inputs.Strings(), fooKotlincClasses.String())
}
- fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
+ if !inList(kotlinStdlib.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinStdlib.String())
+ }
+
+ if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinAnnotations.String())
+ }
+
+ if !inList(fooKotlincHeaderClasses.String(), fooHeaderJar.Inputs.Strings()) {
+ t.Errorf("foo header jar inputs %v does not contain %q",
+ fooHeaderJar.Inputs.Strings(), fooKotlincHeaderClasses.String())
+ }
+
bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
@@ -117,51 +140,71 @@
buildOS := ctx.Config().BuildOS.String()
- kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
- kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ foo := ctx.ModuleForTests("foo", "android_common")
+ kaptStubs := foo.Rule("kapt")
+ turbineApt := foo.Description("turbine apt")
+ kotlinc := foo.Rule("kotlinc")
+ javac := foo.Rule("javac")
bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
// Test that the kotlin and java sources are passed to kapt and kotlinc
- if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" {
- t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs)
+ if len(kaptStubs.Inputs) != 2 || kaptStubs.Inputs[0].String() != "a.java" || kaptStubs.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kaptStubs.Inputs)
}
if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
}
- // Test that only the java sources are passed to javac
+ // Test that only the java sources are passed to turbine-apt and javac
+ if len(turbineApt.Inputs) != 1 || turbineApt.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo turbine apt inputs %v != ["a.java"]`, turbineApt.Inputs)
+ }
if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
}
- // Test that the kapt srcjar is a dependency of kotlinc and javac rules
- if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) {
- t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings())
- }
- if !inList(kapt.Output.String(), javac.Implicits.Strings()) {
- t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings())
+ // Test that the kapt stubs jar is a dependency of turbine-apt
+ if !inList(kaptStubs.Output.String(), turbineApt.Implicits.Strings()) {
+ t.Errorf("expected %q in turbine-apt implicits %v", kaptStubs.Output.String(), kotlinc.Implicits.Strings())
}
- // Test that the kapt srcjar is extracted by the kotlinc and javac rules
- if kotlinc.Args["srcJars"] != kapt.Output.String() {
- t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ // Test that the turbine-apt srcjar is a dependency of kotlinc and javac rules
+ if !inList(turbineApt.Output.String(), kotlinc.Implicits.Strings()) {
+ t.Errorf("expected %q in kotlinc implicits %v", turbineApt.Output.String(), kotlinc.Implicits.Strings())
}
- if javac.Args["srcJars"] != kapt.Output.String() {
- t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ if !inList(turbineApt.Output.String(), javac.Implicits.Strings()) {
+ t.Errorf("expected %q in javac implicits %v", turbineApt.Output.String(), javac.Implicits.Strings())
+ }
+
+ // Test that the turbine-apt srcjar is extracted by the kotlinc and javac rules
+ if kotlinc.Args["srcJars"] != turbineApt.Output.String() {
+ t.Errorf("expected %q in kotlinc srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"])
+ }
+ if javac.Args["srcJars"] != turbineApt.Output.String() {
+ t.Errorf("expected %q in javac srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"])
}
// Test that the processors are passed to kapt
expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
" -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
- if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
- t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
+ if kaptStubs.Args["kaptProcessorPath"] != expectedProcessorPath {
+ t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kaptStubs.Args["kaptProcessorPath"])
}
expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
- if kapt.Args["kaptProcessor"] != expectedProcessor {
- t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
+ if kaptStubs.Args["kaptProcessor"] != expectedProcessor {
+ t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kaptStubs.Args["kaptProcessor"])
+ }
+
+ // Test that the processors are passed to turbine-apt
+ expectedProcessorPath = "--processorpath " + bar + " " + baz
+ if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessorPath) {
+ t.Errorf("expected turbine-apt processorpath %q, got %q", expectedProcessorPath, turbineApt.Args["turbineFlags"])
+ }
+ expectedProcessor = "--processors com.bar com.baz"
+ if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessor) {
+ t.Errorf("expected turbine-apt processor %q, got %q", expectedProcessor, turbineApt.Args["turbineFlags"])
}
// Test that the processors are not passed to javac
@@ -297,6 +340,7 @@
java_library {
name: "withcompose",
srcs: ["a.kt"],
+ plugins: ["plugin"],
static_libs: ["androidx.compose.runtime_runtime"],
}
@@ -304,6 +348,10 @@
name: "nocompose",
srcs: ["a.kt"],
}
+
+ java_plugin {
+ name: "plugin",
+ }
`)
buildOS := result.Config.BuildOS.String()
@@ -318,6 +366,9 @@
android.AssertStringDoesContain(t, "missing compose compiler plugin",
withCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
+ android.AssertStringListContains(t, "missing kapt compose compiler dependency",
+ withCompose.Rule("kapt").Implicits.Strings(), composeCompiler.String())
+
android.AssertStringListDoesNotContain(t, "unexpected compose compiler dependency",
noCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index e3396c1..8e22491 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -81,7 +81,6 @@
"ds-car-docs", // for AAOS API documentation only
"DynamicSystemInstallationService",
"EmergencyInfo-lib",
- "ethernet-service",
"EthernetServiceTests",
"ExternalStorageProvider",
"face-V1-0-javalib",
diff --git a/java/lint.go b/java/lint.go
index 7845c33..22c9ec4 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -75,9 +75,9 @@
extraLintCheckJars android.Paths
test bool
library bool
- minSdkVersion string
- targetSdkVersion string
- compileSdkVersion string
+ minSdkVersion android.ApiLevel
+ targetSdkVersion android.ApiLevel
+ compileSdkVersion android.ApiLevel
compileSdkKind android.SdkKind
javaLanguageLevel string
kotlinLanguageLevel string
@@ -102,12 +102,12 @@
lintOutputs() *lintOutputs
}
-type lintDepSetsIntf interface {
+type LintDepSetsIntf interface {
LintDepSets() LintDepSets
// Methods used to propagate strict_updatability_linting values.
- getStrictUpdatabilityLinting() bool
- setStrictUpdatabilityLinting(bool)
+ GetStrictUpdatabilityLinting() bool
+ SetStrictUpdatabilityLinting(bool)
}
type LintDepSets struct {
@@ -158,15 +158,15 @@
return l.outputs.depSets
}
-func (l *linter) getStrictUpdatabilityLinting() bool {
+func (l *linter) GetStrictUpdatabilityLinting() bool {
return BoolDefault(l.properties.Lint.Strict_updatability_linting, false)
}
-func (l *linter) setStrictUpdatabilityLinting(strictLinting bool) {
+func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) {
l.properties.Lint.Strict_updatability_linting = &strictLinting
}
-var _ lintDepSetsIntf = (*linter)(nil)
+var _ LintDepSetsIntf = (*linter)(nil)
var _ lintOutputsIntf = (*linter)(nil)
@@ -273,7 +273,7 @@
cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
- if l.getStrictUpdatabilityLinting() {
+ if l.GetStrictUpdatabilityLinting() {
// Verify the module does not baseline issues that endanger safe updatability.
if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
cmd.FlagWithInput("--baseline ", baselinePath.Path())
@@ -300,7 +300,7 @@
Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
Text(`echo " android:versionCode='1' android:versionName='1' >" &&`).
Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
- l.minSdkVersion, l.targetSdkVersion).
+ l.minSdkVersion.String(), l.targetSdkVersion.String()).
Text(`echo "</manifest>"`).
Text(") >").Output(manifestPath)
@@ -325,7 +325,7 @@
return
}
- if l.minSdkVersion != l.compileSdkVersion {
+ if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
if len(filtered) != 0 {
@@ -382,7 +382,7 @@
depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
- if depLint, ok := dep.(lintDepSetsIntf); ok {
+ if depLint, ok := dep.(LintDepSetsIntf); ok {
depSetsBuilder.Transitive(depLint.LintDepSets())
}
})
@@ -427,7 +427,7 @@
FlagWithOutput("--html ", html).
FlagWithOutput("--text ", text).
FlagWithOutput("--xml ", xml).
- FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
+ FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
FlagWithArg("--java-language-level ", l.javaLanguageLevel).
FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
@@ -524,10 +524,18 @@
return
}
- frameworkDocStubs := findModuleOrErr(ctx, "framework-doc-stubs")
- if frameworkDocStubs == nil {
+ apiVersionsDb := findModuleOrErr(ctx, "api_versions_public")
+ if apiVersionsDb == nil {
if !ctx.Config().AllowMissingDependencies() {
- ctx.Errorf("lint: missing framework-doc-stubs")
+ ctx.Errorf("lint: missing module api_versions_public")
+ }
+ return
+ }
+
+ sdkAnnotations := findModuleOrErr(ctx, "sdk-annotations.zip")
+ if sdkAnnotations == nil {
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.Errorf("lint: missing module sdk-annotations.zip")
}
return
}
@@ -542,13 +550,13 @@
ctx.Build(pctx, android.BuildParams{
Rule: android.CpIfChanged,
- Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
+ Input: android.OutputFileForModule(ctx, sdkAnnotations, ""),
Output: copiedAnnotationsZipPath(ctx),
})
ctx.Build(pctx, android.BuildParams{
Rule: android.CpIfChanged,
- Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
+ Input: android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"),
})
@@ -660,10 +668,10 @@
// Enforce the strict updatability linting to all applicable transitive dependencies.
func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) {
m := ctx.Module()
- if d, ok := m.(lintDepSetsIntf); ok && d.getStrictUpdatabilityLinting() {
+ if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() {
ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
- if a, ok := d.(lintDepSetsIntf); ok {
- a.setStrictUpdatabilityLinting(true)
+ if a, ok := d.(LintDepSetsIntf); ok {
+ a.SetStrictUpdatabilityLinting(true)
}
})
}
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 4bc0c5f..1eee354 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -28,6 +28,11 @@
--disable_check SuspiciousImport
--disable_check UnusedResources
--disable_check ViewConstructor
+# Disable NewApi checks for the platform since platform is the one that implements
+# the API. This prevents noisy lint warnings like b/228956345#1
+# NewApi checks will continue to be enforced for apex deps since
+# lint.strict_updatability_linting will be true for those Soong modules
+--disable_check NewApi
# Downgrade existing errors to warnings
--warning_check AppCompatResource # 55 occurences in 10 modules
@@ -66,7 +71,6 @@
--warning_check MissingTvBanner # 3 occurences in 3 modules
--warning_check NamespaceTypo # 3 occurences in 3 modules
--warning_check NetworkSecurityConfig # 46 occurences in 12 modules
---warning_check NewApi # 1996 occurences in 122 modules
--warning_check NotSibling # 15 occurences in 10 modules
--warning_check ObjectAnimatorBinding # 14 occurences in 5 modules
--warning_check OnClick # 49 occurences in 21 modules
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 1c2a3ae..10c9187 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -51,6 +51,7 @@
var addSourceBootclassPathModule = android.FixtureAddTextFile("source/Android.bp", `
java_library {
name: "foo",
+ host_supported: true, // verify that b/232106778 is fixed
srcs: ["a.java"],
system_modules: "none",
sdk_version: "none",
@@ -271,7 +272,9 @@
entries := android.AndroidMkEntriesForTest(t, result.TestContext, platformBootclasspath)
goals := entries[0].GetDistForGoals(platformBootclasspath)
android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore\n", goals[0])
- android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[1]))
+ android.AssertStringDoesContain(t, "platform dist goals meta check", goals[1], "$(if $(strip $(ALL_TARGETS.")
+ android.AssertStringDoesContain(t, "platform dist goals meta assign", goals[1], "),,$(eval ALL_TARGETS.")
+ android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[2]))
}
func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
diff --git a/java/plugin.go b/java/plugin.go
index 297ac2c..123dbd4 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -14,7 +14,10 @@
package java
-import "android/soong/android"
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+)
func init() {
registerJavaPluginBuildComponents(android.InitRegistrationContext)
@@ -24,7 +27,6 @@
ctx.RegisterModuleType("java_plugin", PluginFactory)
}
-// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
func PluginFactory() android.Module {
module := &Plugin{}
@@ -32,9 +34,13 @@
module.AddProperties(&module.pluginProperties)
InitJavaModule(module, android.HostSupported)
+
+ android.InitBazelModule(module)
+
return module
}
+// Plugin describes a java_plugin module, a host java library that will be used by javac as an annotation processor.
type Plugin struct {
Library
@@ -50,3 +56,34 @@
// parallelism and cause more recompilation for modules that depend on modules that use this plugin.
Generates_api *bool
}
+
+type pluginAttributes struct {
+ *javaCommonAttributes
+ Deps bazel.LabelListAttribute
+ Processor_class *string
+}
+
+// ConvertWithBp2build is used to convert android_app to Bazel.
+func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ pluginName := p.Name()
+ commonAttrs, depLabels := p.convertLibraryAttrsBp2Build(ctx)
+
+ deps := depLabels.Deps
+ deps.Append(depLabels.StaticDeps)
+
+ var processorClass *string
+ if p.pluginProperties.Processor_class != nil {
+ processorClass = p.pluginProperties.Processor_class
+ }
+
+ attrs := &pluginAttributes{
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ Processor_class: processorClass,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_plugin",
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: pluginName}, attrs)
+}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c67e2bd..9449707 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path"
"strconv"
"strings"
@@ -37,6 +38,11 @@
// list of api version directories
Api_dirs []string
+ // Directory containing finalized api txt files for extension versions.
+ // Extension versions higher than the base sdk extension version will
+ // be assumed to be finalized later than all Api_dirs.
+ Extensions_dir *string
+
// The next API directory can optionally point to a directory where
// files incompatibility-tracking files are stored for the current
// "in progress" API. Each module present in one of the api_dirs will have
@@ -60,36 +66,45 @@
// no need to implement
}
-func parseJarPath(path string) (module string, apiver string, scope string) {
- elements := strings.Split(path, "/")
+// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g.
+// <version>/<scope>/<module>.jar
+// <version>/<scope>/api/<module>.txt
+// extensions/<version>/<scope>/<module>.jar
+// extensions/<version>/<scope>/api/<module>.txt
+func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) {
+ elements := strings.Split(p, "/")
- apiver = elements[0]
- scope = elements[1]
-
- module = strings.TrimSuffix(elements[2], ".jar")
- return
-}
-
-func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) {
- elements := strings.Split(path, "/")
- apiver = elements[0]
-
- scope = elements[1]
- if scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
- ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
+ scopeIdx := len(elements) - 2
+ if elements[scopeIdx] == "api" {
+ scopeIdx--
+ }
+ scope = elements[scopeIdx]
+ if scope != "core" && scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
+ ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, p)
return
}
+ version = elements[scopeIdx-1]
- // elements[2] is string literal "api". skipping.
- module = strings.TrimSuffix(elements[3], ".txt")
+ module = strings.TrimSuffix(path.Base(p), path.Ext(p))
return
}
-func prebuiltApiModuleName(mctx android.LoadHookContext, module string, scope string, apiver string) string {
- return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module
+// parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version).
+func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, scope string) {
+ module, v, scope := parsePrebuiltPath(ctx, p)
+ version, err := strconv.Atoi(v)
+ if err != nil {
+ ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v)
+ return
+ }
+ return
}
-func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdkVersion string, compileDex bool) {
+func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string {
+ return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
+}
+
+func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
props := struct {
Name *string
Jars []string
@@ -97,7 +112,7 @@
Installable *bool
Compile_dex *bool
}{}
- props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
+ props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version))
props.Jars = append(props.Jars, path)
props.Sdk_version = proptools.StringPtr(sdkVersion)
props.Installable = proptools.BoolPtr(false)
@@ -132,133 +147,147 @@
mctx.CreateModule(genrule.GenRuleFactory, &props)
}
-func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
+// globApiDirs collects all the files in all api_dirs and all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'.
+// <api-dir>/<scope>/<glob> for all api-dir and scope.
+func globApiDirs(mctx android.LoadHookContext, p *prebuiltApis, api_dir_glob string) []string {
var files []string
for _, apiver := range p.properties.Api_dirs {
- files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...)
+ files = append(files, globScopeDir(mctx, apiver, api_dir_glob)...)
}
return files
}
-func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string {
+// globExtensionDirs collects all the files under the extension dir (for all versions and scopes) that match the given glob
+// <extension-dir>/<version>/<scope>/<glob> for all version and scope.
+func globExtensionDirs(mctx android.LoadHookContext, p *prebuiltApis, extension_dir_glob string) []string {
+ // <extensions-dir>/<num>/<extension-dir-glob>
+ return globScopeDir(mctx, *p.properties.Extensions_dir+"/*", extension_dir_glob)
+}
+
+// globScopeDir collects all the files in the given subdir across all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'.
+// <subdir>/<scope>/<glob> for all scope.
+func globScopeDir(mctx android.LoadHookContext, subdir string, subdir_glob string) []string {
var files []string
dir := mctx.ModuleDir() + "/" + subdir
for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
- glob := fmt.Sprintf("%s/%s/%s", dir, scope, name)
+ glob := fmt.Sprintf("%s/%s/%s", dir, scope, subdir_glob)
vfiles, err := mctx.GlobWithDeps(glob, nil)
if err != nil {
- mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err)
+ mctx.ModuleErrorf("failed to glob %s files under %q: %s", subdir_glob, dir+"/"+scope, err)
}
files = append(files, vfiles...)
}
+ for i, f := range files {
+ files[i] = strings.TrimPrefix(f, mctx.ModuleDir()+"/")
+ }
return files
}
func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) {
- mydir := mctx.ModuleDir() + "/"
// <apiver>/<scope>/<module>.jar
- files := getPrebuiltFiles(mctx, p, "*.jar")
+ files := globApiDirs(mctx, p, "*.jar")
sdkVersion := proptools.StringDefault(p.properties.Imports_sdk_version, "current")
compileDex := proptools.BoolDefault(p.properties.Imports_compile_dex, false)
for _, f := range files {
// create a Import module for each jar file
- localPath := strings.TrimPrefix(f, mydir)
- module, apiver, scope := parseJarPath(localPath)
- createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex)
+ module, version, scope := parsePrebuiltPath(mctx, f)
+ createImport(mctx, module, scope, version, f, sdkVersion, compileDex)
if module == "core-for-system-modules" {
- createSystemModules(mctx, apiver, scope)
+ createSystemModules(mctx, version, scope)
}
}
}
-func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) {
+func createSystemModules(mctx android.LoadHookContext, version, scope string) {
props := struct {
Name *string
Libs []string
}{}
- props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver))
- props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver))
+ props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version))
+ props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version))
mctx.CreateModule(systemModulesImportFactory, &props)
}
+func PrebuiltApiModuleName(module, scope, version string) string {
+ return module + ".api." + scope + "." + version
+}
+
func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
- mydir := mctx.ModuleDir() + "/"
// <apiver>/<scope>/api/<module>.txt
- files := getPrebuiltFiles(mctx, p, "api/*.txt")
-
- if len(files) == 0 {
- mctx.ModuleErrorf("no api file found under %q", mydir)
- }
-
- // construct a map to find out the latest api file path
- // for each (<module>, <scope>) pair.
- type latestApiInfo struct {
- module string
- scope string
- version int
- path string
+ apiLevelFiles := globApiDirs(mctx, p, "api/*.txt")
+ if len(apiLevelFiles) == 0 {
+ mctx.ModuleErrorf("no api file found under %q", mctx.ModuleDir())
}
// Create modules for all (<module>, <scope, <version>) triplets,
- // and a "latest" module variant for each (<module>, <scope>) pair
- apiModuleName := func(module, scope, version string) string {
- return module + ".api." + scope + "." + version
+ for _, f := range apiLevelFiles {
+ module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
+ createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f)
}
- m := make(map[string]latestApiInfo)
- for _, f := range files {
- localPath := strings.TrimPrefix(f, mydir)
- module, apiver, scope := parseApiFilePath(mctx, localPath)
- createApiModule(mctx, apiModuleName(module, scope, apiver), localPath)
- version, err := strconv.Atoi(apiver)
- if err != nil {
- mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver)
- return
- }
+ // Figure out the latest version of each module/scope
+ type latestApiInfo struct {
+ module, scope, path string
+ version int
+ }
- // Track latest version of each module/scope, except for incompatibilities
- if !strings.HasSuffix(module, "incompatibilities") {
+ getLatest := func(files []string) map[string]latestApiInfo {
+ m := make(map[string]latestApiInfo)
+ for _, f := range files {
+ module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
+ if strings.HasSuffix(module, "incompatibilities") {
+ continue
+ }
key := module + "." + scope
- info, ok := m[key]
- if !ok {
- m[key] = latestApiInfo{module, scope, version, localPath}
- } else if version > info.version {
- info.version = version
- info.path = localPath
- m[key] = info
+ info, exists := m[key]
+ if !exists || version > info.version {
+ m[key] = latestApiInfo{module, scope, f, version}
+ }
+ }
+ return m
+ }
+
+ latest := getLatest(apiLevelFiles)
+ if p.properties.Extensions_dir != nil {
+ extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
+ for k, v := range getLatest(extensionApiFiles) {
+ if v.version > mctx.Config().PlatformBaseSdkExtensionVersion() {
+ if _, exists := latest[k]; !exists {
+ mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
+ }
+ latest[k] = v
}
}
}
// Sort the keys in order to make build.ninja stable
- for _, k := range android.SortedStringKeys(m) {
- info := m[k]
- name := apiModuleName(info.module, info.scope, "latest")
+ for _, k := range android.SortedStringKeys(latest) {
+ info := latest[k]
+ name := PrebuiltApiModuleName(info.module, info.scope, "latest")
createApiModule(mctx, name, info.path)
}
// Create incompatibilities tracking files for all modules, if we have a "next" api.
incompatibilities := make(map[string]bool)
if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" {
- files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
+ files := globScopeDir(mctx, nextApiDir, "api/*incompatibilities.txt")
for _, f := range files {
- localPath := strings.TrimPrefix(f, mydir)
- filename, _, scope := parseApiFilePath(mctx, localPath)
+ filename, _, scope := parsePrebuiltPath(mctx, f)
referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
- createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
+ createApiModule(mctx, PrebuiltApiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f)
incompatibilities[referencedModule+"."+scope] = true
}
}
// Create empty incompatibilities files for remaining modules
- for _, k := range android.SortedStringKeys(m) {
+ for _, k := range android.SortedStringKeys(latest) {
if _, ok := incompatibilities[k]; !ok {
- createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
+ createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
}
}
}
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index 79f4225..75422ad 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -20,9 +20,14 @@
"testing"
"android/soong/android"
+
"github.com/google/blueprint"
)
+func intPtr(v int) *int {
+ return &v
+}
+
func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForJavaTest,
@@ -54,3 +59,34 @@
sort.Strings(expected)
android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules)
}
+
+func TestPrebuiltApis_WithExtensions(t *testing.T) {
+ runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_base_sdk_extension_version = intPtr(v)
+ }),
+ FixtureWithPrebuiltApisAndExtensions(map[string][]string{
+ "31": {"foo"},
+ "32": {"foo", "bar"},
+ "current": {"foo", "bar"},
+ }, map[string][]string{
+ "1": {"foo"},
+ "2": {"foo", "bar"},
+ }),
+ ).RunTest(t)
+ foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+ bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+ return
+ }
+ // Here, the base extension level is 1, so extension level 2 is the latest
+ foo_input, bar_input := runTestWithBaseExtensionLevel(1)
+ android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+
+ // Here, the base extension level is 2, so 2 is not later than 32.
+ foo_input, bar_input = runTestWithBaseExtensionLevel(2)
+ android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/bar.txt", bar_input)
+}
diff --git a/java/proto.go b/java/proto.go
index ab913d8..5280077 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -19,6 +19,13 @@
"strconv"
"android/soong/android"
+ "android/soong/bazel"
+
+ "github.com/google/blueprint/proptools"
+)
+
+const (
+ protoTypeDefault = "lite"
)
func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths {
@@ -73,16 +80,18 @@
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
+ const unspecifiedProtobufPluginType = ""
if String(p.Proto.Plugin) == "" {
switch String(p.Proto.Type) {
+ case "stream": // does not require additional dependencies
case "micro":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
case "nano":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
- case "lite", "":
+ case "lite", unspecifiedProtobufPluginType:
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
case "full":
- if ctx.Host() || ctx.BazelConversionMode() {
+ if ctx.Host() {
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
} else {
ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
@@ -132,3 +141,52 @@
return flags
}
+
+type protoAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
+func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label {
+ protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
+ if !ok {
+ return nil
+ }
+
+ typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault)
+ var rule_class string
+ suffix := "_java_proto"
+ switch typ {
+ case "nano":
+ suffix += "_nano"
+ rule_class = "java_nano_proto_library"
+ case "micro":
+ suffix += "_micro"
+ rule_class = "java_micro_proto_library"
+ case "lite":
+ suffix += "_lite"
+ rule_class = "java_lite_proto_library"
+ case "stream":
+ suffix += "_stream"
+ rule_class = "java_stream_proto_library"
+ case "full":
+ rule_class = "java_proto_library"
+ default:
+ ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ)
+ }
+
+ protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"}
+ var protoAttrs protoAttributes
+ protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+
+ name := m.Name() + suffix
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: rule_class,
+ Bzl_load_location: "//build/bazel/rules/java:proto.bzl",
+ },
+ android.CommonAttributes{Name: name},
+ &protoAttrs)
+
+ return &bazel.Label{Label: ":" + name}
+}
diff --git a/java/proto_test.go b/java/proto_test.go
new file mode 100644
index 0000000..d1cb714
--- /dev/null
+++ b/java/proto_test.go
@@ -0,0 +1,53 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+const protoModules = `
+java_library_static {
+ name: "libprotobuf-java-lite",
+}
+`
+
+func TestProtoStream(t *testing.T) {
+ bp := `
+ java_library {
+ name: "java-stream-protos",
+ proto: {
+ type: "stream",
+ },
+ srcs: [
+ "a.proto",
+ "b.proto",
+ ],
+ }
+ `
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ ).RunTestWithBp(t, protoModules+bp)
+
+ proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+
+ if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
+ t.Errorf("expected '--javastream_out' in %q", cmd)
+ }
+}
diff --git a/java/rro.go b/java/rro.go
index 0b4d091..be84aff 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -139,7 +139,7 @@
aaptLinkFlags = append(aaptLinkFlags,
"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
}
- r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
+ r.aapt.buildActions(ctx, r, nil, nil, aaptLinkFlags...)
// Sign the built package
_, certificates := collectAppDeps(ctx, r, false, false)
diff --git a/java/sdk.go b/java/sdk.go
index 0dddd40..b0da5af 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -57,6 +57,12 @@
return JAVA_VERSION_8
} else if sdk.FinalOrFutureInt() <= 31 {
return JAVA_VERSION_9
+ } else if ctx.Config().TargetsJava17() {
+ // Temporary experimental flag to be able to try and build with
+ // java version 17 options. The flag, if used, just sets Java
+ // 17 as the default version, leaving any components that
+ // target an older version intact.
+ return JAVA_VERSION_17
} else {
return JAVA_VERSION_11
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 57ab268..f7e5d9d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -21,7 +21,6 @@
"reflect"
"regexp"
"sort"
- "strconv"
"strings"
"sync"
@@ -98,6 +97,13 @@
// The tag to use to depend on the stubs source and API module.
stubsSourceAndApiTag scopeDependencyTag
+ // The tag to use to depend on the module that provides the latest version of the API .txt file.
+ latestApiModuleTag scopeDependencyTag
+
+ // The tag to use to depend on the module that provides the latest version of the API removed.txt
+ // file.
+ latestRemovedApiModuleTag scopeDependencyTag
+
// The scope specific prefix to add to the api file base of "current.txt" or "removed.txt".
apiFilePrefix string
@@ -159,6 +165,16 @@
apiScope: scope,
depInfoExtractor: (*scopePaths).extractStubsSourceAndApiInfoFromApiStubsProvider,
}
+ scope.latestApiModuleTag = scopeDependencyTag{
+ name: name + "-latest-api",
+ apiScope: scope,
+ depInfoExtractor: (*scopePaths).extractLatestApiPath,
+ }
+ scope.latestRemovedApiModuleTag = scopeDependencyTag{
+ name: name + "-latest-removed-api",
+ apiScope: scope,
+ depInfoExtractor: (*scopePaths).extractLatestRemovedApiPath,
+ }
// To get the args needed to generate the stubs source append all the args from
// this scope and all the scopes it extends as each set of args adds additional
@@ -204,6 +220,24 @@
return scope.name
}
+// snapshotRelativeDir returns the snapshot directory into which the files related to scopes will
+// be stored.
+func (scope *apiScope) snapshotRelativeDir() string {
+ return filepath.Join("sdk_library", scope.name)
+}
+
+// snapshotRelativeCurrentApiTxtPath returns the snapshot path to the API .txt file for the named
+// library.
+func (scope *apiScope) snapshotRelativeCurrentApiTxtPath(name string) string {
+ return filepath.Join(scope.snapshotRelativeDir(), name+".txt")
+}
+
+// snapshotRelativeRemovedApiTxtPath returns the snapshot path to the removed API .txt file for the
+// named library.
+func (scope *apiScope) snapshotRelativeRemovedApiTxtPath(name string) string {
+ return filepath.Join(scope.snapshotRelativeDir(), name+"-removed.txt")
+}
+
type apiScopes []*apiScope
func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string {
@@ -378,6 +412,9 @@
// List of Java libraries that will be in the classpath when building the implementation lib
Impl_only_libs []string `android:"arch_variant"`
+ // List of Java libraries that will included in the implementation lib.
+ Impl_only_static_libs []string `android:"arch_variant"`
+
// List of Java libraries that will be in the classpath when building stubs
Stub_only_libs []string `android:"arch_variant"`
@@ -400,7 +437,7 @@
// Determines whether a runtime implementation library is built; defaults to false.
//
// If true then it also prevents the module from being used as a shared module, i.e.
- // it is as is shared_library: false, was set.
+ // it is as if shared_library: false, was set.
Api_only *bool
// local files that are used within user customized droiddoc options.
@@ -537,6 +574,12 @@
// Extracted annotations.
annotationsZip android.OptionalPath
+
+ // The path to the latest API file.
+ latestApiPath android.OptionalPath
+
+ // The path to the latest removed API file.
+ latestRemovedApiPath android.OptionalPath
}
func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
@@ -600,6 +643,31 @@
})
}
+func extractSingleOptionalOutputPath(dep android.Module) (android.OptionalPath, error) {
+ var paths android.Paths
+ if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok {
+ paths = sourceFileProducer.Srcs()
+ } else {
+ return android.OptionalPath{}, fmt.Errorf("module %q does not produce source files", dep)
+ }
+ if len(paths) != 1 {
+ return android.OptionalPath{}, fmt.Errorf("expected one path from %q, got %q", dep, paths)
+ }
+ return android.OptionalPathForPath(paths[0]), nil
+}
+
+func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error {
+ outputPath, err := extractSingleOptionalOutputPath(dep)
+ paths.latestApiPath = outputPath
+ return err
+}
+
+func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error {
+ outputPath, err := extractSingleOptionalOutputPath(dep)
+ paths.latestRemovedApiPath = outputPath
+ return err
+}
+
type commonToSdkLibraryAndImportProperties struct {
// The naming scheme to use for the components that this module creates.
//
@@ -1172,6 +1240,16 @@
// Add a dependency on the stubs source in order to access both stubs source and api information.
ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
+
+ if module.compareAgainstLatestApi(apiScope) {
+ // Add dependencies on the latest finalized version of the API .txt file.
+ latestApiModuleName := module.latestApiModuleName(apiScope)
+ ctx.AddDependency(module, apiScope.latestApiModuleTag, latestApiModuleName)
+
+ // Add dependencies on the latest finalized version of the remove API .txt file.
+ latestRemovedApiModuleName := module.latestRemovedApiModuleName(apiScope)
+ ctx.AddDependency(module, apiScope.latestRemovedApiModuleTag, latestRemovedApiModuleName)
+ }
}
if module.requiresRuntimeImplementationLibrary() {
@@ -1192,13 +1270,13 @@
if apiScope.unstable {
continue
}
- if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ if m := module.latestApiModuleName(apiScope); !ctx.OtherModuleExists(m) {
missingApiModules = append(missingApiModules, m)
}
- if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ if m := module.latestRemovedApiModuleName(apiScope); !ctx.OtherModuleExists(m) {
missingApiModules = append(missingApiModules, m)
}
- if m := android.SrcIsModule(module.latestIncompatibilitiesFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ if m := module.latestIncompatibilitiesModuleName(apiScope); !ctx.OtherModuleExists(m) {
missingApiModules = append(missingApiModules, m)
}
}
@@ -1272,6 +1350,26 @@
// Make the set of components exported by this module available for use elsewhere.
exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
+
+ // Provide additional information for inclusion in an sdk's generated .info file.
+ additionalSdkInfo := map[string]interface{}{}
+ additionalSdkInfo["dist_stem"] = module.distStem()
+ baseModuleName := module.BaseModuleName()
+ scopes := map[string]interface{}{}
+ additionalSdkInfo["scopes"] = scopes
+ for scope, scopePaths := range module.scopePaths {
+ scopeInfo := map[string]interface{}{}
+ scopes[scope.name] = scopeInfo
+ scopeInfo["current_api"] = scope.snapshotRelativeCurrentApiTxtPath(baseModuleName)
+ scopeInfo["removed_api"] = scope.snapshotRelativeRemovedApiTxtPath(baseModuleName)
+ if p := scopePaths.latestApiPath; p.Valid() {
+ scopeInfo["latest_api"] = p.Path().String()
+ }
+ if p := scopePaths.latestRemovedApiPath; p.Valid() {
+ scopeInfo["latest_removed_api"] = p.Path().String()
+ }
+ }
+ ctx.SetProvider(android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
}
func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1317,16 +1415,32 @@
return proptools.StringDefault(module.sdkLibraryProperties.Dist_group, "unknown")
}
+func latestPrebuiltApiModuleName(name string, apiScope *apiScope) string {
+ return PrebuiltApiModuleName(name, apiScope.name, "latest")
+}
+
func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
- return ":" + module.distStem() + ".api." + apiScope.name + ".latest"
+ return ":" + module.latestApiModuleName(apiScope)
+}
+
+func (module *SdkLibrary) latestApiModuleName(apiScope *apiScope) string {
+ return latestPrebuiltApiModuleName(module.distStem(), apiScope)
}
func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string {
- return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest"
+ return ":" + module.latestRemovedApiModuleName(apiScope)
+}
+
+func (module *SdkLibrary) latestRemovedApiModuleName(apiScope *apiScope) string {
+ return latestPrebuiltApiModuleName(module.distStem()+"-removed", apiScope)
}
func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string {
- return ":" + module.distStem() + "-incompatibilities.api." + apiScope.name + ".latest"
+ return ":" + module.latestIncompatibilitiesModuleName(apiScope)
+}
+
+func (module *SdkLibrary) latestIncompatibilitiesModuleName(apiScope *apiScope) string {
+ return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
}
func childModuleVisibility(childVisibility []string) []string {
@@ -1347,10 +1461,12 @@
visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
props := struct {
- Name *string
- Visibility []string
- Instrument bool
- Libs []string
+ Name *string
+ Visibility []string
+ Instrument bool
+ Libs []string
+ Static_libs []string
+ Apex_available []string
}{
Name: proptools.StringPtr(module.implLibraryModuleName()),
Visibility: visibility,
@@ -1359,6 +1475,12 @@
// Set the impl_only libs. Note that the module's "Libs" get appended as well, via the
// addition of &module.properties below.
Libs: module.sdkLibraryProperties.Impl_only_libs,
+ // Set the impl_only static libs. Note that the module's "static_libs" get appended as well, via the
+ // addition of &module.properties below.
+ Static_libs: module.sdkLibraryProperties.Impl_only_static_libs,
+ // Pass the apex_available settings down so that the impl library can be statically
+ // embedded within a library that is added to an APEX. Needed for updatable-media.
+ Apex_available: module.ApexAvailable(),
}
properties := []interface{}{
@@ -1512,15 +1634,15 @@
}
droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
disabledWarnings := []string{
- "MissingPermission",
"BroadcastBehavior",
- "HiddenSuperclass",
"DeprecationMismatch",
- "UnavailableSymbol",
- "SdkConstant",
+ "HiddenSuperclass",
"HiddenTypeParameter",
+ "MissingPermission",
+ "SdkConstant",
"Todo",
"Typo",
+ "UnavailableSymbol",
}
droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
@@ -1547,7 +1669,7 @@
props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
- if !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) {
+ if module.compareAgainstLatestApi(apiScope) {
// check against the latest released API
latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
props.Previous_api = latestApiFilegroupName
@@ -1599,6 +1721,10 @@
mctx.CreateModule(DroidstubsFactory, &props)
}
+func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
+ return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
+}
+
// Implements android.ApexModule
func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
depTag := mctx.OtherModuleDependencyTag(dep)
@@ -1815,8 +1941,9 @@
*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
}
- // Add the impl_only_libs *after* we're done using the Libs prop in submodules.
+ // Add the impl_only_libs and impl_only_static_libs *after* we're done using them in submodules.
module.properties.Libs = append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...)
+ module.properties.Static_libs = append(module.properties.Static_libs, module.sdkLibraryProperties.Impl_only_static_libs...)
}
func (module *SdkLibrary) InitSdkLibraryProperties() {
@@ -2212,8 +2339,23 @@
return module.uniqueApexVariations()
}
+// MinSdkVersion - Implements hiddenAPIModule
+func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecNone
+}
+
+var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
+
func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
- return module.commonOutputFiles(tag)
+ paths, err := module.commonOutputFiles(tag)
+ if paths != nil || err != nil {
+ return paths, err
+ }
+ if module.implLibraryModule != nil {
+ return module.implLibraryModule.OutputFiles(tag)
+ } else {
+ return nil, nil
+ }
}
func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2363,17 +2505,17 @@
}
}
-func (module *SdkLibraryImport) getStrictUpdatabilityLinting() bool {
+func (module *SdkLibraryImport) GetStrictUpdatabilityLinting() bool {
if module.implLibraryModule == nil {
return false
} else {
- return module.implLibraryModule.getStrictUpdatabilityLinting()
+ return module.implLibraryModule.GetStrictUpdatabilityLinting()
}
}
-func (module *SdkLibraryImport) setStrictUpdatabilityLinting(strictLinting bool) {
+func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(strictLinting bool) {
if module.implLibraryModule != nil {
- module.implLibraryModule.setStrictUpdatabilityLinting(strictLinting)
+ module.implLibraryModule.SetStrictUpdatabilityLinting(strictLinting)
}
}
@@ -2551,8 +2693,14 @@
ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
return ""
}
- intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt())
- return formattedOptionalAttribute(attrName, &intStr)
+ if apiLevel.IsCurrent() {
+ // passing "current" would always mean a future release, never the current (or the current in
+ // progress) which means some conditions would never be triggered.
+ ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+ `"current" is not an allowed value for this attribute`)
+ return ""
+ }
+ return formattedOptionalAttribute(attrName, value)
}
// formats an attribute for the xml permissions file if the value is not null
@@ -2755,7 +2903,7 @@
android.SdkMemberPropertiesBase
// Scope to per scope properties.
- Scopes map[*apiScope]scopeProperties
+ Scopes map[*apiScope]*scopeProperties
// The Java stubs source files.
Stub_srcs []string
@@ -2808,14 +2956,14 @@
StubsSrcJar android.Path
CurrentApiFile android.Path
RemovedApiFile android.Path
- AnnotationsZip android.Path
+ AnnotationsZip android.Path `supported_build_releases:"Tiramisu+"`
SdkVersion string
}
func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
sdk := variant.(*SdkLibrary)
- s.Scopes = make(map[*apiScope]scopeProperties)
+ s.Scopes = make(map[*apiScope]*scopeProperties)
for _, apiScope := range allApiScopes {
paths := sdk.findScopePaths(apiScope)
if paths == nil {
@@ -2838,7 +2986,7 @@
if paths.annotationsZip.Valid() {
properties.AnnotationsZip = paths.annotationsZip.Path()
}
- s.Scopes[apiScope] = properties
+ s.Scopes[apiScope] = &properties
}
}
@@ -2871,7 +3019,7 @@
if properties, ok := s.Scopes[apiScope]; ok {
scopeSet := propertySet.AddPropertySet(apiScope.propertyName)
- scopeDir := filepath.Join("sdk_library", s.OsPrefix(), apiScope.name)
+ scopeDir := apiScope.snapshotRelativeDir()
var jars []string
for _, p := range properties.Jars {
@@ -2895,13 +3043,13 @@
}
if properties.CurrentApiFile != nil {
- currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt")
+ currentApiSnapshotPath := apiScope.snapshotRelativeCurrentApiTxtPath(ctx.Name())
ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, currentApiSnapshotPath)
scopeSet.AddProperty("current_api", currentApiSnapshotPath)
}
if properties.RemovedApiFile != nil {
- removedApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"-removed.txt")
+ removedApiSnapshotPath := apiScope.snapshotRelativeRemovedApiTxtPath(ctx.Name())
ctx.SnapshotBuilder().CopyToSnapshot(properties.RemovedApiFile, removedApiSnapshotPath)
scopeSet.AddProperty("removed_api", removedApiSnapshotPath)
}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index e0e5b56..805bc22 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -126,6 +126,10 @@
exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
expectedFooExportedComponents := []string{
+ "foo-removed.api.public.latest",
+ "foo-removed.api.system.latest",
+ "foo.api.public.latest",
+ "foo.api.system.latest",
"foo.stubs",
"foo.stubs.source",
"foo.stubs.source.system",
@@ -182,7 +186,7 @@
"30": {"foo", "fooUpdatable", "fooUpdatableErr"},
}),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"}
+ variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W", "X"}
}),
).RunTestWithBp(t,
`
@@ -193,7 +197,7 @@
on_bootclasspath_since: "U",
on_bootclasspath_before: "V",
min_device_sdk: "W",
- max_device_sdk: "current",
+ max_device_sdk: "X",
min_sdk_version: "S",
}
java_sdk_library {
@@ -202,12 +206,13 @@
api_packages: ["foo"],
}
`)
+
// test that updatability attributes are passed on correctly
fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`)
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`)
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`)
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`)
// double check that updatability attributes are not written if they don't exist in the bp file
// the permissions file for the foo library defined above
@@ -230,7 +235,7 @@
`on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`,
`on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`,
`min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`,
- `max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`,
+ `max_device_sdk: "current" is not an allowed value for this attribute`,
})).RunTestWithBp(t,
`
java_sdk_library {
@@ -240,7 +245,7 @@
on_bootclasspath_since: "aaa",
on_bootclasspath_before: "bbc",
min_device_sdk: "ccc",
- max_device_sdk: "ddd",
+ max_device_sdk: "current",
}
`)
}
@@ -528,6 +533,8 @@
CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
+ `sdklib-removed.api.public.latest`,
+ `sdklib.api.public.latest`,
`sdklib.impl`,
`sdklib.stubs`,
`sdklib.stubs.source`,
@@ -850,6 +857,8 @@
CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
+ `sdklib-removed.api.public.latest`,
+ `sdklib.api.public.latest`,
`sdklib.impl`,
`sdklib.stubs`,
`sdklib.stubs.source`,
@@ -893,6 +902,8 @@
CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`prebuilt_sdklib`,
+ `sdklib-removed.api.public.latest`,
+ `sdklib.api.public.latest`,
`sdklib.impl`,
`sdklib.stubs`,
`sdklib.stubs.source`,
diff --git a/java/testing.go b/java/testing.go
index 6c49bc8..4000334 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -70,6 +70,10 @@
defaultJavaDir + "/framework/aidl": nil,
// Needed for various deps defined in GatherRequiredDepsForTest()
defaultJavaDir + "/a.java": nil,
+
+ // Needed for R8 rules on apps
+ "build/make/core/proguard.flags": nil,
+ "build/make/core/proguard_basic_keeps.flags": nil,
}.AddToFixture(),
// The java default module definitions.
android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
@@ -146,6 +150,10 @@
// This defines a file in the mock file system in a predefined location (prebuilts/sdk/Android.bp)
// and so only one instance of this can be used in each fixture.
func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.FixturePreparer {
+ return FixtureWithPrebuiltApisAndExtensions(release2Modules, nil)
+}
+
+func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string, extensionLevel2Modules map[string][]string) android.FixturePreparer {
mockFS := android.MockFS{}
path := "prebuilts/sdk/Android.bp"
@@ -153,14 +161,20 @@
prebuilt_apis {
name: "sdk",
api_dirs: ["%s"],
+ extensions_dir: "extensions",
imports_sdk_version: "none",
imports_compile_dex: true,
}
- `, strings.Join(android.SortedStringKeys(release2Modules), `", "`))
+ `, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`))
- for release, modules := range release2Modules {
+ for release, modules := range apiLevel2Modules {
mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
}
+ if extensionLevel2Modules != nil {
+ for release, modules := range extensionLevel2Modules {
+ mockFS.Merge(prebuiltExtensionApiFiles([]string{release}, modules))
+ }
+ }
return android.GroupFixturePreparers(
android.FixtureAddTextFile(path, bp),
android.FixtureMergeMockFs(mockFS),
@@ -198,6 +212,19 @@
return fs
}
+func prebuiltExtensionApiFiles(extensionLevels []string, modules []string) map[string][]byte {
+ fs := make(map[string][]byte)
+ for _, level := range extensionLevels {
+ for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer} {
+ for _, lib := range modules {
+ fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil
+ }
+ }
+ }
+ return fs
+}
+
// FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and
// Config.productVariables structs. As a side effect that enables dexpreopt.
func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer {
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index dbc112e..003b275 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -158,7 +158,6 @@
entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
- entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
},
},
}}
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go
index d9b4e86..cc83430 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -40,7 +40,6 @@
)
var (
- rootDir = flag.String("root", ".", "the value of // for load paths")
// TODO(asmundak): remove this option once there is a consensus on suffix
suffix = flag.String("suffix", ".rbc", "generated files' suffix")
dryRun = flag.Bool("dry_run", false, "dry run")
@@ -57,6 +56,7 @@
cpuProfile = flag.String("cpu_profile", "", "write cpu profile to file")
traceCalls = flag.Bool("trace_calls", false, "trace function calls")
inputVariables = flag.String("input_variables", "", "starlark file containing product config and global variables")
+ makefileList = flag.String("makefile_list", "", "path to a list of all makefiles in the source tree, generated by soong's finder. If not provided, mk2rbc will find the makefiles itself (more slowly than if this flag was provided)")
)
func init() {
@@ -70,7 +70,6 @@
quit("cannot alias unknown flag " + target)
}
flagAlias("suffix", "s")
- flagAlias("root", "d")
flagAlias("dry_run", "n")
flagAlias("convert_dependents", "r")
flagAlias("error_stat", "e")
@@ -79,7 +78,7 @@
var backupSuffix string
var tracedVariables []string
var errorLogger = errorSink{data: make(map[string]datum)}
-var makefileFinder = &LinuxMakefileFinder{}
+var makefileFinder mk2rbc.MakefileFinder
func main() {
flag.Usage = func() {
@@ -90,6 +89,10 @@
}
flag.Parse()
+ if _, err := os.Stat("build/soong/mk2rbc"); err != nil {
+ quit("Must be run from the root of the android tree. (build/soong/mk2rbc does not exist)")
+ }
+
// Delouse
if *suffix == ".mk" {
quit("cannot use .mk as generated file suffix")
@@ -133,6 +136,16 @@
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
+
+ if *makefileList != "" {
+ makefileFinder = &FileListMakefileFinder{
+ cachedMakefiles: nil,
+ filePath: *makefileList,
+ }
+ } else {
+ makefileFinder = &FindCommandMakefileFinder{}
+ }
+
// Find out global variables
getConfigVariables()
getSoongVariables()
@@ -160,7 +173,7 @@
}
ok := true
for _, mkFile := range files {
- ok = convertOne(mkFile) && ok
+ ok = convertOne(mkFile, []string{}) && ok
}
if *launcher != "" {
@@ -170,7 +183,7 @@
if *inputVariables == "" {
quit(fmt.Errorf("the product launcher requires an input variables file"))
}
- if !convertOne(*inputVariables) {
+ if !convertOne(*inputVariables, []string{}) {
quit(fmt.Errorf("the product launcher input variables file failed to convert"))
}
@@ -188,7 +201,7 @@
if *inputVariables == "" {
quit(fmt.Errorf("the board launcher requires an input variables file"))
}
- if !convertOne(*inputVariables) {
+ if !convertOne(*inputVariables, []string{}) {
quit(fmt.Errorf("the board launcher input variables file failed to convert"))
}
err := writeGenerated(*boardlauncher, mk2rbc.BoardLauncher(
@@ -217,17 +230,16 @@
const androidProductsMk = "AndroidProducts.mk"
// Build the list of AndroidProducts.mk files: it's
// build/make/target/product/AndroidProducts.mk + device/**/AndroidProducts.mk plus + vendor/**/AndroidProducts.mk
- targetAndroidProductsFile := filepath.Join(*rootDir, "build", "make", "target", "product", androidProductsMk)
+ targetAndroidProductsFile := filepath.Join("build", "make", "target", "product", androidProductsMk)
if _, err := os.Stat(targetAndroidProductsFile); err != nil {
- fmt.Fprintf(os.Stderr, "%s: %s\n(hint: %s is not a source tree root)\n",
- targetAndroidProductsFile, err, *rootDir)
+ fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
}
productConfigMap := make(map[string]string)
if err := mk2rbc.UpdateProductConfigMap(productConfigMap, targetAndroidProductsFile); err != nil {
fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
}
for _, t := range []string{"device", "vendor"} {
- _ = filepath.WalkDir(filepath.Join(*rootDir, t),
+ _ = filepath.WalkDir(t,
func(path string, d os.DirEntry, err error) error {
if err != nil || d.IsDir() || filepath.Base(path) != androidProductsMk {
return nil
@@ -243,10 +255,9 @@
}
func getConfigVariables() {
- path := filepath.Join(*rootDir, "build", "make", "core", "product.mk")
+ path := filepath.Join("build", "make", "core", "product.mk")
if err := mk2rbc.FindConfigVariables(path, mk2rbc.KnownVariables); err != nil {
- quit(fmt.Errorf("%s\n(check --root[=%s], it should point to the source root)",
- err, *rootDir))
+ quit(err)
}
}
@@ -259,11 +270,11 @@
if name != "BUILD_SYSTEM" {
return fmt.Sprintf("$(%s)", name)
}
- return filepath.Join(*rootDir, "build", "make", "core")
+ return filepath.Join("build", "make", "core")
}
func getSoongVariables() {
- path := filepath.Join(*rootDir, "build", "make", "core", "soong_config.mk")
+ path := filepath.Join("build", "make", "core", "soong_config.mk")
err := mk2rbc.FindSoongVariables(path, fileNameScope{}, mk2rbc.KnownVariables)
if err != nil {
quit(err)
@@ -299,9 +310,13 @@
// the output hierarchy, or to the stdout.
// Optionally, recursively convert the files this one includes by
// $(call inherit-product) or an include statement.
-func convertOne(mkFile string) (ok bool) {
+func convertOne(mkFile string, loadStack []string) (ok bool) {
if v, ok := converted[mkFile]; ok {
- return v != nil
+ if v == nil {
+ fmt.Fprintf(os.Stderr, "Cycle in load graph:\n%s\n%s\n\n", strings.Join(loadStack, "\n"), mkFile)
+ return false
+ }
+ return true
}
converted[mkFile] = nil
defer func() {
@@ -314,12 +329,11 @@
mk2starRequest := mk2rbc.Request{
MkFile: mkFile,
Reader: nil,
- RootDir: *rootDir,
OutputDir: *outputTop,
OutputSuffix: *suffix,
TracedVariables: tracedVariables,
TraceCalls: *traceCalls,
- SourceFS: os.DirFS(*rootDir),
+ SourceFS: os.DirFS("."),
MakefileFinder: makefileFinder,
ErrorLogger: errorLogger,
}
@@ -346,6 +360,7 @@
return false
}
}
+ loadStack = append(loadStack, mkFile)
ok = true
if *recurse {
for _, sub := range ss.SubConfigFiles() {
@@ -353,7 +368,7 @@
if _, err := os.Stat(sub); os.IsNotExist(err) {
continue
}
- ok = convertOne(sub) && ok
+ ok = convertOne(sub, loadStack) && ok
}
}
converted[mkFile] = ss
@@ -519,17 +534,17 @@
return res, len(sorted)
}
-type LinuxMakefileFinder struct {
+// FindCommandMakefileFinder is an implementation of mk2rbc.MakefileFinder that
+// runs the unix find command to find all the makefiles in the source tree.
+type FindCommandMakefileFinder struct {
cachedRoot string
cachedMakefiles []string
}
-func (l *LinuxMakefileFinder) Find(root string) []string {
+func (l *FindCommandMakefileFinder) Find(root string) []string {
if l.cachedMakefiles != nil && l.cachedRoot == root {
return l.cachedMakefiles
}
- l.cachedRoot = root
- l.cachedMakefiles = make([]string, 0)
// Return all *.mk files but not in hidden directories.
@@ -548,9 +563,60 @@
panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
}
scanner := bufio.NewScanner(stdout)
+ result := make([]string, 0)
for scanner.Scan() {
- l.cachedMakefiles = append(l.cachedMakefiles, strings.TrimPrefix(scanner.Text(), "./"))
+ result = append(result, strings.TrimPrefix(scanner.Text(), "./"))
}
stdout.Close()
+ err = scanner.Err()
+ if err != nil {
+ panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
+ }
+ l.cachedRoot = root
+ l.cachedMakefiles = result
+ return l.cachedMakefiles
+}
+
+// FileListMakefileFinder is an implementation of mk2rbc.MakefileFinder that
+// reads a file containing the list of makefiles in the android source tree.
+// This file is generated by soong's finder, so that it can be computed while
+// soong is already walking the source tree looking for other files. If the root
+// to find makefiles under is not the root of the android source tree, it will
+// fall back to using FindCommandMakefileFinder.
+type FileListMakefileFinder struct {
+ FindCommandMakefileFinder
+ cachedMakefiles []string
+ filePath string
+}
+
+func (l *FileListMakefileFinder) Find(root string) []string {
+ root, err1 := filepath.Abs(root)
+ wd, err2 := os.Getwd()
+ if root != wd || err1 != nil || err2 != nil {
+ return l.FindCommandMakefileFinder.Find(root)
+ }
+ if l.cachedMakefiles != nil {
+ return l.cachedMakefiles
+ }
+
+ file, err := os.Open(l.filePath)
+ if err != nil {
+ panic(fmt.Errorf("Cannot read makefile list: %s\n", err))
+ }
+ defer file.Close()
+
+ result := make([]string, 0)
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if len(line) > 0 {
+ result = append(result, line)
+ }
+ }
+
+ if err = scanner.Err(); err != nil {
+ panic(fmt.Errorf("Cannot read makefile list: %s\n", err))
+ }
+ l.cachedMakefiles = result
return l.cachedMakefiles
}
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 3f355ac..6a6eb46 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -221,11 +221,9 @@
}
func (xi *interpolateExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
- argsCopy := make([]starlarkExpr, len(xi.args))
- for i, arg := range xi.args {
- argsCopy[i] = arg.transform(transformer)
+ for i := range xi.args {
+ xi.args[i] = xi.args[i].transform(transformer)
}
- xi.args = argsCopy
if replacement := transformer(xi); replacement != nil {
return replacement
} else {
@@ -234,19 +232,18 @@
}
type variableRefExpr struct {
- ref variable
- isDefined bool
+ ref variable
}
-func NewVariableRefExpr(ref variable, isDefined bool) starlarkExpr {
+func NewVariableRefExpr(ref variable) starlarkExpr {
if predefined, ok := ref.(*predefinedVariable); ok {
return predefined.value
}
- return &variableRefExpr{ref, isDefined}
+ return &variableRefExpr{ref}
}
func (v *variableRefExpr) emit(gctx *generationContext) {
- v.ref.emitGet(gctx, v.isDefined)
+ v.ref.emitGet(gctx)
}
func (v *variableRefExpr) typ() starlarkType {
@@ -378,32 +375,6 @@
}
}
-// variableDefinedExpr corresponds to Make's ifdef VAR
-type variableDefinedExpr struct {
- v variable
-}
-
-func (v *variableDefinedExpr) emit(gctx *generationContext) {
- if v.v != nil {
- v.v.emitDefined(gctx)
- return
- }
- gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
-}
-
-func (_ *variableDefinedExpr) typ() starlarkType {
- return starlarkTypeBool
-}
-
-func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
- v.emit(gctx)
-}
-
-func (v *variableDefinedExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
- // TODO: VariableDefinedExpr isn't really an expression?
- return v
-}
-
type listExpr struct {
items []starlarkExpr
}
@@ -617,9 +588,8 @@
if cx.object != nil {
cx.object = cx.object.transform(transformer)
}
- argsCopy := make([]starlarkExpr, len(cx.args))
- for i, arg := range cx.args {
- argsCopy[i] = arg.transform(transformer)
+ for i := range cx.args {
+ cx.args[i] = cx.args[i].transform(transformer)
}
if replacement := transformer(cx); replacement != nil {
return replacement
@@ -771,8 +741,8 @@
return starlarkTypeUnknown
}
-func (_ *badExpr) emitListVarCopy(_ *generationContext) {
- panic("implement me")
+func (b *badExpr) emitListVarCopy(gctx *generationContext) {
+ b.emit(gctx)
}
func (b *badExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
@@ -794,3 +764,35 @@
x, ok := expr.(*stringLiteralExpr)
return ok && x.literal == ""
}
+
+func negateExpr(expr starlarkExpr) starlarkExpr {
+ switch typedExpr := expr.(type) {
+ case *notExpr:
+ return typedExpr.expr
+ case *inExpr:
+ typedExpr.isNot = !typedExpr.isNot
+ return typedExpr
+ case *eqExpr:
+ typedExpr.isEq = !typedExpr.isEq
+ return typedExpr
+ case *binaryOpExpr:
+ switch typedExpr.op {
+ case ">":
+ typedExpr.op = "<="
+ return typedExpr
+ case "<":
+ typedExpr.op = ">="
+ return typedExpr
+ case ">=":
+ typedExpr.op = "<"
+ return typedExpr
+ case "<=":
+ typedExpr.op = ">"
+ return typedExpr
+ default:
+ return ¬Expr{expr: expr}
+ }
+ default:
+ return ¬Expr{expr: expr}
+ }
+}
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index e317cad..e59146b 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -32,6 +32,7 @@
"os"
"path/filepath"
"regexp"
+ "sort"
"strconv"
"strings"
"text/scanner"
@@ -50,15 +51,12 @@
soongNsPrefix = "SOONG_CONFIG_"
// And here are the functions and variables:
- cfnGetCfg = baseName + ".cfg"
- cfnMain = baseName + ".product_configuration"
- cfnBoardMain = baseName + ".board_configuration"
- cfnPrintVars = baseName + ".printvars"
- cfnWarning = baseName + ".warning"
- cfnLocalAppend = baseName + ".local_append"
- cfnLocalSetDefault = baseName + ".local_set_default"
- cfnInherit = baseName + ".inherit"
- cfnSetListDefault = baseName + ".setdefault"
+ cfnGetCfg = baseName + ".cfg"
+ cfnMain = baseName + ".product_configuration"
+ cfnBoardMain = baseName + ".board_configuration"
+ cfnPrintVars = baseName + ".printvars"
+ cfnInherit = baseName + ".inherit"
+ cfnSetListDefault = baseName + ".setdefault"
)
const (
@@ -69,54 +67,76 @@
var knownFunctions = map[string]interface {
parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
}{
- "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString, addGlobals: false},
- "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
- "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
- soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
- soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
- "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
- "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList, addGlobals: false},
- "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList, addGlobals: false},
- "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList, addGlobals: false},
- "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList, addGlobals: false},
- "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList, addGlobals: false},
- "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
- "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addGlobals: false},
- "error": &makeControlFuncParser{name: baseName + ".mkerror"},
- "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt, addGlobals: false},
- "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList, addGlobals: false},
- "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList, addGlobals: false},
- "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList, addGlobals: false},
- "firstword": &firstOrLastwordCallParser{isLastWord: false},
- "foreach": &foreachCallPaser{},
- "if": &ifCallParser{},
- "info": &makeControlFuncParser{name: baseName + ".mkinfo"},
- "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
- "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
- "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
- "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
- "is-product-in-list": &isProductInListCallParser{},
- "is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
- "is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
- "lastword": &firstOrLastwordCallParser{isLastWord: true},
- "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false},
- "math_max": &mathMaxOrMinCallParser{function: "max"},
- "math_min": &mathMaxOrMinCallParser{function: "min"},
- "math_gt_or_eq": &mathComparisonCallParser{op: ">="},
- "math_gt": &mathComparisonCallParser{op: ">"},
- "math_lt": &mathComparisonCallParser{op: "<"},
- "my-dir": &myDirCallParser{},
- "patsubst": &substCallParser{fname: "patsubst"},
- "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false},
- "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addGlobals: false},
- "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addGlobals: false},
+ "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString},
+ "add-product-dex-preopt-module-config": &simpleCallParser{name: baseName + ".add_product_dex_preopt_module_config", returnType: starlarkTypeString, addHandle: true},
+ "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
+ "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+ soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+ soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
+ "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
+ "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList},
+ "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList},
+ "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList},
+ "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList},
+ "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeString},
+ "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
+ "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addHandle: true},
+ "error": &makeControlFuncParser{name: baseName + ".mkerror"},
+ "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt},
+ "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList},
+ "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList},
+ "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList},
+ "firstword": &simpleCallParser{name: baseName + ".first_word", returnType: starlarkTypeString},
+ "foreach": &foreachCallParser{},
+ "if": &ifCallParser{},
+ "info": &makeControlFuncParser{name: baseName + ".mkinfo"},
+ "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+ "is-product-in-list": &isProductInListCallParser{},
+ "is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
+ "is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
+ "lastword": &simpleCallParser{name: baseName + ".last_word", returnType: starlarkTypeString},
+ "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString},
+ "math_max": &mathMaxOrMinCallParser{function: "max"},
+ "math_min": &mathMaxOrMinCallParser{function: "min"},
+ "math_gt_or_eq": &mathComparisonCallParser{op: ">="},
+ "math_gt": &mathComparisonCallParser{op: ">"},
+ "math_lt": &mathComparisonCallParser{op: "<"},
+ "my-dir": &myDirCallParser{},
+ "patsubst": &substCallParser{fname: "patsubst"},
+ "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList},
+ "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addHandle: true},
+ "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addHandle: true},
// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
"shell": &shellCallParser{},
- "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString, addGlobals: false},
+ "sort": &simpleCallParser{name: baseName + ".mksort", returnType: starlarkTypeList},
+ "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString},
"subst": &substCallParser{fname: "subst"},
"warning": &makeControlFuncParser{name: baseName + ".mkwarning"},
"word": &wordCallParser{},
- "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList, addGlobals: false},
+ "words": &wordsCallParser{},
+ "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList},
+}
+
+// The same as knownFunctions, but returns a []starlarkNode instead of a starlarkExpr
+var knownNodeFunctions = map[string]interface {
+ parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode
+}{
+ "eval": &evalNodeParser{},
+ "if": &ifCallNodeParser{},
+ "inherit-product": &inheritProductCallParser{loadAlways: true},
+ "inherit-product-if-exists": &inheritProductCallParser{loadAlways: false},
+ "foreach": &foreachCallNodeParser{},
+}
+
+// These look like variables, but are actually functions, and would give
+// undefined variable errors if we converted them as variables. Instead,
+// emit an error instead of converting them.
+var unsupportedFunctions = map[string]bool{
+ "local-generated-sources-dir": true,
+ "local-intermediates-dir": true,
}
// These are functions that we don't implement conversions for, but
@@ -142,7 +162,6 @@
type Request struct {
MkFile string // file to convert
Reader io.Reader // if set, read input from this stream instead
- RootDir string // root directory path used to resolve included files
OutputSuffix string // generated Starlark files suffix
OutputDir string // if set, root of the output hierarchy
ErrorLogger ErrorLogger
@@ -187,17 +206,57 @@
return s == "error" || s == "warning" || s == "info"
}
+// varAssignmentScope points to the last assignment for each variable
+// in the current block. It is used during the parsing to chain
+// the assignments to a variable together.
+type varAssignmentScope struct {
+ outer *varAssignmentScope
+ vars map[string]bool
+}
+
// Starlark output generation context
type generationContext struct {
- buf strings.Builder
- starScript *StarlarkScript
- indentLevel int
- inAssignment bool
- tracedCount int
+ buf strings.Builder
+ starScript *StarlarkScript
+ indentLevel int
+ inAssignment bool
+ tracedCount int
+ varAssignments *varAssignmentScope
}
func NewGenerateContext(ss *StarlarkScript) *generationContext {
- return &generationContext{starScript: ss}
+ return &generationContext{
+ starScript: ss,
+ varAssignments: &varAssignmentScope{
+ outer: nil,
+ vars: make(map[string]bool),
+ },
+ }
+}
+
+func (gctx *generationContext) pushVariableAssignments() {
+ va := &varAssignmentScope{
+ outer: gctx.varAssignments,
+ vars: make(map[string]bool),
+ }
+ gctx.varAssignments = va
+}
+
+func (gctx *generationContext) popVariableAssignments() {
+ gctx.varAssignments = gctx.varAssignments.outer
+}
+
+func (gctx *generationContext) hasBeenAssigned(v variable) bool {
+ for va := gctx.varAssignments; va != nil; va = va.outer {
+ if _, ok := va.vars[v.name()]; ok {
+ return true
+ }
+ }
+ return false
+}
+
+func (gctx *generationContext) setHasBeenAssigned(v variable) {
+ gctx.varAssignments.vars[v.name()] = true
}
// emit returns generated script
@@ -370,10 +429,6 @@
}
}
-type nodeReceiver interface {
- newNode(node starlarkNode)
-}
-
// Information about the generated Starlark script.
type StarlarkScript struct {
mkFile string
@@ -382,25 +437,12 @@
nodes []starlarkNode
inherited []*moduleInfo
hasErrors bool
- topDir string
traceCalls bool // print enter/exit each init function
sourceFS fs.FS
makefileFinder MakefileFinder
nodeLocator func(pos mkparser.Pos) int
}
-func (ss *StarlarkScript) newNode(node starlarkNode) {
- ss.nodes = append(ss.nodes, node)
-}
-
-// varAssignmentScope points to the last assignment for each variable
-// in the current block. It is used during the parsing to chain
-// the assignments to a variable together.
-type varAssignmentScope struct {
- outer *varAssignmentScope
- vars map[string]*assignmentNode
-}
-
// parseContext holds the script we are generating and all the ephemeral data
// needed during the parsing.
type parseContext struct {
@@ -414,21 +456,20 @@
errorLogger ErrorLogger
tracedVariables map[string]bool // variables to be traced in the generated script
variables map[string]variable
- varAssignments *varAssignmentScope
- receiver nodeReceiver // receptacle for the generated starlarkNode's
- receiverStack []nodeReceiver
outputDir string
dependentModules map[string]*moduleInfo
soongNamespaces map[string]map[string]bool
includeTops []string
+ typeHints map[string]starlarkType
+ atTopOfMakefile bool
}
func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
- topdir, _ := filepath.Split(filepath.Join(ss.topDir, "foo"))
predefined := []struct{ name, value string }{
{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
- {"TOPDIR", topdir},
+ {"MAKEFILE_LIST", ss.mkFile},
+ {"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk
// TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
{"TARGET_COPY_OUT_SYSTEM", "system"},
{"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"},
@@ -443,7 +484,6 @@
{"TARGET_COPY_OUT_RECOVERY", "recovery"},
{"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
// TODO(asmundak): to process internal config files, we need the following variables:
- // BOARD_CONFIG_VENDOR_PATH
// TARGET_VENDOR
// target_base_product
//
@@ -466,8 +506,9 @@
dependentModules: make(map[string]*moduleInfo),
soongNamespaces: make(map[string]map[string]bool),
includeTops: []string{},
+ typeHints: make(map[string]starlarkType),
+ atTopOfMakefile: true,
}
- ctx.pushVarAssignments()
for _, item := range predefined {
ctx.variables[item.name] = &predefinedVariable{
baseVariable: baseVariable{nam: item.name, typ: starlarkTypeString},
@@ -478,45 +519,6 @@
return ctx
}
-func (ctx *parseContext) lastAssignment(name string) *assignmentNode {
- for va := ctx.varAssignments; va != nil; va = va.outer {
- if v, ok := va.vars[name]; ok {
- return v
- }
- }
- return nil
-}
-
-func (ctx *parseContext) setLastAssignment(name string, asgn *assignmentNode) {
- ctx.varAssignments.vars[name] = asgn
-}
-
-func (ctx *parseContext) pushVarAssignments() {
- va := &varAssignmentScope{
- outer: ctx.varAssignments,
- vars: make(map[string]*assignmentNode),
- }
- ctx.varAssignments = va
-}
-
-func (ctx *parseContext) popVarAssignments() {
- ctx.varAssignments = ctx.varAssignments.outer
-}
-
-func (ctx *parseContext) pushReceiver(rcv nodeReceiver) {
- ctx.receiverStack = append(ctx.receiverStack, ctx.receiver)
- ctx.receiver = rcv
-}
-
-func (ctx *parseContext) popReceiver() {
- last := len(ctx.receiverStack) - 1
- if last < 0 {
- panic(fmt.Errorf("popReceiver: receiver stack empty"))
- }
- ctx.receiver = ctx.receiverStack[last]
- ctx.receiverStack = ctx.receiverStack[0:last]
-}
-
func (ctx *parseContext) hasNodes() bool {
return ctx.currentNodeIndex < len(ctx.nodes)
}
@@ -537,11 +539,10 @@
ctx.currentNodeIndex--
}
-func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
+func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode {
// Handle only simple variables
- if !a.Name.Const() {
- ctx.errorf(a, "Only simple variables are handled")
- return
+ if !a.Name.Const() || a.Target != nil {
+ return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")}
}
name := a.Name.Strings[0]
// The `override` directive
@@ -549,79 +550,75 @@
// is parsed as an assignment to a variable named `override FOO`.
// There are very few places where `override` is used, just flag it.
if strings.HasPrefix(name, "override ") {
- ctx.errorf(a, "cannot handle override directive")
+ return []starlarkNode{ctx.newBadNode(a, "cannot handle override directive")}
+ }
+ if name == ".KATI_READONLY" {
+ // Skip assignments to .KATI_READONLY. If it was in the output file, it
+ // would be an error because it would be sorted before the definition of
+ // the variable it's trying to make readonly.
+ return []starlarkNode{}
}
// Soong configuration
if strings.HasPrefix(name, soongNsPrefix) {
- ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
- return
+ return ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
}
lhs := ctx.addVariable(name)
if lhs == nil {
- ctx.errorf(a, "unknown variable %s", name)
- return
+ return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
}
- _, isTraced := ctx.tracedVariables[name]
+ _, isTraced := ctx.tracedVariables[lhs.name()]
asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
if lhs.valueType() == starlarkTypeUnknown {
// Try to divine variable type from the RHS
asgn.value = ctx.parseMakeString(a, a.Value)
- if xBad, ok := asgn.value.(*badExpr); ok {
- ctx.wrapBadExpr(xBad)
- return
- }
inferred_type := asgn.value.typ()
if inferred_type != starlarkTypeUnknown {
lhs.setValueType(inferred_type)
}
}
if lhs.valueType() == starlarkTypeList {
- xConcat := ctx.buildConcatExpr(a)
- if xConcat == nil {
- return
- }
- switch len(xConcat.items) {
- case 0:
- asgn.value = &listExpr{}
- case 1:
- asgn.value = xConcat.items[0]
- default:
- asgn.value = xConcat
+ xConcat, xBad := ctx.buildConcatExpr(a)
+ if xBad != nil {
+ asgn.value = xBad
+ } else {
+ switch len(xConcat.items) {
+ case 0:
+ asgn.value = &listExpr{}
+ case 1:
+ asgn.value = xConcat.items[0]
+ default:
+ asgn.value = xConcat
+ }
}
} else {
asgn.value = ctx.parseMakeString(a, a.Value)
- if xBad, ok := asgn.value.(*badExpr); ok {
- ctx.wrapBadExpr(xBad)
- return
- }
}
- asgn.previous = ctx.lastAssignment(name)
- ctx.setLastAssignment(name, asgn)
+ if asgn.lhs.valueType() == starlarkTypeString &&
+ asgn.value.typ() != starlarkTypeUnknown &&
+ asgn.value.typ() != starlarkTypeString {
+ asgn.value = &toStringExpr{expr: asgn.value}
+ }
+
switch a.Type {
case "=", ":=":
asgn.flavor = asgnSet
case "+=":
- if asgn.previous == nil && !asgn.lhs.isPreset() {
- asgn.flavor = asgnMaybeAppend
- } else {
- asgn.flavor = asgnAppend
- }
+ asgn.flavor = asgnAppend
case "?=":
asgn.flavor = asgnMaybeSet
default:
panic(fmt.Errorf("unexpected assignment type %s", a.Type))
}
- ctx.receiver.newNode(asgn)
+ return []starlarkNode{asgn}
}
-func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) {
+func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) []starlarkNode {
val := ctx.parseMakeString(asgn, asgn.Value)
if xBad, ok := val.(*badExpr); ok {
- ctx.wrapBadExpr(xBad)
- return
+ return []starlarkNode{&exprNode{expr: xBad}}
}
// Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
@@ -634,17 +631,18 @@
// $(call add_soong_config_namespace,foo)
s, ok := maybeString(val)
if !ok {
- ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")}
}
+ result := make([]starlarkNode, 0)
for _, ns := range strings.Fields(s) {
ctx.addSoongNamespace(ns)
- ctx.receiver.newNode(&exprNode{&callExpr{
+ result = append(result, &exprNode{&callExpr{
name: baseName + ".soong_config_namespace",
args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}},
returnType: starlarkTypeVoid,
}})
}
+ return result
} else {
// Upon seeing
// SOONG_CONFIG_x_y = v
@@ -664,45 +662,41 @@
continue
}
if namespaceName != "" {
- ctx.errorf(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])}
}
namespaceName = name[0:pos]
varName = name[pos+1:]
}
if namespaceName == "" {
- ctx.errorf(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")}
}
if varName == "" {
// Remember variables in this namespace
s, ok := maybeString(val)
if !ok {
- ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")}
}
ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
- return
+ return []starlarkNode{}
}
// Finally, handle assignment to a namespace variable
if !ctx.hasNamespaceVar(namespaceName, varName) {
- ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)}
}
fname := baseName + "." + soongConfigAssign
if asgn.Type == "+=" {
fname = baseName + "." + soongConfigAppend
}
- ctx.receiver.newNode(&exprNode{&callExpr{
+ return []starlarkNode{&exprNode{&callExpr{
name: fname,
args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
returnType: starlarkTypeVoid,
- }})
+ }}}
}
}
-func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
+func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) (*concatExpr, *badExpr) {
xConcat := &concatExpr{}
var xItemList *listExpr
addToItemList := func(x ...starlarkExpr) {
@@ -724,8 +718,7 @@
// expressions return individual elements.
switch x := ctx.parseMakeString(a, item).(type) {
case *badExpr:
- ctx.wrapBadExpr(x)
- return nil
+ return nil, x
case *stringLiteralExpr:
addToItemList(maybeConvertToStringList(x).(*listExpr).items...)
default:
@@ -749,7 +742,7 @@
if xItemList != nil {
xConcat.items = append(xConcat.items, xItemList)
}
- return xConcat
+ return xConcat, nil
}
func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo {
@@ -779,7 +772,17 @@
}
func (ctx *parseContext) handleSubConfig(
- v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule)) {
+ v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule) starlarkNode) []starlarkNode {
+
+ // Allow seeing $(sort $(wildcard realPathExpr)) or $(wildcard realPathExpr)
+ // because those are functionally the same as not having the sort/wildcard calls.
+ if ce, ok := pathExpr.(*callExpr); ok && ce.name == "rblf.mksort" && len(ce.args) == 1 {
+ if ce2, ok2 := ce.args[0].(*callExpr); ok2 && ce2.name == "rblf.expand_wildcard" && len(ce2.args) == 1 {
+ pathExpr = ce2.args[0]
+ }
+ } else if ce2, ok2 := pathExpr.(*callExpr); ok2 && ce2.name == "rblf.expand_wildcard" && len(ce2.args) == 1 {
+ pathExpr = ce2.args[0]
+ }
// In a simple case, the name of a module to inherit/include is known statically.
if path, ok := maybeString(pathExpr); ok {
@@ -788,18 +791,20 @@
moduleShouldExist := loadAlways && ctx.ifNestLevel == 0
if strings.Contains(path, "*") {
if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil {
+ sort.Strings(paths)
+ result := make([]starlarkNode, 0)
for _, p := range paths {
mi := ctx.newDependentModule(p, !moduleShouldExist)
- processModule(inheritedStaticModule{mi, loadAlways})
+ result = append(result, processModule(inheritedStaticModule{mi, loadAlways}))
}
+ return result
} else {
- ctx.errorf(v, "cannot glob wildcard argument")
+ return []starlarkNode{ctx.newBadNode(v, "cannot glob wildcard argument")}
}
} else {
mi := ctx.newDependentModule(path, !moduleShouldExist)
- processModule(inheritedStaticModule{mi, loadAlways})
+ return []starlarkNode{processModule(inheritedStaticModule{mi, loadAlways})}
}
- return
}
// If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the
@@ -817,50 +822,51 @@
// rblf.inherit(handle, _e[0], _e[1])
//
var matchingPaths []string
- varPath, ok := pathExpr.(*interpolateExpr)
- if !ok {
- ctx.errorf(v, "inherit-product/include argument is too complex")
- return
- }
-
- pathPattern := []string{varPath.chunks[0]}
- for _, chunk := range varPath.chunks[1:] {
- if chunk != "" {
- pathPattern = append(pathPattern, chunk)
+ var needsWarning = false
+ if interpolate, ok := pathExpr.(*interpolateExpr); ok {
+ pathPattern := []string{interpolate.chunks[0]}
+ for _, chunk := range interpolate.chunks[1:] {
+ if chunk != "" {
+ pathPattern = append(pathPattern, chunk)
+ }
}
- }
- if pathPattern[0] == "" {
- if len(ctx.includeTops) == 0 {
- ctx.errorf(v, "inherit-product/include statements must not be prefixed with a variable, or must include a #RBC# include_top comment beforehand giving a root directory to search.")
- return
+ if pathPattern[0] == "" && len(ctx.includeTops) > 0 {
+ // If pattern starts from the top. restrict it to the directories where
+ // we know inherit-product uses dynamically calculated path.
+ for _, p := range ctx.includeTops {
+ pathPattern[0] = p
+ matchingPaths = append(matchingPaths, ctx.findMatchingPaths(pathPattern)...)
+ }
+ } else {
+ matchingPaths = ctx.findMatchingPaths(pathPattern)
}
- // If pattern starts from the top. restrict it to the directories where
- // we know inherit-product uses dynamically calculated path.
+ needsWarning = pathPattern[0] == "" && len(ctx.includeTops) == 0
+ } else if len(ctx.includeTops) > 0 {
for _, p := range ctx.includeTops {
- pathPattern[0] = p
- matchingPaths = append(matchingPaths, ctx.findMatchingPaths(pathPattern)...)
+ matchingPaths = append(matchingPaths, ctx.findMatchingPaths([]string{p, ""})...)
}
} else {
- matchingPaths = ctx.findMatchingPaths(pathPattern)
+ return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
}
+
// Safeguard against $(call inherit-product,$(PRODUCT_PATH))
const maxMatchingFiles = 150
if len(matchingPaths) > maxMatchingFiles {
- ctx.errorf(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)
- return
+ return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
}
- res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways}
+
+ res := inheritedDynamicModule{pathExpr, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
for _, p := range matchingPaths {
// A product configuration files discovered dynamically may attempt to inherit
// from another one which does not exist in this source tree. Prevent load errors
// by always loading the dynamic files as optional.
res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
}
- processModule(res)
+ return []starlarkNode{processModule(res)}
}
func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
- files := ctx.script.makefileFinder.Find(ctx.script.topDir)
+ files := ctx.script.makefileFinder.Find(".")
if len(pattern) == 0 {
return files
}
@@ -883,25 +889,30 @@
return res
}
-func (ctx *parseContext) handleInheritModule(v mkparser.Node, args *mkparser.MakeString, loadAlways bool) {
+type inheritProductCallParser struct {
+ loadAlways bool
+}
+
+func (p *inheritProductCallParser) parse(ctx *parseContext, v mkparser.Node, args *mkparser.MakeString) []starlarkNode {
args.TrimLeftSpaces()
args.TrimRightSpaces()
pathExpr := ctx.parseMakeString(v, args)
if _, ok := pathExpr.(*badExpr); ok {
- ctx.errorf(v, "Unable to parse argument to inherit")
+ return []starlarkNode{ctx.newBadNode(v, "Unable to parse argument to inherit")}
}
- ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
- ctx.receiver.newNode(&inheritNode{im, loadAlways})
+ return ctx.handleSubConfig(v, pathExpr, p.loadAlways, func(im inheritedModule) starlarkNode {
+ return &inheritNode{im, p.loadAlways}
})
}
-func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
- ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
- ctx.receiver.newNode(&includeNode{im, loadAlways})
+func (ctx *parseContext) handleInclude(v *mkparser.Directive) []starlarkNode {
+ loadAlways := v.Name[0] != '-'
+ return ctx.handleSubConfig(v, ctx.parseMakeString(v, v.Args), loadAlways, func(im inheritedModule) starlarkNode {
+ return &includeNode{im, loadAlways}
})
}
-func (ctx *parseContext) handleVariable(v *mkparser.Variable) {
+func (ctx *parseContext) handleVariable(v *mkparser.Variable) []starlarkNode {
// Handle:
// $(call inherit-product,...)
// $(call inherit-product-if-exists,...)
@@ -910,112 +921,84 @@
// $(error xxx)
// $(call other-custom-functions,...)
- // inherit-product(-if-exists) gets converted to a series of statements,
- // not just a single expression like parseReference returns. So handle it
- // separately at the beginning here.
- if strings.HasPrefix(v.Name.Dump(), "call inherit-product,") {
- args := v.Name.Clone()
- args.ReplaceLiteral("call inherit-product,", "")
- ctx.handleInheritModule(v, args, true)
- return
+ if name, args, ok := ctx.maybeParseFunctionCall(v, v.Name); ok {
+ if kf, ok := knownNodeFunctions[name]; ok {
+ return kf.parse(ctx, v, args)
+ }
}
- if strings.HasPrefix(v.Name.Dump(), "call inherit-product-if-exists,") {
- args := v.Name.Clone()
- args.ReplaceLiteral("call inherit-product-if-exists,", "")
- ctx.handleInheritModule(v, args, false)
- return
- }
- expr := ctx.parseReference(v, v.Name)
- switch x := expr.(type) {
- case *callExpr:
- ctx.receiver.newNode(&exprNode{expr})
- case *badExpr:
- ctx.wrapBadExpr(x)
- default:
- ctx.errorf(v, "cannot handle %s", v.Dump())
- }
+
+ return []starlarkNode{&exprNode{expr: ctx.parseReference(v, v.Name)}}
}
-func (ctx *parseContext) handleDefine(directive *mkparser.Directive) {
+func (ctx *parseContext) maybeHandleDefine(directive *mkparser.Directive) starlarkNode {
macro_name := strings.Fields(directive.Args.Strings[0])[0]
// Ignore the macros that we handle
_, ignored := ignoredDefines[macro_name]
_, known := knownFunctions[macro_name]
if !ignored && !known {
- ctx.errorf(directive, "define is not supported: %s", macro_name)
+ return ctx.newBadNode(directive, "define is not supported: %s", macro_name)
}
+ return nil
}
-func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) {
- ssSwitch := &switchNode{}
- ctx.pushReceiver(ssSwitch)
- for ctx.processBranch(ifDirective); ctx.hasNodes() && ctx.fatalError == nil; {
+func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) starlarkNode {
+ ssSwitch := &switchNode{
+ ssCases: []*switchCase{ctx.processBranch(ifDirective)},
+ }
+ for ctx.hasNodes() && ctx.fatalError == nil {
node := ctx.getNode()
switch x := node.(type) {
case *mkparser.Directive:
switch x.Name {
case "else", "elifdef", "elifndef", "elifeq", "elifneq":
- ctx.processBranch(x)
+ ssSwitch.ssCases = append(ssSwitch.ssCases, ctx.processBranch(x))
case "endif":
- ctx.popReceiver()
- ctx.receiver.newNode(ssSwitch)
- return
+ return ssSwitch
default:
- ctx.errorf(node, "unexpected directive %s", x.Name)
+ return ctx.newBadNode(node, "unexpected directive %s", x.Name)
}
default:
- ctx.errorf(ifDirective, "unexpected statement")
+ return ctx.newBadNode(ifDirective, "unexpected statement")
}
}
if ctx.fatalError == nil {
ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump())
}
- ctx.popReceiver()
+ return ctx.newBadNode(ifDirective, "no matching endif for %s", ifDirective.Dump())
}
// processBranch processes a single branch (if/elseif/else) until the next directive
// on the same level.
-func (ctx *parseContext) processBranch(check *mkparser.Directive) {
- block := switchCase{gate: ctx.parseCondition(check)}
+func (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase {
+ block := &switchCase{gate: ctx.parseCondition(check)}
defer func() {
- ctx.popVarAssignments()
ctx.ifNestLevel--
-
}()
- ctx.pushVarAssignments()
ctx.ifNestLevel++
- ctx.pushReceiver(&block)
for ctx.hasNodes() {
node := ctx.getNode()
if d, ok := node.(*mkparser.Directive); ok {
switch d.Name {
case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif":
- ctx.popReceiver()
- ctx.receiver.newNode(&block)
ctx.backNode()
- return
+ return block
}
}
- ctx.handleSimpleStatement(node)
+ block.nodes = append(block.nodes, ctx.handleSimpleStatement(node)...)
}
ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump())
- ctx.popReceiver()
-}
-
-func (ctx *parseContext) newIfDefinedNode(check *mkparser.Directive) (starlarkExpr, bool) {
- if !check.Args.Const() {
- return ctx.newBadExpr(check, "ifdef variable ref too complex: %s", check.Args.Dump()), false
- }
- v := ctx.addVariable(check.Args.Strings[0])
- return &variableDefinedExpr{v}, true
+ return block
}
func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode {
switch check.Name {
case "ifdef", "ifndef", "elifdef", "elifndef":
- v, ok := ctx.newIfDefinedNode(check)
- if ok && strings.HasSuffix(check.Name, "ndef") {
+ if !check.Args.Const() {
+ return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump())
+ }
+ v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]))
+ if strings.HasSuffix(check.Name, "ndef") {
v = ¬Expr{v}
}
return &ifNode{
@@ -1035,12 +1018,16 @@
}
func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr {
- message := fmt.Sprintf(text, args...)
if ctx.errorLogger != nil {
ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...)
}
ctx.script.hasErrors = true
- return &badExpr{errorLocation: ctx.errorLocation(node), message: message}
+ return &badExpr{errorLocation: ctx.errorLocation(node), message: fmt.Sprintf(text, args...)}
+}
+
+// records that the given node failed to be converted and includes an explanatory message
+func (ctx *parseContext) newBadNode(failedNode mkparser.Node, message string, args ...interface{}) starlarkNode {
+ return &exprNode{ctx.newBadExpr(failedNode, message, args...)}
}
func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
@@ -1081,53 +1068,35 @@
otherOperand = xLeft
}
- not := func(expr starlarkExpr) starlarkExpr {
- switch typedExpr := expr.(type) {
- case *inExpr:
- typedExpr.isNot = !typedExpr.isNot
- return typedExpr
- case *eqExpr:
- typedExpr.isEq = !typedExpr.isEq
- return typedExpr
- case *binaryOpExpr:
- switch typedExpr.op {
- case ">":
- typedExpr.op = "<="
- return typedExpr
- case "<":
- typedExpr.op = ">="
- return typedExpr
- case ">=":
- typedExpr.op = "<"
- return typedExpr
- case "<=":
- typedExpr.op = ">"
- return typedExpr
- default:
- return ¬Expr{expr: expr}
- }
- default:
- return ¬Expr{expr: expr}
- }
- }
-
// If we've identified one of the operands as being a string literal, check
// for some special cases we can do to simplify the resulting expression.
if otherOperand != nil {
if stringOperand == "" {
if isEq {
- return not(otherOperand)
+ return negateExpr(otherOperand)
} else {
return otherOperand
}
}
if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
if !isEq {
- return not(otherOperand)
+ return negateExpr(otherOperand)
} else {
return otherOperand
}
}
+ if otherOperand.typ() == starlarkTypeList {
+ fields := strings.Fields(stringOperand)
+ elements := make([]starlarkExpr, len(fields))
+ for i, s := range fields {
+ elements[i] = &stringLiteralExpr{literal: s}
+ }
+ return &eqExpr{
+ left: otherOperand,
+ right: &listExpr{elements},
+ isEq: isEq,
+ }
+ }
if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt {
return &eqExpr{
left: otherOperand,
@@ -1142,7 +1111,7 @@
// Given an if statement's directive and the left/right starlarkExprs,
// check if the starlarkExprs are one of a few hardcoded special cases
-// that can be converted to a simpler equalify expression than simply comparing
+// that can be converted to a simpler equality expression than simply comparing
// the two.
func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
right starlarkExpr) (starlarkExpr, bool) {
@@ -1171,10 +1140,8 @@
}
switch call.name {
- case baseName + ".filter", baseName + ".filter-out":
- return ctx.parseCompareFilterFuncResult(directive, call, value, isEq), true
- case baseName + ".expand_wildcard":
- return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
+ case baseName + ".filter":
+ return ctx.parseCompareFilterFuncResult(directive, call, value, isEq)
case baseName + ".findstring":
return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true
case baseName + ".strip":
@@ -1184,84 +1151,39 @@
}
func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
- filterFuncCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+ filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) {
// We handle:
// * ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
// * ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
- // * ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...) becomes if VAR in/not in ["v1", "v2"]
- // TODO(Asmundak): check the last case works for filter-out, too.
+ if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" {
+ return nil, false
+ }
xPattern := filterFuncCall.args[0]
xText := filterFuncCall.args[1]
var xInList *stringLiteralExpr
var expr starlarkExpr
var ok bool
- switch x := xValue.(type) {
- case *stringLiteralExpr:
- if x.literal != "" {
- return ctx.newBadExpr(cond, "filter comparison to non-empty value: %s", xValue)
- }
- // Either pattern or text should be const, and the
- // non-const one should be varRefExpr
- if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
- expr = xText
- } else if xInList, ok = xText.(*stringLiteralExpr); ok {
- expr = xPattern
+ if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
+ expr = xText
+ } else if xInList, ok = xText.(*stringLiteralExpr); ok {
+ expr = xPattern
+ } else {
+ return nil, false
+ }
+ slExpr := newStringListExpr(strings.Fields(xInList.literal))
+ // Generate simpler code for the common cases:
+ if expr.typ() == starlarkTypeList {
+ if len(slExpr.items) == 1 {
+ // Checking that a string belongs to list
+ return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true
} else {
- expr = &callExpr{
- object: nil,
- name: filterFuncCall.name,
- args: filterFuncCall.args,
- returnType: starlarkTypeBool,
- }
- if negate {
- expr = ¬Expr{expr: expr}
- }
- return expr
+ return nil, false
}
- case *variableRefExpr:
- if v, ok := xPattern.(*variableRefExpr); ok {
- if xInList, ok = xText.(*stringLiteralExpr); ok && v.ref.name() == x.ref.name() {
- // ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...), flip negate,
- // it's the opposite to what is done when comparing to empty.
- expr = xPattern
- negate = !negate
- }
- }
+ } else if len(slExpr.items) == 1 {
+ return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true
+ } else {
+ return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true
}
- if expr != nil && xInList != nil {
- slExpr := newStringListExpr(strings.Fields(xInList.literal))
- // Generate simpler code for the common cases:
- if expr.typ() == starlarkTypeList {
- if len(slExpr.items) == 1 {
- // Checking that a string belongs to list
- return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}
- } else {
- // TODO(asmundak):
- panic("TBD")
- }
- } else if len(slExpr.items) == 1 {
- return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}
- } else {
- return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}
- }
- }
- return ctx.newBadExpr(cond, "filter arguments are too complex: %s", cond.Dump())
-}
-
-func (ctx *parseContext) parseCompareWildcardFuncResult(directive *mkparser.Directive,
- xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
- if !isEmptyString(xValue) {
- return ctx.newBadExpr(directive, "wildcard result can be compared only to empty: %s", xValue)
- }
- callFunc := baseName + ".file_wildcard_exists"
- if s, ok := xCall.args[0].(*stringLiteralExpr); ok && !strings.ContainsAny(s.literal, "*?{[") {
- callFunc = baseName + ".file_exists"
- }
- var cc starlarkExpr = &callExpr{name: callFunc, args: xCall.args, returnType: starlarkTypeBool}
- if !negate {
- cc = ¬Expr{cc}
- }
- return cc
}
func (ctx *parseContext) parseCheckFindstringFuncResult(directive *mkparser.Directive,
@@ -1308,6 +1230,37 @@
right: xValue, isEq: !negate}
}
+func (ctx *parseContext) maybeParseFunctionCall(node mkparser.Node, ref *mkparser.MakeString) (name string, args *mkparser.MakeString, ok bool) {
+ ref.TrimLeftSpaces()
+ ref.TrimRightSpaces()
+
+ words := ref.SplitN(" ", 2)
+ if !words[0].Const() {
+ return "", nil, false
+ }
+
+ name = words[0].Dump()
+ args = mkparser.SimpleMakeString("", words[0].Pos())
+ if len(words) >= 2 {
+ args = words[1]
+ }
+ args.TrimLeftSpaces()
+ if name == "call" {
+ words = args.SplitN(",", 2)
+ if words[0].Empty() || !words[0].Const() {
+ return "", nil, false
+ }
+ name = words[0].Dump()
+ if len(words) < 2 {
+ args = mkparser.SimpleMakeString("", words[0].Pos())
+ } else {
+ args = words[1]
+ }
+ }
+ ok = true
+ return
+}
+
// parses $(...), returning an expression
func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeString) starlarkExpr {
ref.TrimLeftSpaces()
@@ -1317,12 +1270,39 @@
// Handle only the case where the first (or only) word is constant
words := ref.SplitN(" ", 2)
if !words[0].Const() {
- return ctx.newBadExpr(node, "reference is too complex: %s", refDump)
+ if len(words) == 1 {
+ expr := ctx.parseMakeString(node, ref)
+ return &callExpr{
+ object: &identifierExpr{"cfg"},
+ name: "get",
+ args: []starlarkExpr{
+ expr,
+ &callExpr{
+ object: &identifierExpr{"g"},
+ name: "get",
+ args: []starlarkExpr{
+ expr,
+ &stringLiteralExpr{literal: ""},
+ },
+ returnType: starlarkTypeUnknown,
+ },
+ },
+ returnType: starlarkTypeUnknown,
+ }
+ } else {
+ return ctx.newBadExpr(node, "reference is too complex: %s", refDump)
+ }
+ }
+
+ if name, _, ok := ctx.maybeParseFunctionCall(node, ref); ok {
+ if _, unsupported := unsupportedFunctions[name]; unsupported {
+ return ctx.newBadExpr(node, "%s is not supported", refDump)
+ }
}
// If it is a single word, it can be a simple variable
// reference or a function call
- if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" {
+ if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" && refDump != "eval" {
if strings.HasPrefix(refDump, soongNsPrefix) {
// TODO (asmundak): if we find many, maybe handle them.
return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump)
@@ -1351,45 +1331,31 @@
args: []starlarkExpr{
&stringLiteralExpr{literal: substParts[0]},
&stringLiteralExpr{literal: substParts[1]},
- NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil),
+ NewVariableRefExpr(v),
},
}
}
if v := ctx.addVariable(refDump); v != nil {
- return NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil)
+ return NewVariableRefExpr(v)
}
return ctx.newBadExpr(node, "unknown variable %s", refDump)
}
- expr := &callExpr{name: words[0].Dump(), returnType: starlarkTypeUnknown}
- args := mkparser.SimpleMakeString("", words[0].Pos())
- if len(words) >= 2 {
- args = words[1]
- }
- args.TrimLeftSpaces()
- if expr.name == "call" {
- words = args.SplitN(",", 2)
- if words[0].Empty() || !words[0].Const() {
- return ctx.newBadExpr(node, "cannot handle %s", refDump)
- }
- expr.name = words[0].Dump()
- if len(words) < 2 {
- args = &mkparser.MakeString{}
+ if name, args, ok := ctx.maybeParseFunctionCall(node, ref); ok {
+ if kf, found := knownFunctions[name]; found {
+ return kf.parse(ctx, node, args)
} else {
- args = words[1]
+ return ctx.newBadExpr(node, "cannot handle invoking %s", name)
}
}
- if kf, found := knownFunctions[expr.name]; found {
- return kf.parse(ctx, node, args)
- } else {
- return ctx.newBadExpr(node, "cannot handle invoking %s", expr.name)
- }
+ return ctx.newBadExpr(node, "cannot handle %s", refDump)
}
type simpleCallParser struct {
name string
returnType starlarkType
addGlobals bool
+ addHandle bool
}
func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
@@ -1397,6 +1363,9 @@
if p.addGlobals {
expr.args = append(expr.args, &globalsExpr{})
}
+ if p.addHandle {
+ expr.args = append(expr.args, &identifierExpr{name: "handle"})
+ }
for _, arg := range args.Split(",") {
arg.TrimLeftSpaces()
arg.TrimRightSpaces()
@@ -1452,7 +1421,7 @@
if !args.Empty() {
return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.")
}
- return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
+ return &stringLiteralExpr{literal: filepath.Dir(ctx.script.mkFile)}
}
type isProductInListCallParser struct{}
@@ -1462,7 +1431,7 @@
return ctx.newBadExpr(node, "is-product-in-list requires an argument")
}
return &inExpr{
- expr: &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
+ expr: NewVariableRefExpr(ctx.addVariable("TARGET_PRODUCT")),
list: maybeConvertToStringList(ctx.parseMakeString(node, args)),
isNot: false,
}
@@ -1475,8 +1444,8 @@
return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform")
}
return &inExpr{
- expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
- list: &variableRefExpr{ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS"), true},
+ expr: NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")),
+ list: NewVariableRefExpr(ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS")),
isNot: false,
}
}
@@ -1488,8 +1457,8 @@
return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments")
}
return &inExpr{
- expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
- list: &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
+ expr: NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")),
+ list: NewVariableRefExpr(ctx.addVariable("QCOM_BOARD_PLATFORMS")),
isNot: false,
}
}
@@ -1562,9 +1531,46 @@
}
}
-type foreachCallPaser struct{}
+type ifCallNodeParser struct{}
-func (p *foreachCallPaser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+func (p *ifCallNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode {
+ words := args.Split(",")
+ if len(words) != 2 && len(words) != 3 {
+ return []starlarkNode{ctx.newBadNode(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))}
+ }
+
+ ifn := &ifNode{expr: ctx.parseMakeString(node, words[0])}
+ cases := []*switchCase{
+ {
+ gate: ifn,
+ nodes: ctx.parseNodeMakeString(node, words[1]),
+ },
+ }
+ if len(words) == 3 {
+ cases = append(cases, &switchCase{
+ gate: &elseNode{},
+ nodes: ctx.parseNodeMakeString(node, words[2]),
+ })
+ }
+ if len(cases) == 2 {
+ if len(cases[1].nodes) == 0 {
+ // Remove else branch if it has no contents
+ cases = cases[:1]
+ } else if len(cases[0].nodes) == 0 {
+ // If the if branch has no contents but the else does,
+ // move them to the if and negate its condition
+ ifn.expr = negateExpr(ifn.expr)
+ cases[0].nodes = cases[1].nodes
+ cases = cases[:1]
+ }
+ }
+
+ return []starlarkNode{&switchNode{ssCases: cases}}
+}
+
+type foreachCallParser struct{}
+
+func (p *foreachCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
words := args.Split(",")
if len(words) != 3 {
return ctx.newBadExpr(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))
@@ -1589,11 +1595,96 @@
}
}
- return &foreachExpr{
+ var result starlarkExpr = &foreachExpr{
varName: loopVarName,
list: list,
action: action,
}
+
+ if action.typ() == starlarkTypeList {
+ result = &callExpr{
+ name: baseName + ".flatten_2d_list",
+ args: []starlarkExpr{result},
+ returnType: starlarkTypeList,
+ }
+ }
+
+ return result
+}
+
+func transformNode(node starlarkNode, transformer func(expr starlarkExpr) starlarkExpr) {
+ switch a := node.(type) {
+ case *ifNode:
+ a.expr = a.expr.transform(transformer)
+ case *switchCase:
+ transformNode(a.gate, transformer)
+ for _, n := range a.nodes {
+ transformNode(n, transformer)
+ }
+ case *switchNode:
+ for _, n := range a.ssCases {
+ transformNode(n, transformer)
+ }
+ case *exprNode:
+ a.expr = a.expr.transform(transformer)
+ case *assignmentNode:
+ a.value = a.value.transform(transformer)
+ case *foreachNode:
+ a.list = a.list.transform(transformer)
+ for _, n := range a.actions {
+ transformNode(n, transformer)
+ }
+ case *inheritNode:
+ if b, ok := a.module.(inheritedDynamicModule); ok {
+ b.path = b.path.transform(transformer)
+ a.module = b
+ }
+ case *includeNode:
+ if b, ok := a.module.(inheritedDynamicModule); ok {
+ b.path = b.path.transform(transformer)
+ a.module = b
+ }
+ }
+}
+
+type foreachCallNodeParser struct{}
+
+func (p *foreachCallNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode {
+ words := args.Split(",")
+ if len(words) != 3 {
+ return []starlarkNode{ctx.newBadNode(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))}
+ }
+ if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) {
+ return []starlarkNode{ctx.newBadNode(node, "first argument to foreach function must be a simple string identifier")}
+ }
+
+ loopVarName := words[0].Strings[0]
+
+ list := ctx.parseMakeString(node, words[1])
+ if list.typ() != starlarkTypeList {
+ list = &callExpr{
+ name: baseName + ".words",
+ returnType: starlarkTypeList,
+ args: []starlarkExpr{list},
+ }
+ }
+
+ actions := ctx.parseNodeMakeString(node, words[2])
+ // TODO(colefaust): Replace transforming code with something more elegant
+ for _, action := range actions {
+ transformNode(action, func(expr starlarkExpr) starlarkExpr {
+ if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName {
+ return &identifierExpr{loopVarName}
+ }
+ return nil
+ })
+ }
+
+ return []starlarkNode{&foreachNode{
+ varName: loopVarName,
+ list: list,
+ actions: actions,
+ }}
}
type wordCallParser struct{}
@@ -1603,9 +1694,11 @@
if len(words) != 2 {
return ctx.newBadExpr(node, "word function should have 2 arguments")
}
- var index uint64 = 0
+ var index = 0
if words[0].Const() {
- index, _ = strconv.ParseUint(strings.TrimSpace(words[0].Strings[0]), 10, 64)
+ if i, err := strconv.Atoi(strings.TrimSpace(words[0].Strings[0])); err == nil {
+ index = i
+ }
}
if index < 1 {
return ctx.newBadExpr(node, "word index should be constant positive integer")
@@ -1613,35 +1706,40 @@
words[1].TrimLeftSpaces()
words[1].TrimRightSpaces()
array := ctx.parseMakeString(node, words[1])
- if xBad, ok := array.(*badExpr); ok {
- return xBad
- }
- if array.typ() != starlarkTypeList {
- array = &callExpr{object: array, name: "split", returnType: starlarkTypeList}
- }
- return &indexExpr{array, &intLiteralExpr{int(index - 1)}}
-}
-
-type firstOrLastwordCallParser struct {
- isLastWord bool
-}
-
-func (p *firstOrLastwordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
- arg := ctx.parseMakeString(node, args)
- if bad, ok := arg.(*badExpr); ok {
+ if bad, ok := array.(*badExpr); ok {
return bad
}
- index := &intLiteralExpr{0}
- if p.isLastWord {
- if v, ok := arg.(*variableRefExpr); ok && v.ref.name() == "MAKEFILE_LIST" {
- return &stringLiteralExpr{ctx.script.mkFile}
+ if array.typ() != starlarkTypeList {
+ array = &callExpr{
+ name: baseName + ".words",
+ args: []starlarkExpr{array},
+ returnType: starlarkTypeList,
}
- index.literal = -1
}
- if arg.typ() == starlarkTypeList {
- return &indexExpr{arg, index}
+ return &indexExpr{array, &intLiteralExpr{index - 1}}
+}
+
+type wordsCallParser struct{}
+
+func (p *wordsCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ args.TrimLeftSpaces()
+ args.TrimRightSpaces()
+ array := ctx.parseMakeString(node, args)
+ if bad, ok := array.(*badExpr); ok {
+ return bad
}
- return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index}
+ if array.typ() != starlarkTypeList {
+ array = &callExpr{
+ name: baseName + ".words",
+ args: []starlarkExpr{array},
+ returnType: starlarkTypeList,
+ }
+ }
+ return &callExpr{
+ name: "len",
+ args: []starlarkExpr{array},
+ returnType: starlarkTypeInt,
+ }
}
func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) {
@@ -1706,6 +1804,44 @@
}
}
+type evalNodeParser struct{}
+
+func (p *evalNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode {
+ parser := mkparser.NewParser("Eval expression", strings.NewReader(args.Dump()))
+ nodes, errs := parser.Parse()
+ if errs != nil {
+ return []starlarkNode{ctx.newBadNode(node, "Unable to parse eval statement")}
+ }
+
+ if len(nodes) == 0 {
+ return []starlarkNode{}
+ } else if len(nodes) == 1 {
+ switch n := nodes[0].(type) {
+ case *mkparser.Assignment:
+ if n.Name.Const() {
+ return ctx.handleAssignment(n)
+ }
+ case *mkparser.Comment:
+ return []starlarkNode{&commentNode{strings.TrimSpace("#" + n.Comment)}}
+ case *mkparser.Directive:
+ if n.Name == "include" || n.Name == "-include" {
+ return ctx.handleInclude(n)
+ }
+ case *mkparser.Variable:
+ // Technically inherit-product(-if-exists) don't need to be put inside
+ // an eval, but some makefiles do it, presumably because they copy+pasted
+ // from a $(eval include ...)
+ if name, _, ok := ctx.maybeParseFunctionCall(n, n.Name); ok {
+ if name == "inherit-product" || name == "inherit-product-if-exists" {
+ return ctx.handleVariable(n)
+ }
+ }
+ }
+ }
+
+ return []starlarkNode{ctx.newBadNode(node, "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported")}
+}
+
func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
if mk.Const() {
return &stringLiteralExpr{mk.Dump()}
@@ -1730,45 +1866,74 @@
return NewInterpolateExpr(parts)
}
+func (ctx *parseContext) parseNodeMakeString(node mkparser.Node, mk *mkparser.MakeString) []starlarkNode {
+ // Discard any constant values in the make string, as they would be top level
+ // string literals and do nothing.
+ result := make([]starlarkNode, 0, len(mk.Variables))
+ for i := range mk.Variables {
+ result = append(result, ctx.handleVariable(&mk.Variables[i])...)
+ }
+ return result
+}
+
// Handles the statements whose treatment is the same in all contexts: comment,
// assignment, variable (which is a macro call in reality) and all constructs that
// do not handle in any context ('define directive and any unrecognized stuff).
-func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) {
+func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNode {
+ var result []starlarkNode
switch x := node.(type) {
case *mkparser.Comment:
- ctx.maybeHandleAnnotation(x)
- ctx.insertComment("#" + x.Comment)
+ if n, handled := ctx.maybeHandleAnnotation(x); handled && n != nil {
+ result = []starlarkNode{n}
+ } else if !handled {
+ result = []starlarkNode{&commentNode{strings.TrimSpace("#" + x.Comment)}}
+ }
case *mkparser.Assignment:
- ctx.handleAssignment(x)
+ result = ctx.handleAssignment(x)
case *mkparser.Variable:
- ctx.handleVariable(x)
+ result = ctx.handleVariable(x)
case *mkparser.Directive:
switch x.Name {
case "define":
- ctx.handleDefine(x)
+ if res := ctx.maybeHandleDefine(x); res != nil {
+ result = []starlarkNode{res}
+ }
case "include", "-include":
- ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
+ result = ctx.handleInclude(x)
case "ifeq", "ifneq", "ifdef", "ifndef":
- ctx.handleIfBlock(x)
+ result = []starlarkNode{ctx.handleIfBlock(x)}
default:
- ctx.errorf(x, "unexpected directive %s", x.Name)
+ result = []starlarkNode{ctx.newBadNode(x, "unexpected directive %s", x.Name)}
}
default:
- ctx.errorf(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))
+ result = []starlarkNode{ctx.newBadNode(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))}
}
// Clear the includeTops after each non-comment statement
// so that include annotations placed on certain statements don't apply
// globally for the rest of the makefile was well.
- if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
+ if _, wasComment := node.(*mkparser.Comment); !wasComment {
+ ctx.atTopOfMakefile = false
ctx.includeTops = []string{}
}
+
+ if result == nil {
+ result = []starlarkNode{}
+ }
+
+ return result
+}
+
+// The types allowed in a type_hint
+var typeHintMap = map[string]starlarkType{
+ "string": starlarkTypeString,
+ "list": starlarkTypeList,
}
// Processes annotation. An annotation is a comment that starts with #RBC# and provides
// a conversion hint -- say, where to look for the dynamically calculated inherit/include
-// paths.
-func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) {
+// paths. Returns true if the comment was a successfully-handled annotation.
+func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlarkNode, bool) {
maybeTrim := func(s, prefix string) (string, bool) {
if strings.HasPrefix(s, prefix) {
return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true
@@ -1777,44 +1942,49 @@
}
annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix)
if !ok {
- return
+ return nil, false
}
if p, ok := maybeTrim(annotation, "include_top"); ok {
// Don't allow duplicate include tops, because then we will generate
// invalid starlark code. (duplicate keys in the _entry dictionary)
for _, top := range ctx.includeTops {
if top == p {
- return
+ return nil, true
}
}
ctx.includeTops = append(ctx.includeTops, p)
- return
+ return nil, true
+ } else if p, ok := maybeTrim(annotation, "type_hint"); ok {
+ // Type hints must come at the beginning the file, to avoid confusion
+ // if a type hint was specified later and thus only takes effect for half
+ // of the file.
+ if !ctx.atTopOfMakefile {
+ return ctx.newBadNode(cnode, "type_hint annotations must come before the first Makefile statement"), true
+ }
+
+ parts := strings.Fields(p)
+ if len(parts) <= 1 {
+ return ctx.newBadNode(cnode, "Invalid type_hint annotation: %s. Must be a variable type followed by a list of variables of that type", p), true
+ }
+
+ var varType starlarkType
+ if varType, ok = typeHintMap[parts[0]]; !ok {
+ varType = starlarkTypeUnknown
+ }
+ if varType == starlarkTypeUnknown {
+ return ctx.newBadNode(cnode, "Invalid type_hint annotation. Only list/string types are accepted, found %s", parts[0]), true
+ }
+
+ for _, name := range parts[1:] {
+ // Don't allow duplicate type hints
+ if _, ok := ctx.typeHints[name]; ok {
+ return ctx.newBadNode(cnode, "Duplicate type hint for variable %s", name), true
+ }
+ ctx.typeHints[name] = varType
+ }
+ return nil, true
}
- ctx.errorf(cnode, "unsupported annotation %s", cnode.Comment)
-
-}
-
-func (ctx *parseContext) insertComment(s string) {
- ctx.receiver.newNode(&commentNode{strings.TrimSpace(s)})
-}
-
-func (ctx *parseContext) carryAsComment(failedNode mkparser.Node) {
- for _, line := range strings.Split(failedNode.Dump(), "\n") {
- ctx.insertComment("# " + line)
- }
-}
-
-// records that the given node failed to be converted and includes an explanatory message
-func (ctx *parseContext) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
- if ctx.errorLogger != nil {
- ctx.errorLogger.NewError(ctx.errorLocation(failedNode), failedNode, message, args...)
- }
- ctx.receiver.newNode(&exprNode{ctx.newBadExpr(failedNode, message, args...)})
- ctx.script.hasErrors = true
-}
-
-func (ctx *parseContext) wrapBadExpr(xBad *badExpr) {
- ctx.receiver.newNode(&exprNode{xBad})
+ return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
}
func (ctx *parseContext) loadedModulePath(path string) string {
@@ -1924,11 +2094,11 @@
starScript := &StarlarkScript{
moduleName: moduleNameForFile(req.MkFile),
mkFile: req.MkFile,
- topDir: req.RootDir,
traceCalls: req.TraceCalls,
sourceFS: req.SourceFS,
makefileFinder: req.MakefileFinder,
nodeLocator: func(pos mkparser.Pos) int { return parser.Unpack(pos).Line },
+ nodes: make([]starlarkNode, 0),
}
ctx := newParseContext(starScript, nodes)
ctx.outputSuffix = req.OutputSuffix
@@ -1940,9 +2110,8 @@
ctx.tracedVariables[v] = true
}
}
- ctx.pushReceiver(starScript)
for ctx.hasNodes() && ctx.fatalError == nil {
- ctx.handleSimpleStatement(ctx.getNode())
+ starScript.nodes = append(starScript.nodes, ctx.handleSimpleStatement(ctx.getNode())...)
}
if ctx.fatalError != nil {
return nil, ctx.fatalError
@@ -1964,9 +2133,7 @@
fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
- fmt.Fprintf(&buf, "globals, cfg, globals_base = %s(init, input_variables_init)\n", cfnBoardMain)
- fmt.Fprintf(&buf, "# TODO: Some product config variables need to be printed, but most are readonly so we can't just print cfg here.\n")
- fmt.Fprintf(&buf, "%s((globals, cfg, globals_base))\n", cfnPrintVars)
+ fmt.Fprintf(&buf, "%s(%s(init, input_variables_init))\n", cfnPrintVars, cfnBoardMain)
return buf.String()
}
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index c4f7da3..a09764c 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -65,6 +65,10 @@
PRODUCT_NAME := Pixel 3
PRODUCT_MODEL :=
local_var = foo
+local-var-with-dashes := bar
+$(warning local-var-with-dashes: $(local-var-with-dashes))
+GLOBAL-VAR-WITH-DASHES := baz
+$(warning GLOBAL-VAR-WITH-DASHES: $(GLOBAL-VAR-WITH-DASHES))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -73,6 +77,10 @@
cfg["PRODUCT_NAME"] = "Pixel 3"
cfg["PRODUCT_MODEL"] = ""
_local_var = "foo"
+ _local_var_with_dashes = "bar"
+ rblf.mkwarning("pixel3.mk", "local-var-with-dashes: %s" % _local_var_with_dashes)
+ g["GLOBAL-VAR-WITH-DASHES"] = "baz"
+ rblf.mkwarning("pixel3.mk", "GLOBAL-VAR-WITH-DASHES: %s" % g["GLOBAL-VAR-WITH-DASHES"])
`,
},
{
@@ -109,8 +117,8 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.mk2rbc_error("product.mk:2", "cannot handle invoking foo1")
- rblf.mk2rbc_error("product.mk:3", "cannot handle invoking foo0")
+ cfg["PRODUCT_NAME"] = rblf.mk2rbc_error("product.mk:2", "cannot handle invoking foo1")
+ cfg["PRODUCT_NAME"] = rblf.mk2rbc_error("product.mk:3", "cannot handle invoking foo0")
`,
},
{
@@ -131,7 +139,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.inherit(handle, "part", _part_init)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
if not _part1_init:
rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
rblf.inherit(handle, "part1", _part1_init)
@@ -174,7 +182,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
_part_init(g, handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
if not _part1_init:
rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
_part1_init(g, handle)
@@ -189,15 +197,31 @@
mkname: "path/product.mk",
in: `
$(call inherit-product, */font.mk)
+$(call inherit-product, $(sort $(wildcard */font.mk)))
+$(call inherit-product, $(wildcard */font.mk))
+
+include */font.mk
+include $(sort $(wildcard */font.mk))
+include $(wildcard */font.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
-load("//foo:font.star", _font_init = "init")
-load("//bar:font.star", _font1_init = "init")
+load("//bar:font.star", _font_init = "init")
+load("//foo:font.star", _font1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.inherit(handle, "foo/font", _font_init)
- rblf.inherit(handle, "bar/font", _font1_init)
+ rblf.inherit(handle, "bar/font", _font_init)
+ rblf.inherit(handle, "foo/font", _font1_init)
+ rblf.inherit(handle, "bar/font", _font_init)
+ rblf.inherit(handle, "foo/font", _font1_init)
+ rblf.inherit(handle, "bar/font", _font_init)
+ rblf.inherit(handle, "foo/font", _font1_init)
+ _font_init(g, handle)
+ _font1_init(g, handle)
+ _font_init(g, handle)
+ _font1_init(g, handle)
+ _font_init(g, handle)
+ _font1_init(g, handle)
`,
},
{
@@ -231,7 +255,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo"
else:
pass
@@ -246,6 +270,8 @@
in: `
$(warning this is the warning)
$(warning)
+$(warning # this warning starts with a pound)
+$(warning this warning has a # in the middle)
$(info this is the info)
$(error this is the error)
PRODUCT_NAME:=$(shell echo *)
@@ -256,6 +282,8 @@
cfg = rblf.cfg(handle)
rblf.mkwarning("product.mk", "this is the warning")
rblf.mkwarning("product.mk", "")
+ rblf.mkwarning("product.mk", "# this warning starts with a pound")
+ rblf.mkwarning("product.mk", "this warning has a # in the middle")
rblf.mkinfo("product.mk", "this is the info")
rblf.mkerror("product.mk", "this is the error")
cfg["PRODUCT_NAME"] = rblf.shell("echo *")
@@ -275,7 +303,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
# Comment
pass
else:
@@ -296,7 +324,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if not g.get("PRODUCT_NAME") != None:
+ if not cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo1"
else:
cfg["PRODUCT_NAME"] = "gizmo2"
@@ -315,9 +343,9 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo"
- elif not g.get("PRODUCT_PACKAGES") != None:
+ elif not cfg.get("PRODUCT_PACKAGES", []):
# Comment
pass
`,
@@ -389,6 +417,10 @@
endif
ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
endif
+ifneq (, $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
+endif
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+endif
ifneq (,$(filter true, $(v1)$(v2)))
endif
ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT)))
@@ -407,8 +439,12 @@
pass
if "plaf" in g.get("PLATFORM_LIST", []):
pass
+ if g["TARGET_BUILD_VARIANT"] == " ".join(rblf.filter(g["TARGET_BUILD_VARIANT"], "userdebug eng")):
+ pass
if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
pass
+ if rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
+ pass
if rblf.filter("true", "%s%s" % (_v1, _v2)):
pass
if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]):
@@ -509,11 +545,11 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if0"]
- if g.get("PRODUCT_MODEL") != None:
+ if cfg.get("PRODUCT_MODEL", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if-if"]
- elif g.get("PRODUCT_NAME") != None:
+ elif cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if-elif"]
else:
cfg["PRODUCT_PACKAGES"] = ["pack-if-else"]
@@ -532,14 +568,18 @@
endif
ifneq (,$(wildcard foo*.mk))
endif
+ifeq (foo1.mk foo2.mk barxyz.mk,$(wildcard foo*.mk bar*.mk))
+endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- if not rblf.file_exists("foo.mk"):
+ if not rblf.expand_wildcard("foo.mk"):
pass
- if rblf.file_wildcard_exists("foo*.mk"):
+ if rblf.expand_wildcard("foo*.mk"):
+ pass
+ if rblf.expand_wildcard("foo*.mk bar*.mk") == ["foo1.mk", "foo2.mk", "barxyz.mk"]:
pass
`,
},
@@ -606,7 +646,7 @@
pass
elif not rblf.board_platform_is(g, "copper"):
pass
- elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
+ elif g.get("TARGET_BOARD_PLATFORM", "") not in g.get("QCOM_BOARD_PLATFORMS", ""):
pass
elif g["TARGET_PRODUCT"] in g.get("PLATFORM_LIST", []):
pass
@@ -629,7 +669,7 @@
pass
elif not rblf.board_platform_is(g, "copper"):
pass
- elif g.get("TARGET_BOARD_PLATFORM", "") in g["QCOM_BOARD_PLATFORMS"]:
+ elif g.get("TARGET_BOARD_PLATFORM", "") in g.get("QCOM_BOARD_PLATFORMS", ""):
pass
`,
},
@@ -731,16 +771,18 @@
$(call require-artifacts-in-path, foo, bar)
$(call require-artifacts-in-path-relaxed, foo, bar)
$(call dist-for-goals, goal, from:to)
+$(call add-product-dex-preopt-module-config,MyModule,disable)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.enforce_product_packages_exist("")
- rblf.enforce_product_packages_exist("foo")
- rblf.require_artifacts_in_path("foo", "bar")
- rblf.require_artifacts_in_path_relaxed("foo", "bar")
+ rblf.enforce_product_packages_exist(handle, "")
+ rblf.enforce_product_packages_exist(handle, "foo")
+ rblf.require_artifacts_in_path(handle, "foo", "bar")
+ rblf.require_artifacts_in_path_relaxed(handle, "foo", "bar")
rblf.mkdist_for_goals(g, "goal", "from:to")
+ rblf.add_product_dex_preopt_module_config(handle, "MyModule", "disable")
`,
},
{
@@ -770,8 +812,12 @@
PRODUCT_COPY_FILES := $(addprefix pfx-,a b c)
PRODUCT_COPY_FILES := $(addsuffix .sff, a b c)
PRODUCT_NAME := $(word 1, $(subst ., ,$(TARGET_BOARD_PLATFORM)))
+ifeq (1,$(words $(SOME_UNKNOWN_VARIABLE)))
+endif
+ifeq ($(words $(SOME_OTHER_VARIABLE)),$(SOME_INT_VARIABLE))
+endif
$(info $(patsubst %.pub,$(PRODUCT_NAME)%,$(PRODUCT_ADB_KEYS)))
-$(info $(dir foo/bar))
+$(info $$(dir foo/bar): $(dir foo/bar))
$(info $(firstword $(PRODUCT_COPY_FILES)))
$(info $(lastword $(PRODUCT_COPY_FILES)))
$(info $(dir $(lastword $(MAKEFILE_LIST))))
@@ -792,14 +838,18 @@
cfg = rblf.cfg(handle)
cfg["PRODUCT_COPY_FILES"] = rblf.addprefix("pfx-", "a b c")
cfg["PRODUCT_COPY_FILES"] = rblf.addsuffix(".sff", "a b c")
- cfg["PRODUCT_NAME"] = ((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " ")).split()[0]
+ cfg["PRODUCT_NAME"] = rblf.words((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " "))[0]
+ if len(rblf.words(g.get("SOME_UNKNOWN_VARIABLE", ""))) == 1:
+ pass
+ if ("%d" % (len(rblf.words(g.get("SOME_OTHER_VARIABLE", ""))))) == g.get("SOME_INT_VARIABLE", ""):
+ pass
rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%s%%" % cfg["PRODUCT_NAME"], g.get("PRODUCT_ADB_KEYS", "")))
- rblf.mkinfo("product.mk", rblf.dir("foo/bar"))
- rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][0])
- rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][-1])
- rblf.mkinfo("product.mk", rblf.dir("product.mk"))
- rblf.mkinfo("product.mk", rblf.dir(cfg["PRODUCT_COPY_FILES"][-1]))
- rblf.mkinfo("product.mk", rblf.dir((_foobar).split()[-1]))
+ rblf.mkinfo("product.mk", "$(dir foo/bar): %s" % rblf.dir("foo/bar"))
+ rblf.mkinfo("product.mk", rblf.first_word(cfg["PRODUCT_COPY_FILES"]))
+ rblf.mkinfo("product.mk", rblf.last_word(cfg["PRODUCT_COPY_FILES"]))
+ rblf.mkinfo("product.mk", rblf.dir(rblf.last_word("product.mk")))
+ rblf.mkinfo("product.mk", rblf.dir(rblf.last_word(cfg["PRODUCT_COPY_FILES"])))
+ rblf.mkinfo("product.mk", rblf.dir(rblf.last_word(_foobar)))
rblf.mkinfo("product.mk", rblf.abspath("foo/bar"))
rblf.mkinfo("product.mk", rblf.notdir("foo/bar"))
rblf.soong_config_namespace(g, "snsconfig")
@@ -883,6 +933,43 @@
`,
},
{
+ desc: "assigment setdefaults",
+ mkname: "product.mk",
+ in: `
+# All of these should have a setdefault because they're self-referential and not defined before
+PRODUCT_LIST1 = a $(PRODUCT_LIST1)
+PRODUCT_LIST2 ?= a $(PRODUCT_LIST2)
+PRODUCT_LIST3 += a
+
+# Now doing them again should not have a setdefault because they've already been set
+PRODUCT_LIST1 = a $(PRODUCT_LIST1)
+PRODUCT_LIST2 ?= a $(PRODUCT_LIST2)
+PRODUCT_LIST3 += a
+`,
+ expected: `# All of these should have a setdefault because they're self-referential and not defined before
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.setdefault(handle, "PRODUCT_LIST1")
+ cfg["PRODUCT_LIST1"] = (["a"] +
+ cfg.get("PRODUCT_LIST1", []))
+ if cfg.get("PRODUCT_LIST2") == None:
+ rblf.setdefault(handle, "PRODUCT_LIST2")
+ cfg["PRODUCT_LIST2"] = (["a"] +
+ cfg.get("PRODUCT_LIST2", []))
+ rblf.setdefault(handle, "PRODUCT_LIST3")
+ cfg["PRODUCT_LIST3"] += ["a"]
+ # Now doing them again should not have a setdefault because they've already been set
+ cfg["PRODUCT_LIST1"] = (["a"] +
+ cfg["PRODUCT_LIST1"])
+ if cfg.get("PRODUCT_LIST2") == None:
+ cfg["PRODUCT_LIST2"] = (["a"] +
+ cfg["PRODUCT_LIST2"])
+ cfg["PRODUCT_LIST3"] += ["a"]
+`,
+ },
+ {
desc: "soong namespace assignments",
mkname: "product.mk",
in: `
@@ -900,7 +987,7 @@
rblf.soong_config_namespace(g, "cvd")
rblf.soong_config_set(g, "cvd", "launch_configs", "cvd_config_auto.json")
rblf.soong_config_append(g, "cvd", "grub_config", "grub.cfg")
- rblf.mk2rbc_error("product.mk:7", "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: SOONG_CONFIG_cvd_grub_config")
+ _x = rblf.mk2rbc_error("product.mk:7", "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: SOONG_CONFIG_cvd_grub_config")
`,
}, {
desc: "soong namespace accesses",
@@ -963,19 +1050,22 @@
`,
},
{
- desc: "strip function",
+ desc: "strip/sort functions",
mkname: "product.mk",
in: `
ifeq ($(filter hwaddress,$(PRODUCT_PACKAGES)),)
PRODUCT_PACKAGES := $(strip $(PRODUCT_PACKAGES) hwaddress)
endif
+MY_VAR := $(sort b a c)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "hwaddress" not in cfg.get("PRODUCT_PACKAGES", []):
+ rblf.setdefault(handle, "PRODUCT_PACKAGES")
cfg["PRODUCT_PACKAGES"] = (rblf.mkstrip("%s hwaddress" % " ".join(cfg.get("PRODUCT_PACKAGES", [])))).split()
+ g["MY_VAR"] = rblf.mksort("b a c")
`,
},
{
@@ -1064,6 +1154,11 @@
MY_PATH:=foo
#RBC# include_top vendor/foo1
$(call inherit-product,$(MY_PATH)/cfg.mk)
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_OTHER_PATH))
+#RBC# include_top vendor/foo1
+$(foreach f,$(MY_MAKEFILES), \
+ $(call inherit-product,$(f)))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
@@ -1071,7 +1166,6 @@
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
- #RBC# include_top vendor/foo1
_entry = {
"vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
}.get("%s/cfg.mk" % g["MY_PATH"])
@@ -1079,6 +1173,21 @@
if not _varmod_init:
rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
rblf.inherit(handle, _varmod, _varmod_init)
+ _entry = {
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ }.get(g.get("MY_OTHER_PATH", ""))
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % (g.get("MY_OTHER_PATH", "")))
+ rblf.inherit(handle, _varmod, _varmod_init)
+ for f in rblf.words(g.get("MY_MAKEFILES", "")):
+ _entry = {
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ }.get(f)
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % (f))
+ rblf.inherit(handle, _varmod, _varmod_init)
`,
},
{
@@ -1089,6 +1198,7 @@
#RBC# include_top vendor/foo1
$(call inherit-product,$(MY_PATH)/cfg.mk)
#RBC# include_top vendor/foo1
+#RBC# include_top vendor/foo1
$(call inherit-product,$(MY_PATH)/cfg.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1097,7 +1207,6 @@
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
- #RBC# include_top vendor/foo1
_entry = {
"vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
}.get("%s/cfg.mk" % g["MY_PATH"])
@@ -1105,7 +1214,6 @@
if not _varmod_init:
rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
rblf.inherit(handle, _varmod, _varmod_init)
- #RBC# include_top vendor/foo1
_entry = {
"vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
}.get("%s/cfg.mk" % g["MY_PATH"])
@@ -1116,7 +1224,7 @@
`,
},
{
- desc: "Dynamic inherit path that lacks necessary hint",
+ desc: "Dynamic inherit path that lacks hint",
mkname: "product.mk",
in: `
#RBC# include_top foo
@@ -1130,9 +1238,9 @@
$(call inherit-product,$(MY_VAR)/font.mk)
`,
- expected: `#RBC# include_top foo
-load("//build/make/core:product_config.rbc", "rblf")
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//foo:font.star|init", _font_init = "init")
+load("//bar:font.star|init", _font1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
@@ -1143,7 +1251,6 @@
if not _varmod_init:
rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
rblf.inherit(handle, _varmod, _varmod_init)
- #RBC# include_top foo
# There's some space and even this comment between the include_top and the inherit-product
_entry = {
"foo/font.mk": ("foo/font", _font_init),
@@ -1152,20 +1259,30 @@
if not _varmod_init:
rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
rblf.inherit(handle, _varmod, _varmod_init)
- rblf.mk2rbc_error("product.mk:11", "inherit-product/include statements must not be prefixed with a variable, or must include a #RBC# include_top comment beforehand giving a root directory to search.")
+ rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
+ _entry = {
+ "foo/font.mk": ("foo/font", _font_init),
+ "bar/font.mk": ("bar/font", _font1_init),
+ }.get("%s/font.mk" % g.get("MY_VAR", ""))
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+ rblf.inherit(handle, _varmod, _varmod_init)
`,
},
{
desc: "Ignore make rules",
mkname: "product.mk",
in: `
+foo: PRIVATE_VARIABLE = some_tool $< $@
foo: foo.c
gcc -o $@ $*`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.mk2rbc_error("product.mk:2", "unsupported line rule: foo: foo.c\n#gcc -o $@ $*")
+ rblf.mk2rbc_error("product.mk:2", "Only simple variables are handled")
+ rblf.mk2rbc_error("product.mk:3", "unsupported line rule: foo: foo.c\n#gcc -o $@ $*")
`,
},
{
@@ -1178,7 +1295,6 @@
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.mk2rbc_error("product.mk:2", "cannot handle override directive")
- g["override FOO"] = ""
`,
},
{
@@ -1187,6 +1303,7 @@
in: `
ifeq (,$(call foobar))
endif
+my_sources := $(local-generated-sources-dir)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1194,6 +1311,7 @@
cfg = rblf.cfg(handle)
if rblf.mk2rbc_error("build/product.mk:2", "cannot handle invoking foobar"):
pass
+ _my_sources = rblf.mk2rbc_error("build/product.mk:4", "local-generated-sources-dir is not supported")
`,
},
{
@@ -1205,7 +1323,7 @@
TEST_VAR_LIST += bar
TEST_VAR_2 := $(if $(TEST_VAR),bar)
TEST_VAR_3 := $(if $(TEST_VAR),bar,baz)
-TEST_VAR_3 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
+TEST_VAR_4 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1216,7 +1334,7 @@
g["TEST_VAR_LIST"] += ["bar"]
g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "")
g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz")
- g["TEST_VAR_3"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
+ g["TEST_VAR_4"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
`,
},
{
@@ -1245,7 +1363,17 @@
BOOT_KERNEL_MODULES_LIST := foo.ko
BOOT_KERNEL_MODULES_LIST += bar.ko
BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m))
+NESTED_LISTS := $(foreach m,$(SOME_VAR),$(BOOT_KERNEL_MODULES_LIST))
+NESTED_LISTS_2 := $(foreach x,$(SOME_VAR),$(foreach y,$(x),prefix$(y)))
+FOREACH_WITH_IF := $(foreach module,\
+ $(BOOT_KERNEL_MODULES_LIST),\
+ $(if $(filter $(module),foo.ko),,$(error module "$(module)" has an error!)))
+
+# Same as above, but not assigning it to a variable allows it to be converted to statements
+$(foreach module,\
+ $(BOOT_KERNEL_MODULES_LIST),\
+ $(if $(filter $(module),foo.ko),,$(error module "$(module)" has an error!)))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1256,6 +1384,13 @@
g["BOOT_KERNEL_MODULES_LIST"] = ["foo.ko"]
g["BOOT_KERNEL_MODULES_LIST"] += ["bar.ko"]
g["BOOT_KERNEL_MODULES_FILTER_2"] = ["%%/%s" % m for m in g["BOOT_KERNEL_MODULES_LIST"]]
+ g["NESTED_LISTS"] = rblf.flatten_2d_list([g["BOOT_KERNEL_MODULES_LIST"] for m in rblf.words(g.get("SOME_VAR", ""))])
+ g["NESTED_LISTS_2"] = rblf.flatten_2d_list([["prefix%s" % y for y in rblf.words(x)] for x in rblf.words(g.get("SOME_VAR", ""))])
+ g["FOREACH_WITH_IF"] = [("" if rblf.filter(module, "foo.ko") else rblf.mkerror("product.mk", "module \"%s\" has an error!" % module)) for module in g["BOOT_KERNEL_MODULES_LIST"]]
+ # Same as above, but not assigning it to a variable allows it to be converted to statements
+ for module in g["BOOT_KERNEL_MODULES_LIST"]:
+ if not rblf.filter(module, "foo.ko"):
+ rblf.mkerror("product.mk", "module \"%s\" has an error!" % module)
`,
},
{
@@ -1342,6 +1477,149 @@
pass
`,
},
+ {
+ desc: "Type hints",
+ mkname: "product.mk",
+ in: `
+# Test type hints
+#RBC# type_hint list MY_VAR MY_VAR_2
+# Unsupported type
+#RBC# type_hint bool MY_VAR_3
+# Invalid syntax
+#RBC# type_hint list
+# Duplicated variable
+#RBC# type_hint list MY_VAR_2
+#RBC# type_hint list my-local-var-with-dashes
+#RBC# type_hint string MY_STRING_VAR
+
+MY_VAR := foo
+MY_VAR_UNHINTED := foo
+
+# Vars set after other statements still get the hint
+MY_VAR_2 := foo
+
+# You can't specify a type hint after the first statement
+#RBC# type_hint list MY_VAR_4
+MY_VAR_4 := foo
+
+my-local-var-with-dashes := foo
+
+MY_STRING_VAR := $(wildcard foo/bar.mk)
+`,
+ expected: `# Test type hints
+# Unsupported type
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.mk2rbc_error("product.mk:5", "Invalid type_hint annotation. Only list/string types are accepted, found bool")
+ # Invalid syntax
+ rblf.mk2rbc_error("product.mk:7", "Invalid type_hint annotation: list. Must be a variable type followed by a list of variables of that type")
+ # Duplicated variable
+ rblf.mk2rbc_error("product.mk:9", "Duplicate type hint for variable MY_VAR_2")
+ g["MY_VAR"] = ["foo"]
+ g["MY_VAR_UNHINTED"] = "foo"
+ # Vars set after other statements still get the hint
+ g["MY_VAR_2"] = ["foo"]
+ # You can't specify a type hint after the first statement
+ rblf.mk2rbc_error("product.mk:20", "type_hint annotations must come before the first Makefile statement")
+ g["MY_VAR_4"] = "foo"
+ _my_local_var_with_dashes = ["foo"]
+ g["MY_STRING_VAR"] = " ".join(rblf.expand_wildcard("foo/bar.mk"))
+`,
+ },
+ {
+ desc: "Set LOCAL_PATH to my-dir",
+ mkname: "product.mk",
+ in: `
+LOCAL_PATH := $(call my-dir)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+
+`,
+ },
+ {
+ desc: "Evals",
+ mkname: "product.mk",
+ in: `
+$(eval)
+$(eval MY_VAR := foo)
+$(eval # This is a test of eval functions)
+$(eval $(TOO_COMPLICATED) := bar)
+$(eval include foo/font.mk)
+$(eval $(call inherit-product,vendor/foo1/cfg.mk))
+
+$(foreach x,$(MY_LIST_VAR), \
+ $(eval PRODUCT_COPY_FILES += foo/bar/$(x):$(TARGET_COPY_OUT_VENDOR)/etc/$(x)) \
+ $(if $(MY_OTHER_VAR),$(eval PRODUCT_COPY_FILES += $(MY_OTHER_VAR):foo/bar/$(x))))
+
+$(foreach x,$(MY_LIST_VAR), \
+ $(eval include foo/$(x).mk))
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//foo:font.star", _font_init = "init")
+load("//vendor/foo1:cfg.star", _cfg_init = "init")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["MY_VAR"] = "foo"
+ # This is a test of eval functions
+ rblf.mk2rbc_error("product.mk:5", "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported")
+ _font_init(g, handle)
+ rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+ for x in rblf.words(g.get("MY_LIST_VAR", "")):
+ rblf.setdefault(handle, "PRODUCT_COPY_FILES")
+ cfg["PRODUCT_COPY_FILES"] += ("foo/bar/%s:%s/etc/%s" % (x, g.get("TARGET_COPY_OUT_VENDOR", ""), x)).split()
+ if g.get("MY_OTHER_VAR", ""):
+ cfg["PRODUCT_COPY_FILES"] += ("%s:foo/bar/%s" % (g.get("MY_OTHER_VAR", ""), x)).split()
+ for x in rblf.words(g.get("MY_LIST_VAR", "")):
+ _entry = {
+ "foo/font.mk": ("foo/font", _font_init),
+ }.get("foo/%s.mk" % x)
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("foo/%s.mk" % x))
+ _varmod_init(g, handle)
+`,
+ },
+ {
+ desc: ".KATI_READONLY",
+ mkname: "product.mk",
+ in: `
+MY_VAR := foo
+.KATI_READONLY := MY_VAR
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["MY_VAR"] = "foo"
+`,
+ },
+ {
+ desc: "Complicated variable references",
+ mkname: "product.mk",
+ in: `
+MY_VAR := foo
+MY_VAR_2 := MY_VAR
+MY_VAR_3 := $($(MY_VAR_2))
+MY_VAR_4 := $(foo bar)
+MY_VAR_5 := $($(MY_VAR_2) bar)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["MY_VAR"] = "foo"
+ g["MY_VAR_2"] = "MY_VAR"
+ g["MY_VAR_3"] = (cfg).get(g["MY_VAR_2"], (g).get(g["MY_VAR_2"], ""))
+ g["MY_VAR_4"] = rblf.mk2rbc_error("product.mk:5", "cannot handle invoking foo")
+ g["MY_VAR_5"] = rblf.mk2rbc_error("product.mk:6", "reference is too complex: $(MY_VAR_2) bar")
+`,
+ },
}
var known_variables = []struct {
@@ -1413,7 +1691,6 @@
ss, err := Convert(Request{
MkFile: test.mkname,
Reader: bytes.NewBufferString(test.in),
- RootDir: ".",
OutputSuffix: ".star",
SourceFS: fs,
MakefileFinder: &testMakefileFinder{fs: fs},
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index 4f7c4f0..a01abd8 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -83,9 +83,11 @@
}
type inheritedDynamicModule struct {
- path interpolateExpr
+ path starlarkExpr
candidateModules []*moduleInfo
loadAlways bool
+ location ErrorLocation
+ needsWarning bool
}
func (i inheritedDynamicModule) name() string {
@@ -97,6 +99,10 @@
}
func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
+ if i.needsWarning {
+ gctx.newLine()
+ gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
+ }
gctx.newLine()
gctx.writef("_entry = {")
gctx.indentLevel++
@@ -114,7 +120,7 @@
}
func (i inheritedDynamicModule) pathExpr() starlarkExpr {
- return &i.path
+ return i.path
}
func (i inheritedDynamicModule) needsLoadCheck() bool {
@@ -178,10 +184,9 @@
const (
// Assignment flavors
- asgnSet assignmentFlavor = iota // := or =
- asgnMaybeSet assignmentFlavor = iota // ?= and variable may be unset
- asgnAppend assignmentFlavor = iota // += and variable has been set before
- asgnMaybeAppend assignmentFlavor = iota // += and variable may be unset
+ asgnSet assignmentFlavor = iota // := or =
+ asgnMaybeSet assignmentFlavor = iota // ?=
+ asgnAppend assignmentFlavor = iota // +=
)
type assignmentNode struct {
@@ -191,7 +196,6 @@
flavor assignmentFlavor
location ErrorLocation
isTraced bool
- previous *assignmentNode
}
func (asgn *assignmentNode) emit(gctx *generationContext) {
@@ -204,11 +208,25 @@
gctx.newLine()
gctx.tracedCount++
gctx.writef(`print("%s.%d: %s := ", `, gctx.starScript.mkFile, gctx.tracedCount, asgn.lhs.name())
- asgn.lhs.emitGet(gctx, true)
+ asgn.lhs.emitGet(gctx)
gctx.writef(")")
}
}
+func (asgn *assignmentNode) isSelfReferential() bool {
+ if asgn.flavor == asgnAppend {
+ return true
+ }
+ isSelfReferential := false
+ asgn.value.transform(func(expr starlarkExpr) starlarkExpr {
+ if ref, ok := expr.(*variableRefExpr); ok && ref.ref.name() == asgn.lhs.name() {
+ isSelfReferential = true
+ }
+ return nil
+ })
+ return isSelfReferential
+}
+
type exprNode struct {
expr starlarkExpr
}
@@ -249,32 +267,22 @@
nodes []starlarkNode
}
-func (cb *switchCase) newNode(node starlarkNode) {
- cb.nodes = append(cb.nodes, node)
-}
-
func (cb *switchCase) emit(gctx *generationContext) {
cb.gate.emit(gctx)
gctx.indentLevel++
+ gctx.pushVariableAssignments()
hasStatements := false
- emitNode := func(node starlarkNode) {
+ for _, node := range cb.nodes {
if _, ok := node.(*commentNode); !ok {
hasStatements = true
}
node.emit(gctx)
}
- if len(cb.nodes) > 0 {
- emitNode(cb.nodes[0])
- for _, node := range cb.nodes[1:] {
- emitNode(node)
- }
- if !hasStatements {
- gctx.emitPass()
- }
- } else {
+ if !hasStatements {
gctx.emitPass()
}
gctx.indentLevel--
+ gctx.popVariableAssignments()
}
// A single complete if ... elseif ... else ... endif sequences
@@ -282,22 +290,35 @@
ssCases []*switchCase
}
-func (ssw *switchNode) newNode(node starlarkNode) {
- switch br := node.(type) {
- case *switchCase:
- ssw.ssCases = append(ssw.ssCases, br)
- default:
- panic(fmt.Errorf("expected switchCase node, got %t", br))
+func (ssw *switchNode) emit(gctx *generationContext) {
+ for _, ssCase := range ssw.ssCases {
+ ssCase.emit(gctx)
}
}
-func (ssw *switchNode) emit(gctx *generationContext) {
- if len(ssw.ssCases) == 0 {
- gctx.emitPass()
- } else {
- ssw.ssCases[0].emit(gctx)
- for _, ssCase := range ssw.ssCases[1:] {
- ssCase.emit(gctx)
+type foreachNode struct {
+ varName string
+ list starlarkExpr
+ actions []starlarkNode
+}
+
+func (f *foreachNode) emit(gctx *generationContext) {
+ gctx.pushVariableAssignments()
+ gctx.newLine()
+ gctx.writef("for %s in ", f.varName)
+ f.list.emit(gctx)
+ gctx.write(":")
+ gctx.indentLevel++
+ hasStatements := false
+ for _, a := range f.actions {
+ if _, ok := a.(*commentNode); !ok {
+ hasStatements = true
}
+ a.emit(gctx)
}
+ if !hasStatements {
+ gctx.emitPass()
+ }
+ gctx.indentLevel--
+ gctx.popVariableAssignments()
}
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index f7adca5..0a26ed8 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -21,9 +21,8 @@
type variable interface {
name() string
- emitGet(gctx *generationContext, isDefined bool)
+ emitGet(gctx *generationContext)
emitSet(gctx *generationContext, asgn *assignmentNode)
- emitDefined(gctx *generationContext)
valueType() starlarkType
setValueType(t starlarkType)
defaultValueString() string
@@ -74,13 +73,11 @@
func (pcv productConfigVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
emitAssignment := func() {
- pcv.emitGet(gctx, true)
- gctx.write(" = ")
+ gctx.writef("cfg[%q] = ", pcv.nam)
asgn.value.emitListVarCopy(gctx)
}
emitAppend := func() {
- pcv.emitGet(gctx, true)
- gctx.write(" += ")
+ gctx.writef("cfg[%q] += ", pcv.nam)
value := asgn.value
if pcv.valueType() == starlarkTypeString {
gctx.writef(`" " + `)
@@ -88,56 +85,63 @@
}
value.emit(gctx)
}
-
- switch asgn.flavor {
- case asgnSet:
- emitAssignment()
- case asgnAppend:
- emitAppend()
- case asgnMaybeAppend:
- // If we are not sure variable has been assigned before, emit setdefault
+ emitSetDefault := func() {
if pcv.typ == starlarkTypeList {
gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
} else {
gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
}
gctx.newLine()
+ }
+
+ // If we are not sure variable has been assigned before, emit setdefault
+ needsSetDefault := !gctx.hasBeenAssigned(&pcv) && !pcv.isPreset() && asgn.isSelfReferential()
+
+ switch asgn.flavor {
+ case asgnSet:
+ if needsSetDefault {
+ emitSetDefault()
+ }
+ emitAssignment()
+ case asgnAppend:
+ if needsSetDefault {
+ emitSetDefault()
+ }
emitAppend()
case asgnMaybeSet:
gctx.writef("if cfg.get(%q) == None:", pcv.nam)
gctx.indentLevel++
gctx.newLine()
+ if needsSetDefault {
+ emitSetDefault()
+ }
emitAssignment()
gctx.indentLevel--
}
+
+ gctx.setHasBeenAssigned(&pcv)
}
-func (pcv productConfigVariable) emitGet(gctx *generationContext, isDefined bool) {
- if isDefined || pcv.isPreset() {
+func (pcv productConfigVariable) emitGet(gctx *generationContext) {
+ if gctx.hasBeenAssigned(&pcv) || pcv.isPreset() {
gctx.writef("cfg[%q]", pcv.nam)
} else {
gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString())
}
}
-func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
- gctx.writef("g.get(%q) != None", pcv.name())
-}
-
type otherGlobalVariable struct {
baseVariable
}
func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
emitAssignment := func() {
- scv.emitGet(gctx, true)
- gctx.write(" = ")
+ gctx.writef("g[%q] = ", scv.nam)
asgn.value.emitListVarCopy(gctx)
}
emitAppend := func() {
- scv.emitGet(gctx, true)
- gctx.write(" += ")
+ gctx.writef("g[%q] += ", scv.nam)
value := asgn.value
if scv.valueType() == starlarkTypeString {
gctx.writef(`" " + `)
@@ -146,75 +150,70 @@
value.emit(gctx)
}
+ // If we are not sure variable has been assigned before, emit setdefault
+ needsSetDefault := !gctx.hasBeenAssigned(&scv) && !scv.isPreset() && asgn.isSelfReferential()
+
switch asgn.flavor {
case asgnSet:
+ if needsSetDefault {
+ gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+ gctx.newLine()
+ }
emitAssignment()
case asgnAppend:
- emitAppend()
- case asgnMaybeAppend:
- // If we are not sure variable has been assigned before, emit setdefault
- gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
- gctx.newLine()
+ if needsSetDefault {
+ gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+ gctx.newLine()
+ }
emitAppend()
case asgnMaybeSet:
gctx.writef("if g.get(%q) == None:", scv.nam)
gctx.indentLevel++
gctx.newLine()
+ if needsSetDefault {
+ gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+ gctx.newLine()
+ }
emitAssignment()
gctx.indentLevel--
}
+
+ gctx.setHasBeenAssigned(&scv)
}
-func (scv otherGlobalVariable) emitGet(gctx *generationContext, isDefined bool) {
- if isDefined || scv.isPreset() {
+func (scv otherGlobalVariable) emitGet(gctx *generationContext) {
+ if gctx.hasBeenAssigned(&scv) || scv.isPreset() {
gctx.writef("g[%q]", scv.nam)
} else {
gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString())
}
}
-func (scv otherGlobalVariable) emitDefined(gctx *generationContext) {
- gctx.writef("g.get(%q) != None", scv.name())
-}
-
type localVariable struct {
baseVariable
}
-func (lv localVariable) emitDefined(gctx *generationContext) {
- gctx.writef(lv.String())
-}
-
func (lv localVariable) String() string {
return "_" + lv.nam
}
func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
switch asgn.flavor {
- case asgnSet:
+ case asgnSet, asgnMaybeSet:
gctx.writef("%s = ", lv)
asgn.value.emitListVarCopy(gctx)
case asgnAppend:
- lv.emitGet(gctx, false)
- gctx.write(" += ")
+ gctx.writef("%s += ", lv)
value := asgn.value
if lv.valueType() == starlarkTypeString {
gctx.writef(`" " + `)
value = &toStringExpr{expr: value}
}
value.emit(gctx)
- case asgnMaybeAppend:
- gctx.writef("%s(%q, ", cfnLocalAppend, lv)
- asgn.value.emit(gctx)
- gctx.write(")")
- case asgnMaybeSet:
- gctx.writef("%s(%q, ", cfnLocalSetDefault, lv)
- asgn.value.emit(gctx)
- gctx.write(")")
}
}
-func (lv localVariable) emitGet(gctx *generationContext, _ bool) {
+func (lv localVariable) emitGet(gctx *generationContext) {
gctx.writef("%s", lv)
}
@@ -223,7 +222,7 @@
value starlarkExpr
}
-func (pv predefinedVariable) emitGet(gctx *generationContext, _ bool) {
+func (pv predefinedVariable) emitGet(gctx *generationContext) {
pv.value.emit(gctx)
}
@@ -244,10 +243,6 @@
panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump()))
}
-func (pv predefinedVariable) emitDefined(gctx *generationContext) {
- gctx.write("True")
-}
-
var localProductConfigVariables = map[string]string{
"LOCAL_AUDIO_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
"LOCAL_AUDIO_PRODUCT_COPY_FILES": "PRODUCT_COPY_FILES",
@@ -278,31 +273,41 @@
// addVariable returns a variable with a given name. A variable is
// added if it does not exist yet.
func (ctx *parseContext) addVariable(name string) variable {
+ // Get the hintType before potentially changing the variable name
+ var hintType starlarkType
+ var ok bool
+ if hintType, ok = ctx.typeHints[name]; !ok {
+ hintType = starlarkTypeUnknown
+ }
+ // Heuristics: if variable's name is all lowercase, consider it local
+ // string variable.
+ isLocalVariable := name == strings.ToLower(name)
+ // Local variables can't have special characters in them, because they
+ // will be used as starlark identifiers
+ if isLocalVariable {
+ name = strings.ReplaceAll(strings.TrimSpace(name), "-", "_")
+ }
v, found := ctx.variables[name]
if !found {
- _, preset := presetVariables[name]
if vi, found := KnownVariables[name]; found {
+ _, preset := presetVariables[name]
switch vi.class {
case VarClassConfig:
v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
case VarClassSoong:
v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
}
- } else if name == strings.ToLower(name) {
- // Heuristics: if variable's name is all lowercase, consider it local
- // string variable.
- v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
+ } else if isLocalVariable {
+ v = &localVariable{baseVariable{nam: name, typ: hintType}}
} else {
- vt := starlarkTypeUnknown
- if strings.HasPrefix(name, "LOCAL_") {
- // Heuristics: local variables that contribute to corresponding config variables
- if cfgVarName, found := localProductConfigVariables[name]; found {
- vi, found2 := KnownVariables[cfgVarName]
- if !found2 {
- panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
- }
- vt = vi.valueType
+ vt := hintType
+ // Heuristics: local variables that contribute to corresponding config variables
+ if cfgVarName, found := localProductConfigVariables[name]; found && vt == starlarkTypeUnknown {
+ vi, found2 := KnownVariables[cfgVarName]
+ if !found2 {
+ panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
}
+ vt = vi.valueType
}
if strings.HasSuffix(name, "_LIST") && vt == starlarkTypeUnknown {
// Heuristics: Variables with "_LIST" suffix are lists
diff --git a/multitree/Android.bp b/multitree/Android.bp
new file mode 100644
index 0000000..9b16d20
--- /dev/null
+++ b/multitree/Android.bp
@@ -0,0 +1,19 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-multitree",
+ pkgPath: "android/soong/multitree",
+ deps: [
+ "blueprint",
+ "soong-android",
+ ],
+ srcs: [
+ "api_surface.go",
+ "export.go",
+ "metadata.go",
+ "import.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/multitree/api_surface.go b/multitree/api_surface.go
new file mode 100644
index 0000000..f739a24
--- /dev/null
+++ b/multitree/api_surface.go
@@ -0,0 +1,119 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multitree
+
+import (
+ "android/soong/android"
+ "fmt"
+
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("android/soong/multitree")
+)
+
+func init() {
+ RegisterApiSurfaceBuildComponents(android.InitRegistrationContext)
+}
+
+var PrepareForTestWithApiSurface = android.FixtureRegisterWithContext(RegisterApiSurfaceBuildComponents)
+
+func RegisterApiSurfaceBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("api_surface", ApiSurfaceFactory)
+}
+
+type ApiSurface struct {
+ android.ModuleBase
+ ExportableModuleBase
+ properties apiSurfaceProperties
+
+ allOutputs android.Paths
+ taggedOutputs map[string]android.Paths
+}
+
+type apiSurfaceProperties struct {
+ Contributions []string
+}
+
+func ApiSurfaceFactory() android.Module {
+ module := &ApiSurface{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ InitExportableModule(module)
+ return module
+}
+
+func (surface *ApiSurface) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if surface.properties.Contributions != nil {
+ ctx.AddVariationDependencies(nil, nil, surface.properties.Contributions...)
+ }
+
+}
+func (surface *ApiSurface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ contributionFiles := make(map[string]android.Paths)
+ var allOutputs android.Paths
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ if contribution, ok := child.(ApiContribution); ok {
+ copied := contribution.CopyFilesWithTag(ctx)
+ for tag, files := range copied {
+ contributionFiles[child.Name()+"#"+tag] = files
+ }
+ for _, paths := range copied {
+ allOutputs = append(allOutputs, paths...)
+ }
+ return false // no transitive dependencies
+ }
+ return false
+ })
+
+ // phony target
+ ctx.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Output: android.PathForPhony(ctx, ctx.ModuleName()),
+ Inputs: allOutputs,
+ })
+
+ surface.allOutputs = allOutputs
+ surface.taggedOutputs = contributionFiles
+}
+
+func (surface *ApiSurface) OutputFiles(tag string) (android.Paths, error) {
+ if tag != "" {
+ return nil, fmt.Errorf("unknown tag: %q", tag)
+ }
+ return surface.allOutputs, nil
+}
+
+func (surface *ApiSurface) TaggedOutputs() map[string]android.Paths {
+ return surface.taggedOutputs
+}
+
+func (surface *ApiSurface) Exportable() bool {
+ return true
+}
+
+var _ android.OutputFileProducer = (*ApiSurface)(nil)
+var _ Exportable = (*ApiSurface)(nil)
+
+type ApiContribution interface {
+ // copy files necessaryt to construct an API surface
+ // For C, it will be map.txt and .h files
+ // For Java, it will be api.txt
+ CopyFilesWithTag(ctx android.ModuleContext) map[string]android.Paths // output paths
+
+ // Generate Android.bp in out/ to use the exported .txt files
+ // GenerateBuildFiles(ctx ModuleContext) Paths //output paths
+}
diff --git a/multitree/export.go b/multitree/export.go
new file mode 100644
index 0000000..aecade5
--- /dev/null
+++ b/multitree/export.go
@@ -0,0 +1,67 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multitree
+
+import (
+ "android/soong/android"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type moduleExportProperty struct {
+ // True if the module is exported to the other components in a multi-tree.
+ // Any components in the multi-tree can import this module to use.
+ Export *bool
+}
+
+type ExportableModuleBase struct {
+ properties moduleExportProperty
+}
+
+type Exportable interface {
+ // Properties for the exporable module.
+ exportableModuleProps() *moduleExportProperty
+
+ // Check if this module can be exported.
+ // If this returns false, the module will not be exported regardless of the 'export' value.
+ Exportable() bool
+
+ // Returns 'true' if this module has 'export: true'
+ // This module will not be exported if it returns 'false' to 'Exportable()' interface even if
+ // it has 'export: true'.
+ IsExported() bool
+
+ // Map from tags to outputs.
+ // Each module can tag their outputs for convenience.
+ TaggedOutputs() map[string]android.Paths
+}
+
+type ExportableModule interface {
+ android.Module
+ android.OutputFileProducer
+ Exportable
+}
+
+func InitExportableModule(module ExportableModule) {
+ module.AddProperties(module.exportableModuleProps())
+}
+
+func (m *ExportableModuleBase) exportableModuleProps() *moduleExportProperty {
+ return &m.properties
+}
+
+func (m *ExportableModuleBase) IsExported() bool {
+ return proptools.Bool(m.properties.Export)
+}
diff --git a/multitree/import.go b/multitree/import.go
new file mode 100644
index 0000000..1e5c421
--- /dev/null
+++ b/multitree/import.go
@@ -0,0 +1,96 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multitree
+
+import (
+ "android/soong/android"
+)
+
+var (
+ nameSuffix = ".imported"
+)
+
+type MultitreeImportedModuleInterface interface {
+ GetMultitreeImportedModuleName() string
+}
+
+func init() {
+ android.RegisterModuleType("imported_filegroup", importedFileGroupFactory)
+
+ android.PreArchMutators(RegisterMultitreePreArchMutators)
+}
+
+type importedFileGroupProperties struct {
+ // Imported modules from the other components in a multi-tree
+ Imported []string
+}
+
+type importedFileGroup struct {
+ android.ModuleBase
+
+ properties importedFileGroupProperties
+ srcs android.Paths
+}
+
+func (ifg *importedFileGroup) Name() string {
+ return ifg.BaseModuleName() + nameSuffix
+}
+
+func importedFileGroupFactory() android.Module {
+ module := &importedFileGroup{}
+ module.AddProperties(&module.properties)
+
+ android.InitAndroidModule(module)
+ return module
+}
+
+var _ MultitreeImportedModuleInterface = (*importedFileGroup)(nil)
+
+func (ifg *importedFileGroup) GetMultitreeImportedModuleName() string {
+ // The base module name of the imported filegroup is used as the imported module name
+ return ifg.BaseModuleName()
+}
+
+var _ android.SourceFileProducer = (*importedFileGroup)(nil)
+
+func (ifg *importedFileGroup) Srcs() android.Paths {
+ return ifg.srcs
+}
+
+func (ifg *importedFileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // srcs from this module must not be used. Adding a dot path to avoid the empty
+ // source failure. Still soong returns error when a module wants to build against
+ // this source, which is intended.
+ ifg.srcs = android.PathsForModuleSrc(ctx, []string{"."})
+}
+
+func RegisterMultitreePreArchMutators(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("multitree_imported_rename", MultitreeImportedRenameMutator).Parallel()
+}
+
+func MultitreeImportedRenameMutator(ctx android.BottomUpMutatorContext) {
+ if m, ok := ctx.Module().(MultitreeImportedModuleInterface); ok {
+ name := m.GetMultitreeImportedModuleName()
+ if !ctx.OtherModuleExists(name) {
+ // Provide an empty filegroup not to break the build while updating the metadata.
+ // In other cases, soong will report an error to guide users to run 'm update-meta'
+ // first.
+ if !ctx.Config().TargetMultitreeUpdateMeta() {
+ ctx.ModuleErrorf("\"%s\" filegroup must be imported.\nRun 'm update-meta' first to import the filegroup.", name)
+ }
+ ctx.Rename(name)
+ }
+ }
+}
diff --git a/multitree/metadata.go b/multitree/metadata.go
new file mode 100644
index 0000000..3fd7215
--- /dev/null
+++ b/multitree/metadata.go
@@ -0,0 +1,74 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multitree
+
+import (
+ "android/soong/android"
+ "encoding/json"
+)
+
+func init() {
+ android.RegisterSingletonType("update-meta", UpdateMetaSingleton)
+}
+
+func UpdateMetaSingleton() android.Singleton {
+ return &updateMetaSingleton{}
+}
+
+type jsonImported struct {
+ FileGroups map[string][]string `json:",omitempty"`
+}
+
+type metadataJsonFlags struct {
+ Imported jsonImported `json:",omitempty"`
+ Exported map[string][]string `json:",omitempty"`
+}
+
+type updateMetaSingleton struct {
+ importedModules []string
+ generatedMetadataFile android.OutputPath
+}
+
+func (s *updateMetaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ metadata := metadataJsonFlags{
+ Imported: jsonImported{
+ FileGroups: make(map[string][]string),
+ },
+ Exported: make(map[string][]string),
+ }
+ ctx.VisitAllModules(func(module android.Module) {
+ if ifg, ok := module.(*importedFileGroup); ok {
+ metadata.Imported.FileGroups[ifg.BaseModuleName()] = ifg.properties.Imported
+ }
+ if e, ok := module.(ExportableModule); ok {
+ if e.IsExported() && e.Exportable() {
+ for tag, files := range e.TaggedOutputs() {
+ // TODO(b/219846705): refactor this to a dictionary
+ metadata.Exported[e.Name()+":"+tag] = append(metadata.Exported[e.Name()+":"+tag], files.Strings()...)
+ }
+ }
+ }
+ })
+ jsonStr, err := json.Marshal(metadata)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+ s.generatedMetadataFile = android.PathForOutput(ctx, "multitree", "metadata.json")
+ android.WriteFileRule(ctx, s.generatedMetadataFile, string(jsonStr))
+}
+
+func (s *updateMetaSingleton) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict("MULTITREE_METADATA", s.generatedMetadataFile.String())
+}
diff --git a/provenance/Android.bp b/provenance/Android.bp
new file mode 100644
index 0000000..6fd67aa
--- /dev/null
+++ b/provenance/Android.bp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-provenance",
+ pkgPath: "android/soong/provenance",
+ srcs: [
+ "provenance_singleton.go",
+ ],
+ deps: [
+ "soong-android",
+ ],
+ testSrcs: [
+ "provenance_singleton_test.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+}
diff --git a/provenance/provenance_metadata_proto/Android.bp b/provenance/provenance_metadata_proto/Android.bp
new file mode 100644
index 0000000..7fc47a9
--- /dev/null
+++ b/provenance/provenance_metadata_proto/Android.bp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_library_host {
+ name: "provenance_metadata_proto",
+ version: {
+ py3: {
+ enabled: true,
+ },
+ },
+ srcs: [
+ "provenance_metadata.proto",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
diff --git a/provenance/provenance_metadata_proto/provenance_metadata.proto b/provenance/provenance_metadata_proto/provenance_metadata.proto
new file mode 100644
index 0000000..f42aba7
--- /dev/null
+++ b/provenance/provenance_metadata_proto/provenance_metadata.proto
@@ -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.
+ */
+
+syntax = "proto3";
+
+package provenance_metadata_proto;
+option go_package = "android/soong/provenance/provenance_metadata_proto";
+
+// Provenance metadata of artifacts.
+message ProvenanceMetadata {
+ // Name of the module/target that creates the artifact.
+ // It is either a Soong module name or Bazel target label.
+ string module_name = 1;
+
+ // The path to the prebuilt artifacts, which is relative to the source tree
+ // directory. For example, “prebuilts/runtime/mainline/i18n/apex/com.android.i18n-arm.apex”.
+ string artifact_path = 2;
+
+ // The SHA256 hash of the artifact.
+ string artifact_sha256 = 3;
+
+ // The install path of the artifact in filesystem images.
+ // This is the absolute path of the artifact on the device.
+ string artifact_install_path = 4;
+
+ // Path of the attestation file of a prebuilt artifact, which is relative to
+ // the source tree directory. This is for prebuilt artifacts which have
+ // corresponding attestation files checked in the source tree.
+ string attestation_path = 5;
+}
+
+message ProvenanceMetaDataList {
+ repeated ProvenanceMetadata metadata = 1;
+}
\ No newline at end of file
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
new file mode 100644
index 0000000..d1cbd8f
--- /dev/null
+++ b/provenance/provenance_singleton.go
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package provenance
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("android/soong/provenance")
+ rule = pctx.HostBinToolVariable("gen_provenance_metadata", "gen_provenance_metadata")
+
+ genProvenanceMetaData = pctx.AndroidStaticRule("genProvenanceMetaData",
+ blueprint.RuleParams{
+ Command: `rm -rf "$out" && ` +
+ `${gen_provenance_metadata} --module_name=${module_name} ` +
+ `--artifact_path=$in --install_path=${install_path} --metadata_path=$out`,
+ CommandDeps: []string{"${gen_provenance_metadata}"},
+ }, "module_name", "install_path")
+
+ mergeProvenanceMetaData = pctx.AndroidStaticRule("mergeProvenanceMetaData",
+ blueprint.RuleParams{
+ Command: `rm -rf $out $out.temp && ` +
+ `echo "# proto-file: build/soong/provenance/proto/provenance_metadata.proto" > $out && ` +
+ `echo "# proto-message: ProvenanceMetaDataList" >> $out && ` +
+ `touch $out.temp && cat $out.temp $in | grep -v "^#.*" >> $out && rm -rf $out.temp`,
+ })
+)
+
+type ProvenanceMetadata interface {
+ ProvenanceMetaDataFile() android.OutputPath
+}
+
+func init() {
+ RegisterProvenanceSingleton(android.InitRegistrationContext)
+}
+
+func RegisterProvenanceSingleton(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
+}
+
+var PrepareForTestWithProvenanceSingleton = android.FixtureRegisterWithContext(RegisterProvenanceSingleton)
+
+func provenanceInfoSingletonFactory() android.Singleton {
+ return &provenanceInfoSingleton{}
+}
+
+type provenanceInfoSingleton struct {
+ mergedMetaDataFile android.OutputPath
+}
+
+func (p *provenanceInfoSingleton) GenerateBuildActions(context android.SingletonContext) {
+ allMetaDataFiles := make([]android.Path, 0)
+ context.VisitAllModulesIf(moduleFilter, func(module android.Module) {
+ if p, ok := module.(ProvenanceMetadata); ok {
+ allMetaDataFiles = append(allMetaDataFiles, p.ProvenanceMetaDataFile())
+ }
+ })
+ p.mergedMetaDataFile = android.PathForOutput(context, "provenance_metadata.textproto")
+ context.Build(pctx, android.BuildParams{
+ Rule: mergeProvenanceMetaData,
+ Description: "merge provenance metadata",
+ Inputs: allMetaDataFiles,
+ Output: p.mergedMetaDataFile,
+ })
+
+ context.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Description: "phony rule of merge provenance metadata",
+ Inputs: []android.Path{p.mergedMetaDataFile},
+ Output: android.PathForPhony(context, "provenance_metadata"),
+ })
+
+ context.Phony("droidcore", android.PathForPhony(context, "provenance_metadata"))
+}
+
+func moduleFilter(module android.Module) bool {
+ if !module.Enabled() || module.IsSkipInstall() {
+ return false
+ }
+ if p, ok := module.(ProvenanceMetadata); ok {
+ return p.ProvenanceMetaDataFile().String() != ""
+ }
+ return false
+}
+
+func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.OutputPath {
+ onDevicePathOfInstalledFile := android.InstallPathToOnDevicePath(ctx, installedFile)
+ artifactMetaDataFile := android.PathForIntermediates(ctx, "provenance_metadata", ctx.ModuleDir(), ctx.ModuleName(), "provenance_metadata.textproto")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: genProvenanceMetaData,
+ Description: "generate artifact provenance metadata",
+ Inputs: []android.Path{artifactPath},
+ Output: artifactMetaDataFile,
+ Args: map[string]string{
+ "module_name": ctx.ModuleName(),
+ "install_path": onDevicePathOfInstalledFile,
+ }})
+
+ return artifactMetaDataFile
+}
+
+func (p *provenanceInfoSingleton) MakeVars(ctx android.MakeVarsContext) {
+ ctx.DistForGoal("droidcore", p.mergedMetaDataFile)
+}
+
+var _ android.SingletonMakeVarsProvider = (*provenanceInfoSingleton)(nil)
diff --git a/provenance/provenance_singleton_test.go b/provenance/provenance_singleton_test.go
new file mode 100644
index 0000000..0f1eae2
--- /dev/null
+++ b/provenance/provenance_singleton_test.go
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package provenance
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestProvenanceSingleton(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithProvenanceSingleton,
+ android.PrepareForTestWithAndroidMk).RunTestWithBp(t, "")
+
+ outputs := result.SingletonForTests("provenance_metadata_singleton").AllOutputs()
+ for _, output := range outputs {
+ testingBuildParam := result.SingletonForTests("provenance_metadata_singleton").Output(output)
+ switch {
+ case strings.Contains(output, "soong/provenance_metadata.textproto"):
+ android.AssertStringEquals(t, "Invalid build rule", "android/soong/provenance.mergeProvenanceMetaData", testingBuildParam.Rule.String())
+ android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0)
+ android.AssertStringDoesContain(t, "Invalid output path", output, "soong/provenance_metadata.textproto")
+ android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
+
+ case strings.HasSuffix(output, "provenance_metadata"):
+ android.AssertStringEquals(t, "Invalid build rule", "<builtin>:phony", testingBuildParam.Rule.String())
+ android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/provenance_metadata.textproto")
+ android.AssertStringEquals(t, "Invalid output path", output, "provenance_metadata")
+ android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
+ }
+ }
+}
diff --git a/provenance/tools/Android.bp b/provenance/tools/Android.bp
new file mode 100644
index 0000000..0eddd76
--- /dev/null
+++ b/provenance/tools/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_binary_host {
+ name: "gen_provenance_metadata",
+ srcs: [
+ "gen_provenance_metadata.py",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+ libs: [
+ "provenance_metadata_proto",
+ "libprotobuf-python",
+ ],
+}
+
+python_test_host {
+ name: "gen_provenance_metadata_test",
+ main: "gen_provenance_metadata_test.py",
+ srcs: [
+ "gen_provenance_metadata_test.py",
+ ],
+ data: [
+ ":gen_provenance_metadata",
+ ],
+ libs: [
+ "provenance_metadata_proto",
+ "libprotobuf-python",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/provenance/tools/gen_provenance_metadata.py b/provenance/tools/gen_provenance_metadata.py
new file mode 100644
index 0000000..f3f4d1f
--- /dev/null
+++ b/provenance/tools/gen_provenance_metadata.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import hashlib
+import os.path
+import sys
+
+import google.protobuf.text_format as text_format
+import provenance_metadata_pb2
+
+def Log(*info):
+ if args.verbose:
+ for i in info:
+ print(i)
+
+def ParseArgs(argv):
+ parser = argparse.ArgumentParser(description='Create provenance metadata for a prebuilt artifact')
+ parser.add_argument('-v', '--verbose', action='store_true', help='Print more information in execution')
+ parser.add_argument('--module_name', help='Module name', required=True)
+ parser.add_argument('--artifact_path', help='Relative path of the prebuilt artifact in source tree', required=True)
+ parser.add_argument('--install_path', help='Absolute path of the artifact in the filesystem images', required=True)
+ parser.add_argument('--metadata_path', help='Path of the provenance metadata file created for the artifact', required=True)
+ return parser.parse_args(argv)
+
+def main(argv):
+ global args
+ args = ParseArgs(argv)
+ Log("Args:", vars(args))
+
+ provenance_metadata = provenance_metadata_pb2.ProvenanceMetadata()
+ provenance_metadata.module_name = args.module_name
+ provenance_metadata.artifact_path = args.artifact_path
+ provenance_metadata.artifact_install_path = args.install_path
+
+ Log("Generating SHA256 hash")
+ h = hashlib.sha256()
+ with open(args.artifact_path, "rb") as artifact_file:
+ h.update(artifact_file.read())
+ provenance_metadata.artifact_sha256 = h.hexdigest()
+
+ Log("Check if there is attestation for the artifact")
+ attestation_file_name = args.artifact_path + ".intoto.jsonl"
+ if os.path.isfile(attestation_file_name):
+ provenance_metadata.attestation_path = attestation_file_name
+
+ text_proto = [
+ "# proto-file: build/soong/provenance/proto/provenance_metadata.proto",
+ "# proto-message: ProvenanceMetaData",
+ "",
+ text_format.MessageToString(provenance_metadata)
+ ]
+ with open(args.metadata_path, "wt") as metadata_file:
+ file_content = "\n".join(text_proto)
+ Log("Writing provenance metadata in textproto:", file_content)
+ metadata_file.write(file_content)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/provenance/tools/gen_provenance_metadata_test.py b/provenance/tools/gen_provenance_metadata_test.py
new file mode 100644
index 0000000..1f69b8f
--- /dev/null
+++ b/provenance/tools/gen_provenance_metadata_test.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import hashlib
+import logging
+import os
+import subprocess
+import tempfile
+import unittest
+
+import google.protobuf.text_format as text_format
+import provenance_metadata_pb2
+
+logger = logging.getLogger(__name__)
+
+def run(args, verbose=None, **kwargs):
+ """Creates and returns a subprocess.Popen object.
+
+ Args:
+ args: The command represented as a list of strings.
+ verbose: Whether the commands should be shown. Default to the global
+ verbosity if unspecified.
+ kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
+ stdin, etc. stdout and stderr will default to subprocess.PIPE and
+ subprocess.STDOUT respectively unless caller specifies any of them.
+ universal_newlines will default to True, as most of the users in
+ releasetools expect string output.
+
+ Returns:
+ A subprocess.Popen object.
+ """
+ if 'stdout' not in kwargs and 'stderr' not in kwargs:
+ kwargs['stdout'] = subprocess.PIPE
+ kwargs['stderr'] = subprocess.STDOUT
+ if 'universal_newlines' not in kwargs:
+ kwargs['universal_newlines'] = True
+ if verbose:
+ logger.info(" Running: \"%s\"", " ".join(args))
+ return subprocess.Popen(args, **kwargs)
+
+
+def run_and_check_output(args, verbose=None, **kwargs):
+ """Runs the given command and returns the output.
+
+ Args:
+ args: The command represented as a list of strings.
+ verbose: Whether the commands should be shown. Default to the global
+ verbosity if unspecified.
+ kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
+ stdin, etc. stdout and stderr will default to subprocess.PIPE and
+ subprocess.STDOUT respectively unless caller specifies any of them.
+
+ Returns:
+ The output string.
+
+ Raises:
+ ExternalError: On non-zero exit from the command.
+ """
+ proc = run(args, verbose=verbose, **kwargs)
+ output, _ = proc.communicate()
+ if output is None:
+ output = ""
+ if verbose:
+ logger.info("%s", output.rstrip())
+ if proc.returncode != 0:
+ raise RuntimeError(
+ "Failed to run command '{}' (exit code {}):\n{}".format(
+ args, proc.returncode, output))
+ return output
+
+def run_host_command(args, verbose=None, **kwargs):
+ host_build_top = os.environ.get("ANDROID_BUILD_TOP")
+ if host_build_top:
+ host_command_dir = os.path.join(host_build_top, "out/host/linux-x86/bin")
+ args[0] = os.path.join(host_command_dir, args[0])
+ return run_and_check_output(args, verbose, **kwargs)
+
+def sha256(s):
+ h = hashlib.sha256()
+ h.update(bytearray(s, 'utf-8'))
+ return h.hexdigest()
+
+class ProvenanceMetaDataToolTest(unittest.TestCase):
+
+ def test_gen_provenance_metadata(self):
+ artifact_content = "test artifact"
+ artifact_file = tempfile.mktemp()
+ with open(artifact_file,"wt") as f:
+ f.write(artifact_content)
+
+ attestation_file = artifact_file + ".intoto.jsonl"
+ with open(attestation_file, "wt") as af:
+ af.write("attestation file")
+
+ metadata_file = tempfile.mktemp()
+ cmd = ["gen_provenance_metadata"]
+ cmd.extend(["--module_name", "a"])
+ cmd.extend(["--artifact_path", artifact_file])
+ cmd.extend(["--install_path", "b"])
+ cmd.extend(["--metadata_path", metadata_file])
+ output = run_host_command(cmd)
+ self.assertEqual(output, "")
+
+ with open(metadata_file,"rt") as f:
+ data = f.read()
+ provenance_metadata = provenance_metadata_pb2.ProvenanceMetadata()
+ text_format.Parse(data, provenance_metadata)
+ self.assertEqual(provenance_metadata.module_name, "a")
+ self.assertEqual(provenance_metadata.artifact_path, artifact_file)
+ self.assertEqual(provenance_metadata.artifact_install_path, "b")
+ self.assertEqual(provenance_metadata.artifact_sha256, sha256(artifact_content))
+ self.assertEqual(provenance_metadata.attestation_path, attestation_file)
+
+ os.remove(artifact_file)
+ os.remove(metadata_file)
+ os.remove(attestation_file)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
\ No newline at end of file
diff --git a/python/library.go b/python/library.go
index d026c13..5071b74 100644
--- a/python/library.go
+++ b/python/library.go
@@ -17,6 +17,9 @@
// This file contains the module types for building Python library.
import (
+ "path/filepath"
+ "strings"
+
"android/soong/android"
"android/soong/bazel"
@@ -43,9 +46,14 @@
type bazelPythonLibraryAttributes struct {
Srcs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
Srcs_version *string
}
+type bazelPythonProtoLibraryAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
// TODO(b/182306917): this doesn't fully handle all nested props versioned
// by the python version, which would have been handled by the version split
@@ -64,16 +72,45 @@
// do nothing, since python_version defaults to PY2ANDPY3
}
+ // Bazel normally requires `import path.from.top.of.tree` statements in
+ // python code, but with soong you can directly import modules from libraries.
+ // Add "imports" attributes to the bazel library so it matches soong's behavior.
+ imports := "."
+ if m.properties.Pkg_path != nil {
+ // TODO(b/215119317) This is a hack to handle the fact that we don't convert
+ // pkg_path properly right now. If the folder structure that contains this
+ // Android.bp file matches pkg_path, we can set imports to an appropriate
+ // number of ../..s to emulate moving the files under a pkg_path folder.
+ pkg_path := filepath.Clean(*m.properties.Pkg_path)
+ if strings.HasPrefix(pkg_path, "/") {
+ ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
+ return
+ }
+
+ if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
+ ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
+ return
+ }
+ numFolders := strings.Count(pkg_path, "/") + 1
+ dots := make([]string, numFolders)
+ for i := 0; i < numFolders; i++ {
+ dots[i] = ".."
+ }
+ imports = strings.Join(dots, "/")
+ }
+
baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+
attrs := &bazelPythonLibraryAttributes{
Srcs: baseAttrs.Srcs,
Deps: baseAttrs.Deps,
Srcs_version: python_version,
+ Imports: bazel.MakeStringListAttribute([]string{imports}),
}
props := bazel.BazelTargetModuleProperties{
- Rule_class: "py_library",
- Bzl_load_location: "//build/bazel/rules/python:library.bzl",
+ // Use the native py_library rule.
+ Rule_class: "py_library",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{
diff --git a/python/python.go b/python/python.go
index 734ac57..7e4cb83 100644
--- a/python/python.go
+++ b/python/python.go
@@ -207,6 +207,29 @@
}
}
}
+
+ partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
+ "proto": android.ProtoSrcLabelPartition,
+ "py": bazel.LabelPartition{Keep_remainder: true},
+ })
+ attrs.Srcs = partitionedSrcs["py"]
+
+ if !partitionedSrcs["proto"].IsEmpty() {
+ protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
+ protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+
+ pyProtoLibraryName := m.Name() + "_py_proto"
+ ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+ Rule_class: "py_proto_library",
+ Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
+ }, android.CommonAttributes{
+ Name: pyProtoLibraryName,
+ }, &bazelPythonProtoLibraryAttributes{
+ Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+ })
+
+ attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
+ }
return attrs
}
@@ -423,6 +446,9 @@
if ctx.Target().Os.Bionic() {
launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
}
+ if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
+ }
switch p.properties.Actual_version {
case pyVersion2:
@@ -432,6 +458,7 @@
if p.bootstrapper.autorun() {
launcherModule = "py2-launcher-autorun"
}
+
launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
case pyVersion3:
@@ -441,6 +468,9 @@
if p.bootstrapper.autorun() {
launcherModule = "py3-launcher-autorun"
}
+ if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
+ launcherModule += "-static"
+ }
if ctx.Device() {
launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
diff --git a/rust/OWNERS b/rust/OWNERS
index d07ef7e..ddaebc5 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -1,5 +1,5 @@
# Additional owner/reviewers for rust rules, including parent directory owners.
-per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
+per-file * = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
# Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
+per-file allowed_list.go = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 4e58632..2361e03 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -106,6 +106,9 @@
}
entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+ if test.Properties.Data_bins != nil {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+ }
})
cc.AndroidMkWriteTestData(test.data, ret)
diff --git a/rust/binary.go b/rust/binary.go
index 0dc320e..41110f9 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -128,11 +128,11 @@
return Bool(binary.baseCompiler.Properties.Prefer_rlib) || Bool(binary.Properties.Static_executable)
}
-func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
srcPath, _ := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
outputFile := android.PathForModuleOut(ctx, fileName)
- ret := outputFile
+ ret := buildOutput{outputFile: outputFile}
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
@@ -147,8 +147,7 @@
}
binary.baseCompiler.unstrippedOutputFile = outputFile
- TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile)
-
+ ret.kytheFile = TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile).kytheFile
return ret
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index f4c337d..b4626a0 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -30,7 +30,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r437112b"
+ bindgenClangVersion = "clang-r450784d"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/builder.go b/rust/builder.go
index 00035b9..7dd9dd2 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -83,10 +83,37 @@
RspfileContent: "$in",
},
"outDir")
+
+ // Cross-referencing:
+ _ = pctx.SourcePathVariable("rustExtractor",
+ "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/rust_extractor")
+ _ = pctx.VariableFunc("kytheCorpus",
+ func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+ _ = pctx.VariableFunc("kytheCuEncoding",
+ func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
+ _ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
+ kytheExtract = pctx.AndroidStaticRule("kythe",
+ blueprint.RuleParams{
+ Command: `KYTHE_CORPUS=${kytheCorpus} ` +
+ `KYTHE_OUTPUT_FILE=$out ` +
+ `KYTHE_VNAMES=$kytheVnames ` +
+ `KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+ `KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` +
+ `$rustExtractor $envVars ` +
+ `$rustcCmd ` +
+ `-C linker=${config.RustLinker} ` +
+ `-C link-args="${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}" ` +
+ `$in ${libFlags} $rustcFlags`,
+ CommandDeps: []string{"$rustExtractor", "$kytheVnames"},
+ Rspfile: "${out}.rsp",
+ RspfileContent: "$in",
+ },
+ "rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
)
type buildOutput struct {
outputFile android.Path
+ kytheFile android.Path
}
func init() {
@@ -274,7 +301,7 @@
implicits = append(implicits, outputs.Paths()...)
}
- envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
+ envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx))
if ctx.RustModule().compiler.CargoEnvCompat() {
if _, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
@@ -324,6 +351,25 @@
},
})
+ if flags.EmitXrefs {
+ kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: kytheExtract,
+ Description: "Xref Rust extractor " + main.Rel(),
+ Output: kytheFile,
+ Inputs: inputs,
+ Implicits: implicits,
+ Args: map[string]string{
+ "rustcFlags": strings.Join(rustcFlags, " "),
+ "linkFlags": strings.Join(linkFlags, " "),
+ "libFlags": strings.Join(libFlags, " "),
+ "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "),
+ "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "),
+ "envVars": strings.Join(envVars, " "),
+ },
+ })
+ output.kytheFile = kytheFile
+ }
return output
}
diff --git a/rust/compiler.go b/rust/compiler.go
index c5d40f4..bcd58c8 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -121,6 +121,12 @@
// include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
Whole_static_libs []string `android:"arch_variant"`
+ // list of Rust system library dependencies.
+ //
+ // This is usually only needed when `no_stdlibs` is true, in which case it can be used to depend on system crates
+ // like `core` and `alloc`.
+ Stdlibs []string `android:"arch_variant"`
+
// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -298,6 +304,7 @@
flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags())
+ flags.EmitXrefs = ctx.Config().EmitXrefRules()
if ctx.Host() && !ctx.Windows() {
rpathPrefix := `\$$ORIGIN/`
@@ -318,7 +325,7 @@
return flags
}
-func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
}
@@ -360,6 +367,7 @@
deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
+ deps.Stdlibs = append(deps.Stdlibs, compiler.Properties.Stdlibs...)
if !Bool(compiler.Properties.No_stdlibs) {
for _, stdlib := range config.Stdlibs {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 0962168..9129b0e 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -8,6 +8,7 @@
RustAllowedPaths = []string{
"device/google/cuttlefish",
"external/adhd",
+ "external/boringssl",
"external/crosvm",
"external/libchromeos-rs",
"external/minijail",
@@ -24,11 +25,16 @@
"packages/modules/DnsResolver",
"packages/modules/Uwb",
"packages/modules/Virtualization",
+ "platform_testing/tests/codecoverage/native/rust",
"prebuilts/rust",
+ "system/core/debuggerd/rust",
"system/core/libstats/pull_rust",
+ "system/core/trusty/libtrusty-rs",
+ "system/core/trusty/keymint",
"system/extras/profcollectd",
"system/extras/simpleperf",
"system/hardware/interfaces/keystore2",
+ "system/keymint",
"system/librustutils",
"system/logging/liblog",
"system/logging/rust",
@@ -37,6 +43,7 @@
"system/tools/aidl",
"tools/security/fuzzing/example_rust_fuzzer",
"tools/security/fuzzing/orphans",
+ "tools/security/remote_provisioning/cert_validator",
"tools/vendor",
"vendor/",
}
diff --git a/rust/config/global.go b/rust/config/global.go
index c1ce13f..554cfe2 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.58.1"
+ RustDefaultVersion = "1.61.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2021"
Stdlibs = []string{
@@ -49,7 +49,8 @@
"-C overflow-checks=on",
"-C force-unwind-tables=yes",
// Use v0 mangling to distinguish from C++ symbols
- "-Z symbol-mangling-version=v0",
+ "-C symbol-mangling-version=v0",
+ "--color always",
}
deviceGlobalRustFlags = []string{
@@ -86,12 +87,7 @@
return "${RustDefaultBase}"
})
- pctx.VariableFunc("RustVersion", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("RUST_PREBUILTS_VERSION"); override != "" {
- return override
- }
- return RustDefaultVersion
- })
+ pctx.VariableFunc("RustVersion", getRustVersionPctx)
pctx.StaticVariable("RustPath", "${RustBase}/${HostPrebuiltTag}/${RustVersion}")
pctx.StaticVariable("RustBin", "${RustPath}/bin")
@@ -103,3 +99,14 @@
pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
}
+
+func getRustVersionPctx(ctx android.PackageVarContext) string {
+ return GetRustVersion(ctx)
+}
+
+func GetRustVersion(ctx android.PathContext) string {
+ if override := ctx.Config().Getenv("RUST_PREBUILTS_VERSION"); override != "" {
+ return override
+ }
+ return RustDefaultVersion
+}
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index a769f12..9c9d572 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -121,14 +121,7 @@
}
func LibclangRuntimeLibrary(t Toolchain, library string) string {
- arch := t.LibclangRuntimeLibraryArch()
- if arch == "" {
- return ""
- }
- if !t.Bionic() {
- return "libclang_rt." + library + "-" + arch
- }
- return "libclang_rt." + library + "-" + arch + "-android"
+ return "libclang_rt." + library
}
func LibRustRuntimeLibrary(t Toolchain, library string) string {
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index 7608349..4d7c422 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -42,8 +42,6 @@
"-nodefaultlibs",
"-nostdlib",
"-Wl,--no-dynamic-linker",
- // for unwind
- "-lgcc", "-lgcc_eh",
}
linuxX86Rustflags = []string{}
linuxX86Linkflags = []string{}
diff --git a/rust/coverage.go b/rust/coverage.go
index 050b811..651ce6e 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -22,6 +22,7 @@
var CovLibraryName = "libprofile-clang-extras"
+// Add '%c' to default specifier after we resolve http://b/210012154
const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
type coverage struct {
@@ -59,6 +60,10 @@
flags.LinkFlags = append(flags.LinkFlags,
profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+ if cc.EnableContinuousCoverage(ctx) {
+ flags.RustFlags = append(flags.RustFlags, "-C llvm-args=--runtime-counter-relocation")
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,-mllvm,-runtime-counter-relocation")
+ }
}
return flags, deps
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 98be7c2..865665e 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -47,17 +47,17 @@
// Check that compiler flags are set appropriately .
fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
- !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
+ !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
- t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+ t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
}
// Check that dependencies have 'fuzzer' variants produced for them as well.
libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
- !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
+ !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
- t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+ t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
}
}
diff --git a/rust/image.go b/rust/image.go
index 5d57f15..dfc7f74 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -149,6 +149,10 @@
return mod.ModuleBase.InRecovery() || mod.ModuleBase.InstallInRecovery()
}
+func (mod *Module) InRamdisk() bool {
+ return mod.ModuleBase.InRamdisk() || mod.ModuleBase.InstallInRamdisk()
+}
+
func (mod *Module) InVendorRamdisk() bool {
return mod.ModuleBase.InVendorRamdisk() || mod.ModuleBase.InstallInVendorRamdisk()
}
diff --git a/rust/library.go b/rust/library.go
index baac3f0..c2ce9de 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -246,10 +246,6 @@
return rlibAutoDep
} else if library.dylib() || library.shared() {
return dylibAutoDep
- } else if ctx.BazelConversionMode() {
- // In Bazel conversion mode, we are currently ignoring the deptag, so we just need to supply a
- // compatible tag in order to add the dependency.
- return rlibAutoDep
} else {
panic(fmt.Errorf("autoDep called on library %q that has no enabled variants.", ctx.ModuleName()))
}
@@ -474,8 +470,9 @@
return flags
}
-func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
- var outputFile, ret android.ModuleOutPath
+func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
+ var outputFile android.ModuleOutPath
+ var ret buildOutput
var fileName string
srcPath := library.srcPath(ctx, deps)
@@ -487,19 +484,19 @@
if library.rlib() {
fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
- ret = outputFile
+ ret.outputFile = outputFile
} else if library.dylib() {
fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
- ret = outputFile
+ ret.outputFile = outputFile
} else if library.static() {
fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
- ret = outputFile
+ ret.outputFile = outputFile
} else if library.shared() {
fileName = library.sharedLibFilename(ctx)
outputFile = android.PathForModuleOut(ctx, fileName)
- ret = outputFile
+ ret.outputFile = outputFile
}
if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
@@ -524,13 +521,13 @@
// Call the appropriate builder for this library type
if library.rlib() {
- TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile)
+ ret.kytheFile = TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile).kytheFile
} else if library.dylib() {
- TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile)
+ ret.kytheFile = TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile).kytheFile
} else if library.static() {
- TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile)
+ ret.kytheFile = TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile).kytheFile
} else if library.shared() {
- TransformSrctoShared(ctx, srcPath, deps, flags, outputFile)
+ ret.kytheFile = TransformSrctoShared(ctx, srcPath, deps, flags, outputFile).kytheFile
}
if library.rlib() || library.dylib() {
@@ -572,7 +569,7 @@
return ret
}
-func (library *libraryDecorator) srcPath(ctx ModuleContext, deps PathDeps) android.Path {
+func (library *libraryDecorator) srcPath(ctx ModuleContext, _ PathDeps) android.Path {
if library.sourceProvider != nil {
// Assume the first source from the source provider is the library entry point.
return library.sourceProvider.Srcs()[0]
@@ -773,9 +770,10 @@
// Glob together the headers from the modules include_dirs property
for _, path := range android.CopyOfPaths(l.includeDirs) {
dir := path.String()
- glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+ globDir := dir + "/**/*"
+ glob, err := ctx.GlobWithDeps(globDir, nil)
if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
+ ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
return
}
diff --git a/rust/library_test.go b/rust/library_test.go
index d78dcdd..4633cc7 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -200,23 +200,34 @@
func TestAutoDeps(t *testing.T) {
ctx := testRust(t, `
- rust_library_host {
- name: "libbar",
- srcs: ["bar.rs"],
- crate_name: "bar",
- }
+ rust_library_host {
+ name: "libbar",
+ srcs: ["bar.rs"],
+ crate_name: "bar",
+ }
+ rust_library_host_rlib {
+ name: "librlib_only",
+ srcs: ["bar.rs"],
+ crate_name: "rlib_only",
+ }
rust_library_host {
name: "libfoo",
srcs: ["foo.rs"],
crate_name: "foo",
- rustlibs: ["libbar"],
+ rustlibs: [
+ "libbar",
+ "librlib_only",
+ ],
}
- rust_ffi_host {
- name: "libfoo.ffi",
- srcs: ["foo.rs"],
- crate_name: "foo",
- rustlibs: ["libbar"],
- }`)
+ rust_ffi_host {
+ name: "libfoo.ffi",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ rustlibs: [
+ "libbar",
+ "librlib_only",
+ ],
+ }`)
libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
@@ -239,7 +250,9 @@
if android.InList("libbar.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
t.Errorf("libbar present as rlib dependency in dynamic lib")
}
-
+ if !android.InList("librlib_only.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
+ t.Errorf("librlib_only should be selected by rustlibs as an rlib.")
+ }
}
}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 6f17272..fe9d0b5 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -22,6 +22,7 @@
android.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory)
android.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
android.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory)
+ android.RegisterModuleType("rust_prebuilt_proc_macro", PrebuiltProcMacroFactory)
}
type PrebuiltProperties struct {
@@ -38,8 +39,42 @@
Properties PrebuiltProperties
}
+type prebuiltProcMacroDecorator struct {
+ android.Prebuilt
+
+ *procMacroDecorator
+ Properties PrebuiltProperties
+}
+
+func PrebuiltProcMacroFactory() android.Module {
+ module, _ := NewPrebuiltProcMacro(android.HostSupportedNoCross)
+ return module.Init()
+}
+
+type rustPrebuilt interface {
+ prebuiltSrcs() []string
+ prebuilt() *android.Prebuilt
+}
+
+func NewPrebuiltProcMacro(hod android.HostOrDeviceSupported) (*Module, *prebuiltProcMacroDecorator) {
+ module, library := NewProcMacro(hod)
+ prebuilt := &prebuiltProcMacroDecorator{
+ procMacroDecorator: library,
+ }
+ module.compiler = prebuilt
+
+ addSrcSupplier(module, prebuilt)
+
+ return module, prebuilt
+}
+
var _ compiler = (*prebuiltLibraryDecorator)(nil)
var _ exportedFlagsProducer = (*prebuiltLibraryDecorator)(nil)
+var _ rustPrebuilt = (*prebuiltLibraryDecorator)(nil)
+
+var _ compiler = (*prebuiltProcMacroDecorator)(nil)
+var _ exportedFlagsProducer = (*prebuiltProcMacroDecorator)(nil)
+var _ rustPrebuilt = (*prebuiltProcMacroDecorator)(nil)
func PrebuiltLibraryFactory() android.Module {
module, _ := NewPrebuiltLibrary(android.HostAndDeviceSupported)
@@ -56,7 +91,7 @@
return module.Init()
}
-func addSrcSupplier(module android.PrebuiltInterface, prebuilt *prebuiltLibraryDecorator) {
+func addSrcSupplier(module android.PrebuiltInterface, prebuilt rustPrebuilt) {
srcsSupplier := func(_ android.BaseModuleContext, _ android.Module) []string {
return prebuilt.prebuiltSrcs()
}
@@ -110,7 +145,7 @@
&prebuilt.Properties)
}
-func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
prebuilt.flagExporter.setProvider(ctx)
@@ -119,7 +154,7 @@
ctx.PropertyErrorf("srcs", "prebuilt libraries can only have one entry in srcs (the prebuilt path)")
}
prebuilt.baseCompiler.unstrippedOutputFile = srcPath
- return srcPath
+ return buildOutput{outputFile: srcPath}
}
func (prebuilt *prebuiltLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags,
@@ -152,3 +187,44 @@
func (prebuilt *prebuiltLibraryDecorator) prebuilt() *android.Prebuilt {
return &prebuilt.Prebuilt
}
+
+func (prebuilt *prebuiltProcMacroDecorator) prebuiltSrcs() []string {
+ srcs := prebuilt.Properties.Srcs
+ return srcs
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) prebuilt() *android.Prebuilt {
+ return &prebuilt.Prebuilt
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) compilerProps() []interface{} {
+ return append(prebuilt.procMacroDecorator.compilerProps(),
+ &prebuilt.Properties)
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
+ prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
+ prebuilt.flagExporter.setProvider(ctx)
+
+ srcPath, paths := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
+ if len(paths) > 0 {
+ ctx.PropertyErrorf("srcs", "prebuilt libraries can only have one entry in srcs (the prebuilt path)")
+ }
+ prebuilt.baseCompiler.unstrippedOutputFile = srcPath
+ return buildOutput{outputFile: srcPath}
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) rustdoc(ctx ModuleContext, flags Flags,
+ deps PathDeps) android.OptionalPath {
+
+ return android.OptionalPath{}
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
+ return deps
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) nativeCoverage() bool {
+ return false
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 974c096..832b62c 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -33,6 +33,7 @@
}
type procMacroInterface interface {
+ ProcMacro() bool
}
var _ compiler = (*procMacroDecorator)(nil)
@@ -69,14 +70,14 @@
return flags
}
-func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix()
outputFile := android.PathForModuleOut(ctx, fileName)
srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
- TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
+ ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
procMacro.baseCompiler.unstrippedOutputFile = outputFile
- return outputFile
+ return ret
}
func (procMacro *procMacroDecorator) getStem(ctx ModuleContext) string {
@@ -90,6 +91,10 @@
return rlibAutoDep
}
+func (procMacro *procMacroDecorator) ProcMacro() bool {
+ return true
+}
+
func (procMacro *procMacroDecorator) everInstallable() bool {
// Proc_macros are never installed
return false
diff --git a/rust/rust.go b/rust/rust.go
index 018d1dd..48419eb 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -15,6 +15,7 @@
package rust
import (
+ "android/soong/bloaty"
"fmt"
"strings"
@@ -22,11 +23,11 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/bloaty"
"android/soong/cc"
cc_config "android/soong/cc/config"
"android/soong/fuzz"
"android/soong/rust/config"
+ "android/soong/snapshot"
)
var pctx = android.NewPackageContext("android/soong/rust")
@@ -51,6 +52,7 @@
})
pctx.Import("android/soong/rust/config")
pctx.ImportAs("cc_config", "android/soong/cc/config")
+ android.InitRegistrationContext.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
}
type Flags struct {
@@ -63,6 +65,7 @@
Toolchain config.Toolchain
Coverage bool
Clippy bool
+ EmitXrefs bool // If true, emit rules to aid cross-referencing
}
type BaseProperties struct {
@@ -160,6 +163,9 @@
// Output file to be installed, may be stripped or unstripped.
outputFile android.OptionalPath
+ // Cross-reference input file
+ kytheFiles android.Paths
+
docTimestampFile android.OptionalPath
hideApexVariantFromMake bool
@@ -331,6 +337,20 @@
return false
}
+func (mod *Module) IsVndkPrebuiltLibrary() bool {
+ // Rust modules do not provide VNDK prebuilts
+ return false
+}
+
+func (mod *Module) IsVendorPublicLibrary() bool {
+ return mod.VendorProperties.IsVendorPublicLibrary
+}
+
+func (mod *Module) SdkAndPlatformVariantVisibleToMake() bool {
+ // Rust modules to not provide Sdk variants
+ return false
+}
+
func (c *Module) IsVndkPrivate() bool {
return false
}
@@ -379,6 +399,10 @@
return false
}
+func (mod *Module) XrefRustFiles() android.Paths {
+ return mod.kytheFiles
+}
+
type Deps struct {
Dylibs []string
Rlibs []string
@@ -442,7 +466,7 @@
cfgFlags(ctx ModuleContext, flags Flags) Flags
featureFlags(ctx ModuleContext, flags Flags) Flags
compilerProps() []interface{}
- compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path
+ compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput
compilerDeps(ctx DepsContext, deps Deps) Deps
crateName() string
rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
@@ -478,6 +502,10 @@
exportLinkObjects(...string)
}
+type xref interface {
+ XrefRustFiles() android.Paths
+}
+
type flagExporter struct {
linkDirs []string
linkObjects []string
@@ -806,6 +834,13 @@
return mod.Properties.Installable
}
+func (mod *Module) ProcMacro() bool {
+ if pm, ok := mod.compiler.(procMacroInterface); ok {
+ return pm.ProcMacro()
+ }
+ return false
+}
+
func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if mod.cachedToolchain == nil {
mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
@@ -833,24 +868,7 @@
toolchain := mod.toolchain(ctx)
mod.makeLinkType = cc.GetMakeLinkType(actx, mod)
- // Differentiate static libraries that are vendor available
- if mod.UseVndk() {
- if mod.InProduct() && !mod.OnlyInProduct() {
- mod.Properties.SubName += cc.ProductSuffix
- } else {
- mod.Properties.SubName += cc.VendorSuffix
- }
- } else if mod.InRamdisk() && !mod.OnlyInRamdisk() {
- mod.Properties.SubName += cc.RamdiskSuffix
- } else if mod.InVendorRamdisk() && !mod.OnlyInVendorRamdisk() {
- mod.Properties.SubName += cc.VendorRamdiskSuffix
- } else if mod.InRecovery() && !mod.OnlyInRecovery() {
- mod.Properties.SubName += cc.RecoverySuffix
- }
-
- if mod.Target().NativeBridge == android.NativeBridgeEnabled {
- mod.Properties.SubName += cc.NativeBridgeSuffix
- }
+ mod.Properties.SubName = cc.GetSubnameProperty(actx, mod)
if !toolchain.Supported() {
// This toolchain's unsupported, there's nothing to do for this mod.
@@ -899,11 +917,14 @@
if mod.compiler != nil && !mod.compiler.Disabled() {
mod.compiler.initialize(ctx)
- outputFile := mod.compiler.compile(ctx, flags, deps)
+ buildOutput := mod.compiler.compile(ctx, flags, deps)
if ctx.Failed() {
return
}
- mod.outputFile = android.OptionalPathForPath(outputFile)
+ mod.outputFile = android.OptionalPathForPath(buildOutput.outputFile)
+ if buildOutput.kytheFile != nil {
+ mod.kytheFiles = append(mod.kytheFiles, buildOutput.kytheFile)
+ }
bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), android.OptionalPathForPath(mod.compiler.unstrippedOutputFilePath()))
mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
@@ -920,12 +941,13 @@
}
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) {
+ if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) && !mod.ProcMacro() {
// If the module has been specifically configure to not be installed then
// hide from make as otherwise it will break when running inside make as the
// output path to install will not be specified. Not all uninstallable
// modules can be hidden from make as some are needed for resolving make
- // side dependencies.
+ // side dependencies. In particular, proc-macros need to be captured in the
+ // host snapshot.
mod.HideFromMake()
} else if !mod.installable(apexInfo) {
mod.SkipInstall()
@@ -968,6 +990,7 @@
deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+ deps.Stdlibs = android.LastUniqueStrings(deps.Stdlibs)
deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
return deps
@@ -1045,7 +1068,7 @@
}
func (mod *Module) Prebuilt() *android.Prebuilt {
- if p, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ if p, ok := mod.compiler.(rustPrebuilt); ok {
return p.prebuilt()
}
return nil
@@ -1192,6 +1215,10 @@
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
directStaticLibDeps = append(directStaticLibDeps, ccDep)
+
+ // Record baseLibName for snapshots.
+ mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
+
mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
case cc.IsSharedDepTag(depTag):
// For the shared lib dependencies, we may link to the stub variant
@@ -1214,7 +1241,6 @@
// Record baseLibName for snapshots.
mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
- mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
exportDep = true
@@ -1358,13 +1384,12 @@
}
// rlibs
+ rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: rlibVariation})
for _, lib := range deps.Rlibs {
depTag := rlibDepTag
lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
- actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
- {Mutator: "rust_libraries", Variation: rlibVariation},
- }...), depTag, lib)
+ actx.AddVariationDependencies(rlibDepVariations, depTag, lib)
}
// dylibs
@@ -1376,21 +1401,25 @@
// rustlibs
if deps.Rustlibs != nil && !mod.compiler.Disabled() {
autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
- if autoDep.depTag == rlibDepTag {
- for _, lib := range deps.Rustlibs {
- depTag := autoDep.depTag
- lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
- actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
- {Mutator: "rust_libraries", Variation: autoDep.variation},
- }...), depTag, lib)
+ for _, lib := range deps.Rustlibs {
+ if autoDep.depTag == rlibDepTag {
+ // Handle the rlib deptag case
+ addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations)
+ } else {
+ // autoDep.depTag is a dylib depTag. Not all rustlibs may be available as a dylib however.
+ // Check for the existence of the dylib deptag variant. Select it if available,
+ // otherwise select the rlib variant.
+ autoDepVariations := append(commonDepVariations,
+ blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation})
+ if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) {
+ actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib)
+ } else {
+ // If there's no dylib dependency available, try to add the rlib dependency instead.
+ addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations)
+ }
}
- } else {
- actx.AddVariationDependencies(
- append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
- autoDep.depTag, deps.Rustlibs...)
}
}
-
// stdlibs
if deps.Stdlibs != nil {
if mod.compiler.stdLinkage(ctx) == RlibLinkage {
@@ -1466,6 +1495,12 @@
actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
}
+// addRlibDependency will add an rlib dependency, rewriting to the snapshot library if available.
+func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo *cc.SnapshotInfo, variations []blueprint.Variation) {
+ lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+ actx.AddVariationDependencies(variations, rlibDepTag, lib)
+}
+
func BeginMutator(ctx android.BottomUpMutatorContext) {
if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
mod.beginMutator(ctx)
@@ -1497,6 +1532,7 @@
}
var _ android.HostToolProvider = (*Module)(nil)
+var _ snapshot.RelativeInstallPath = (*Module)(nil)
func (mod *Module) HostToolPath() android.OptionalPath {
if !mod.Host() {
@@ -1504,6 +1540,10 @@
}
if binary, ok := mod.compiler.(*binaryDecorator); ok {
return android.OptionalPathForPath(binary.baseCompiler.path)
+ } else if pm, ok := mod.compiler.(*procMacroDecorator); ok {
+ // Even though proc-macros aren't strictly "tools", since they target the compiler
+ // and act as compiler plugins, we treat them similarly.
+ return android.OptionalPathForPath(pm.baseCompiler.path)
}
return android.OptionalPath{}
}
@@ -1594,6 +1634,25 @@
return "", false
}
+func kytheExtractRustFactory() android.Singleton {
+ return &kytheExtractRustSingleton{}
+}
+
+type kytheExtractRustSingleton struct {
+}
+
+func (k kytheExtractRustSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ var xrefTargets android.Paths
+ ctx.VisitAllModules(func(module android.Module) {
+ if rustModule, ok := module.(xref); ok {
+ xrefTargets = append(xrefTargets, rustModule.XrefRustFiles()...)
+ }
+ })
+ if len(xrefTargets) > 0 {
+ ctx.Phony("xref_rust", xrefTargets...)
+ }
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/rust/sanitize.go b/rust/sanitize.go
index be9dc42..aadc00f 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -49,15 +49,15 @@
Memtag_heap *bool `android:"arch_variant"`
}
}
- SanitizerEnabled bool `blueprint:"mutated"`
- SanitizeDep bool `blueprint:"mutated"`
+ SanitizerEnabled bool `blueprint:"mutated"`
+ SanitizeDepTypes []cc.SanitizerType `blueprint:"mutated"`
// Used when we need to place libraries in their own directory, such as ASAN.
InSanitizerDir bool `blueprint:"mutated"`
}
var fuzzerFlags = []string{
- "-C passes='sancov'",
+ "-C passes='sancov-module'",
"--cfg fuzzing",
"-C llvm-args=-sanitizer-coverage-level=3",
@@ -70,7 +70,7 @@
"-C link-dead-code",
// Sancov breaks with lto
- // TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
+ // TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov-module works with LTO
"-C lto=no",
}
@@ -444,8 +444,14 @@
return mod.sanitize.isSanitizerExplicitlyDisabled(t)
}
-func (mod *Module) SanitizeDep() bool {
- return mod.sanitize.Properties.SanitizeDep
+func (mod *Module) SanitizeDep(t cc.SanitizerType) bool {
+ for _, e := range mod.sanitize.Properties.SanitizeDepTypes {
+ if t == e {
+ return true
+ }
+ }
+
+ return false
}
func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
@@ -454,8 +460,10 @@
}
}
-func (mod *Module) SetSanitizeDep(b bool) {
- mod.sanitize.Properties.SanitizeDep = b
+func (c *Module) SetSanitizeDep(t cc.SanitizerType) {
+ if !c.SanitizeDep(t) {
+ c.sanitize.Properties.SanitizeDepTypes = append(c.sanitize.Properties.SanitizeDepTypes, t)
+ }
}
func (mod *Module) StaticallyLinked() bool {
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
index dfbc1d1..2f79cc5 100644
--- a/rust/snapshot_prebuilt.go
+++ b/rust/snapshot_prebuilt.go
@@ -69,7 +69,7 @@
return module, prebuilt
}
-func (library *snapshotLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+func (library *snapshotLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
var variant string
if library.static() {
variant = cc.SnapshotStaticSuffix
@@ -85,11 +85,11 @@
}
if !library.MatchesWithDevice(ctx.DeviceConfig()) {
- return nil
+ return buildOutput{}
}
outputFile := android.PathForModuleSrc(ctx, *library.properties.Src)
library.unstrippedOutputFile = outputFile
- return outputFile
+ return buildOutput{outputFile: outputFile}
}
func (library *snapshotLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath {
diff --git a/rust/test.go b/rust/test.go
index 250b765..6e53935 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -15,6 +15,8 @@
package rust
import (
+ "path/filepath"
+
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -151,9 +153,15 @@
ctx.ModuleErrorf("data_lib %q is not a linkable module", depName)
}
if linkableDep.OutputFile().Valid() {
+ // Copy the output in "lib[64]" so that it's compatible with
+ // the default rpath values.
+ libDir := "lib"
+ if linkableDep.Target().Arch.ArchType.Multilib == "lib64" {
+ libDir = "lib64"
+ }
test.data = append(test.data,
android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
- RelativeInstallPath: linkableDep.RelativeInstallPath()})
+ RelativeInstallPath: filepath.Join(libDir, linkableDep.RelativeInstallPath())})
}
})
diff --git a/rust/test_test.go b/rust/test_test.go
index 1124176..8906f1c 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -187,12 +187,12 @@
t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
}
entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
- if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
- t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:lib64/foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:lib64/foo/bar/baz`,"+
" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
}
- if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][1], ":librust_test_lib.so:foo/bar/baz") {
- t.Errorf("expected LOCAL_TEST_DATA to end with `:librust_test_lib.so:foo/bar/baz`,"+
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][1], ":librust_test_lib.so:lib64/foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:librust_test_lib.so:lib64/foo/bar/baz`,"+
" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][1])
}
if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][2], ":rusty:foo/bar/baz") {
diff --git a/rust/testing.go b/rust/testing.go
index 1b34dfe..4796f69 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -88,13 +88,13 @@
export_include_dirs: ["libprotobuf-cpp-full-includes"],
}
cc_library {
- name: "libclang_rt.asan-aarch64-android",
+ name: "libclang_rt.asan",
no_libcrt: true,
nocrt: true,
system_shared_libs: [],
}
cc_library {
- name: "libclang_rt.hwasan_static-aarch64-android",
+ name: "libclang_rt.hwasan_static",
no_libcrt: true,
nocrt: true,
system_shared_libs: [],
@@ -194,6 +194,7 @@
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
})
ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+ ctx.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
})
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 03bd867..7be0042 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -561,7 +561,7 @@
static_libs: [
"libvendor",
"libvndk",
- "libclang_rt.builtins-aarch64-android",
+ "libclang_rt.builtins",
"note_memtag_heap_sync",
],
shared_libs: [
@@ -589,7 +589,7 @@
static_libs: [
"libvendor",
"libvndk",
- "libclang_rt.builtins-arm-android",
+ "libclang_rt.builtins",
],
shared_libs: [
"libvendor_available",
@@ -731,19 +731,7 @@
}
vendor_snapshot_static {
- name: "libclang_rt.builtins-aarch64-android",
- version: "30",
- target_arch: "arm64",
- vendor: true,
- arch: {
- arm64: {
- src: "libclang_rt.builtins-aarch64-android.a",
- },
- },
- }
-
- vendor_snapshot_static {
- name: "libclang_rt.builtins-arm-android",
+ name: "libclang_rt.builtins",
version: "30",
target_arch: "arm64",
vendor: true,
@@ -751,6 +739,9 @@
arm: {
src: "libclang_rt.builtins-arm-android.a",
},
+ arm64: {
+ src: "libclang_rt.builtins-aarch64-android.a",
+ },
},
}
@@ -967,7 +958,7 @@
}
libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
- if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot", "libclang_rt.builtins-aarch64-android.vendor"}; !reflect.DeepEqual(g, w) {
+ if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot", "libclang_rt.builtins.vendor"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
}
@@ -1024,7 +1015,7 @@
}
memtagStaticLibs := ctx.ModuleForTests("memtag_binary", "android_vendor.30_arm64_armv8-a").Module().(*Module).Properties.AndroidMkStaticLibs
- if g, w := memtagStaticLibs, []string{"libclang_rt.builtins-aarch64-android.vendor", "note_memtag_heap_sync.vendor"}; !reflect.DeepEqual(g, w) {
+ if g, w := memtagStaticLibs, []string{"libclang_rt.builtins.vendor", "note_memtag_heap_sync.vendor"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted memtag_binary AndroidMkStaticLibs %q, got %q", w, g)
}
}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 4c847a1..4773579 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -188,3 +188,8 @@
"get_clang_version.py",
],
}
+
+sh_binary_host {
+ name: "list_image",
+ src: "list_image.sh",
+}
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index 4783037..b57963b 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -19,54 +19,9 @@
exit 1
fi
-TOP=$(pwd)
-
-source build/envsetup.sh
-PLATFORM_SDK_VERSION=$(get_build_var PLATFORM_SDK_VERSION)
-PLATFORM_VERSION_ALL_CODENAMES=$(get_build_var PLATFORM_VERSION_ALL_CODENAMES)
-
-# PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
-# turn this into ["O","P"].
-PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
-PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
-
-# Get the list of missing <uses-library> modules and convert it to a JSON array
-# (quote module names, add comma separator and wrap in brackets).
-MISSING_USES_LIBRARIES="$(get_build_var INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)"
-MISSING_USES_LIBRARIES="[$(echo $MISSING_USES_LIBRARIES | sed -e 's/\([^ ]\+\)/\"\1\"/g' -e 's/[ ]\+/, /g')]"
-
-SOONG_OUT=${OUT_DIR}/soong
-SOONG_NDK_OUT=${OUT_DIR}/soong/ndk
-rm -rf ${SOONG_OUT}
-mkdir -p ${SOONG_OUT}
-
-# We only really need to set some of these variables, but soong won't merge this
-# with the defaults, so we need to write out all the defaults with our values
-# added.
-cat > ${SOONG_OUT}/soong.variables << EOF
-{
- "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
- "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
-
- "DeviceName": "generic_arm64",
- "HostArch": "x86_64",
- "Malloc_not_svelte": false,
- "Safestack": false,
-
- "Ndk_abis": true,
-
- "VendorVars": {
- "art_module": {
- "source_build": "true"
- }
- },
-
- "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
-}
-EOF
-m --soong-only --skip-config ${SOONG_OUT}/ndk.timestamp
+TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
if [ -n "${DIST_DIR}" ]; then
mkdir -p ${DIST_DIR} || true
- tar cjf ${DIST_DIR}/ndk_platform.tar.bz2 -C ${SOONG_OUT} ndk
+ tar cjf ${DIST_DIR}/ndk_platform.tar.bz2 -C ${OUT_DIR}/soong ndk
fi
diff --git a/scripts/generate-notice-files.py b/scripts/generate-notice-files.py
deleted file mode 100755
index 1b4acfa..0000000
--- a/scripts/generate-notice-files.py
+++ /dev/null
@@ -1,272 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-Usage: generate-notice-files --text-output [plain text output file] \
- --html-output [html output file] \
- --xml-output [xml output file] \
- -t [file title] -s [directory of notices]
-
-Generate the Android notice files, including both text and html files.
-
--h to display this usage message and exit.
-"""
-from collections import defaultdict
-import argparse
-import hashlib
-import itertools
-import os
-import os.path
-import re
-import struct
-import sys
-
-MD5_BLOCKSIZE = 1024 * 1024
-HTML_ESCAPE_TABLE = {
- b"&": b"&",
- b'"': b""",
- b"'": b"'",
- b">": b">",
- b"<": b"<",
- }
-
-def md5sum(filename):
- """Calculate an MD5 of the file given by FILENAME,
- and return hex digest as a string.
- Output should be compatible with md5sum command"""
-
- f = open(filename, "rb")
- sum = hashlib.md5()
- while 1:
- block = f.read(MD5_BLOCKSIZE)
- if not block:
- break
- sum.update(block)
- f.close()
- return sum.hexdigest()
-
-
-def html_escape(text):
- """Produce entities within text."""
- # Using for i in text doesn't work since i will be an int, not a byte.
- # There are multiple ways to solve this, but the most performant way
- # to iterate over a byte array is to use unpack. Using the
- # for i in range(len(text)) and using that to get a byte using array
- # slices is twice as slow as this method.
- return b"".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))
-
-HTML_OUTPUT_CSS=b"""
-<style type="text/css">
-body { padding: 0; font-family: sans-serif; }
-.same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
-.label { font-weight: bold; }
-.file-list { margin-left: 1em; color: blue; }
-</style>
-
-"""
-
-def combine_notice_files_html(file_hash, input_dir, output_filename):
- """Combine notice files in FILE_HASH and output a HTML version to OUTPUT_FILENAME."""
-
- SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
-
- # Set up a filename to row id table (anchors inside tables don't work in
- # most browsers, but href's to table row ids do)
- id_table = {}
- id_count = 0
- for value in file_hash:
- for filename in value:
- id_table[filename] = id_count
- id_count += 1
-
- # Open the output file, and output the header pieces
- output_file = open(output_filename, "wb")
-
- output_file.write(b"<html><head>\n")
- output_file.write(HTML_OUTPUT_CSS)
- output_file.write(b'</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">\n')
-
- # Output our table of contents
- output_file.write(b'<div class="toc">\n')
- output_file.write(b"<ul>\n")
-
- # Flatten the list of lists into a single list of filenames
- sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
-
- # Print out a nice table of contents
- for filename in sorted_filenames:
- stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
- output_file.write(('<li><a href="#id%d">%s</a></li>\n' % (id_table.get(filename), stripped_filename)).encode())
-
- output_file.write(b"</ul>\n")
- output_file.write(b"</div><!-- table of contents -->\n")
- # Output the individual notice file lists
- output_file.write(b'<table cellpadding="0" cellspacing="0" border="0">\n')
- for value in file_hash:
- output_file.write(('<tr id="id%d"><td class="same-license">\n' % id_table.get(value[0])).encode())
- output_file.write(b'<div class="label">Notices for file(s):</div>\n')
- output_file.write(b'<div class="file-list">\n')
- for filename in value:
- output_file.write(("%s <br/>\n" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))).encode())
- output_file.write(b"</div><!-- file-list -->\n\n")
- output_file.write(b'<pre class="license-text">\n')
- with open(value[0], "rb") as notice_file:
- output_file.write(html_escape(notice_file.read()))
- output_file.write(b"\n</pre><!-- license-text -->\n")
- output_file.write(b"</td></tr><!-- same-license -->\n\n\n\n")
-
- # Finish off the file output
- output_file.write(b"</table>\n")
- output_file.write(b"</body></html>\n")
- output_file.close()
-
-def combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
- """Combine notice files in FILE_HASH and output a text version to OUTPUT_FILENAME."""
-
- SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
- output_file = open(output_filename, "wb")
- output_file.write(file_title.encode())
- output_file.write(b"\n")
- for value in file_hash:
- output_file.write(b"============================================================\n")
- output_file.write(b"Notices for file(s):\n")
- for filename in value:
- output_file.write(SRC_DIR_STRIP_RE.sub(r"\1", filename).encode())
- output_file.write(b"\n")
- output_file.write(b"------------------------------------------------------------\n")
- with open(value[0], "rb") as notice_file:
- output_file.write(notice_file.read())
- output_file.write(b"\n")
- output_file.close()
-
-def combine_notice_files_xml(files_with_same_hash, input_dir, output_filename):
- """Combine notice files in FILE_HASH and output a XML version to OUTPUT_FILENAME."""
-
- SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
-
- # Set up a filename to row id table (anchors inside tables don't work in
- # most browsers, but href's to table row ids do)
- id_table = {}
- for file_key, files in files_with_same_hash.items():
- for filename in files:
- id_table[filename] = file_key
-
- # Open the output file, and output the header pieces
- output_file = open(output_filename, "wb")
-
- output_file.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
- output_file.write(b"<licenses>\n")
-
- # Flatten the list of lists into a single list of filenames
- sorted_filenames = sorted(list(id_table))
-
- # Print out a nice table of contents
- for filename in sorted_filenames:
- stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
- output_file.write(('<file-name contentId="%s">%s</file-name>\n' % (id_table.get(filename), stripped_filename)).encode())
- output_file.write(b"\n\n")
-
- processed_file_keys = []
- # Output the individual notice file lists
- for filename in sorted_filenames:
- file_key = id_table.get(filename)
- if file_key in processed_file_keys:
- continue
- processed_file_keys.append(file_key)
-
- output_file.write(('<file-content contentId="%s"><![CDATA[' % file_key).encode())
- with open(filename, "rb") as notice_file:
- output_file.write(html_escape(notice_file.read()))
- output_file.write(b"]]></file-content>\n\n")
-
- # Finish off the file output
- output_file.write(b"</licenses>\n")
- output_file.close()
-
-def get_args():
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--text-output', required=True,
- help='The text output file path.')
- parser.add_argument(
- '--html-output',
- help='The html output file path.')
- parser.add_argument(
- '--xml-output',
- help='The xml output file path.')
- parser.add_argument(
- '-t', '--title', required=True,
- help='The file title.')
- parser.add_argument(
- '-s', '--source-dir', required=True,
- help='The directory containing notices.')
- parser.add_argument(
- '-i', '--included-subdirs', action='append',
- help='The sub directories which should be included.')
- parser.add_argument(
- '-e', '--excluded-subdirs', action='append',
- help='The sub directories which should be excluded.')
- return parser.parse_args()
-
-def main(argv):
- args = get_args()
-
- txt_output_file = args.text_output
- html_output_file = args.html_output
- xml_output_file = args.xml_output
- file_title = args.title
- included_subdirs = []
- excluded_subdirs = []
- if args.included_subdirs is not None:
- included_subdirs = args.included_subdirs
- if args.excluded_subdirs is not None:
- excluded_subdirs = args.excluded_subdirs
-
- # Find all the notice files and md5 them
- input_dir = os.path.normpath(args.source_dir)
- files_with_same_hash = defaultdict(list)
- for root, dir, files in os.walk(input_dir):
- for file in files:
- matched = True
- if len(included_subdirs) > 0:
- matched = False
- for subdir in included_subdirs:
- if (root == (input_dir + '/' + subdir) or
- root.startswith(input_dir + '/' + subdir + '/')):
- matched = True
- break
- elif len(excluded_subdirs) > 0:
- for subdir in excluded_subdirs:
- if (root == (input_dir + '/' + subdir) or
- root.startswith(input_dir + '/' + subdir + '/')):
- matched = False
- break
- if matched and file.endswith(".txt"):
- filename = os.path.join(root, file)
- file_md5sum = md5sum(filename)
- files_with_same_hash[file_md5sum].append(filename)
-
- filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]
-
- combine_notice_files_text(filesets, input_dir, txt_output_file, file_title)
-
- if html_output_file is not None:
- combine_notice_files_html(filesets, input_dir, html_output_file)
-
- if xml_output_file is not None:
- combine_notice_files_xml(files_with_same_hash, input_dir, xml_output_file)
-
-if __name__ == "__main__":
- main(sys.argv)
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 7ffda62..07878f9 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -19,6 +19,52 @@
}
python_binary_host {
+ name: "analyze_bcpf",
+ main: "analyze_bcpf.py",
+ srcs: ["analyze_bcpf.py"],
+ // Make sure that the bpmodify tool is built.
+ data: [":bpmodify"],
+ libs: [
+ "signature_trie",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+}
+
+python_test_host {
+ name: "analyze_bcpf_test",
+ main: "analyze_bcpf_test.py",
+ srcs: [
+ "analyze_bcpf.py",
+ "analyze_bcpf_test.py",
+ ],
+ // Make sure that the bpmodify tool is built.
+ data: [":bpmodify"],
+ libs: [
+ "signature_trie",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+ test_options: {
+ unit_test: true,
+ },
+}
+
+python_binary_host {
name: "merge_csv",
main: "merge_csv.py",
srcs: ["merge_csv.py"],
@@ -69,10 +115,37 @@
},
}
+python_library_host {
+ name: "signature_trie",
+ srcs: ["signature_trie.py"],
+}
+
+python_test_host {
+ name: "signature_trie_test",
+ main: "signature_trie_test.py",
+ srcs: ["signature_trie_test.py"],
+ libs: ["signature_trie"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+ test_options: {
+ unit_test: true,
+ },
+}
+
python_binary_host {
name: "verify_overlaps",
main: "verify_overlaps.py",
srcs: ["verify_overlaps.py"],
+ libs: [
+ "signature_trie",
+ ],
version: {
py2: {
enabled: false,
@@ -91,6 +164,9 @@
"verify_overlaps.py",
"verify_overlaps_test.py",
],
+ libs: [
+ "signature_trie",
+ ],
version: {
py2: {
enabled: false,
diff --git a/scripts/hiddenapi/analyze_bcpf.py b/scripts/hiddenapi/analyze_bcpf.py
new file mode 100644
index 0000000..595343b
--- /dev/null
+++ b/scripts/hiddenapi/analyze_bcpf.py
@@ -0,0 +1,1477 @@
+#!/usr/bin/env -S python -u
+#
+# 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.
+"""Analyze bootclasspath_fragment usage."""
+import argparse
+import dataclasses
+import enum
+import json
+import logging
+import os
+import re
+import shutil
+import subprocess
+import tempfile
+import textwrap
+import typing
+from enum import Enum
+
+import sys
+
+from signature_trie import signature_trie
+
+_STUB_FLAGS_FILE = "out/soong/hiddenapi/hiddenapi-stub-flags.txt"
+
+_FLAGS_FILE = "out/soong/hiddenapi/hiddenapi-flags.csv"
+
+_INCONSISTENT_FLAGS = "ERROR: Hidden API flags are inconsistent:"
+
+
+class BuildOperation:
+
+ def __init__(self, popen):
+ self.popen = popen
+ self.returncode = None
+
+ def lines(self):
+ """Return an iterator over the lines output by the build operation.
+
+ The lines have had any trailing white space, including the newline
+ stripped.
+ """
+ return newline_stripping_iter(self.popen.stdout.readline)
+
+ def wait(self, *args, **kwargs):
+ self.popen.wait(*args, **kwargs)
+ self.returncode = self.popen.returncode
+
+
+@dataclasses.dataclass()
+class FlagDiffs:
+ """Encapsulates differences in flags reported by the build"""
+
+ # Map from member signature to the (module flags, monolithic flags)
+ diffs: typing.Dict[str, typing.Tuple[str, str]]
+
+
+@dataclasses.dataclass()
+class ModuleInfo:
+ """Provides access to the generated module-info.json file.
+
+ This is used to find the location of the file within which specific modules
+ are defined.
+ """
+
+ modules: typing.Dict[str, typing.Dict[str, typing.Any]]
+
+ @staticmethod
+ def load(filename):
+ with open(filename, "r", encoding="utf8") as f:
+ j = json.load(f)
+ return ModuleInfo(j)
+
+ def _module(self, module_name):
+ """Find module by name in module-info.json file"""
+ if module_name in self.modules:
+ return self.modules[module_name]
+
+ raise Exception(f"Module {module_name} could not be found")
+
+ def module_path(self, module_name):
+ module = self._module(module_name)
+ # The "path" is actually a list of paths, one for each class of module
+ # but as the modules are all created from bp files if a module does
+ # create multiple classes of make modules they should all have the same
+ # path.
+ paths = module["path"]
+ unique_paths = set(paths)
+ if len(unique_paths) != 1:
+ raise Exception(f"Expected module '{module_name}' to have a "
+ f"single unique path but found {unique_paths}")
+ return paths[0]
+
+
+def extract_indent(line):
+ return re.match(r"([ \t]*)", line).group(1)
+
+
+_SPECIAL_PLACEHOLDER: str = "SPECIAL_PLACEHOLDER"
+
+
+@dataclasses.dataclass
+class BpModifyRunner:
+
+ bpmodify_path: str
+
+ def add_values_to_property(self, property_name, values, module_name,
+ bp_file):
+ cmd = [
+ self.bpmodify_path, "-a", values, "-property", property_name, "-m",
+ module_name, "-w", bp_file, bp_file
+ ]
+
+ logging.debug(" ".join(cmd))
+ subprocess.run(
+ cmd,
+ stderr=subprocess.STDOUT,
+ stdout=log_stream_for_subprocess(),
+ check=True)
+
+
+@dataclasses.dataclass
+class FileChange:
+ path: str
+
+ description: str
+
+ def __lt__(self, other):
+ return self.path < other.path
+
+
+class PropertyChangeAction(Enum):
+ """Allowable actions that are supported by HiddenApiPropertyChange."""
+
+ # New values are appended to any existing values.
+ APPEND = 1
+
+ # New values replace any existing values.
+ REPLACE = 2
+
+
+@dataclasses.dataclass
+class HiddenApiPropertyChange:
+
+ property_name: str
+
+ values: typing.List[str]
+
+ property_comment: str = ""
+
+ # The action that indicates how this change is applied.
+ action: PropertyChangeAction = PropertyChangeAction.APPEND
+
+ def snippet(self, indent):
+ snippet = "\n"
+ snippet += format_comment_as_text(self.property_comment, indent)
+ snippet += f"{indent}{self.property_name}: ["
+ if self.values:
+ snippet += "\n"
+ for value in self.values:
+ snippet += f'{indent} "{value}",\n'
+ snippet += f"{indent}"
+ snippet += "],\n"
+ return snippet
+
+ def fix_bp_file(self, bcpf_bp_file, bcpf, bpmodify_runner: BpModifyRunner):
+ # Add an additional placeholder value to identify the modification that
+ # bpmodify makes.
+ bpmodify_values = [_SPECIAL_PLACEHOLDER]
+
+ if self.action == PropertyChangeAction.APPEND:
+ # If adding the values to the existing values then pass the new
+ # values to bpmodify.
+ bpmodify_values.extend(self.values)
+ elif self.action == PropertyChangeAction.REPLACE:
+ # If replacing the existing values then it is not possible to use
+ # bpmodify for that directly. It could be used twice to remove the
+ # existing property and then add a new one but that does not remove
+ # any related comments and loses the position of the existing
+ # property as the new property is always added to the end of the
+ # containing block.
+ #
+ # So, instead of passing the new values to bpmodify this this just
+ # adds an extra placeholder to force bpmodify to format the list
+ # across multiple lines to ensure a consistent structure for the
+ # code that removes all the existing values and adds the new ones.
+ #
+ # This placeholder has to be different to the other placeholder as
+ # bpmodify dedups values.
+ bpmodify_values.append(_SPECIAL_PLACEHOLDER + "_REPLACE")
+ else:
+ raise ValueError(f"unknown action {self.action}")
+
+ packages = ",".join(bpmodify_values)
+ bpmodify_runner.add_values_to_property(
+ f"hidden_api.{self.property_name}", packages, bcpf, bcpf_bp_file)
+
+ with open(bcpf_bp_file, "r", encoding="utf8") as tio:
+ lines = tio.readlines()
+ lines = [line.rstrip("\n") for line in lines]
+
+ if self.fixup_bpmodify_changes(bcpf_bp_file, lines):
+ with open(bcpf_bp_file, "w", encoding="utf8") as tio:
+ for line in lines:
+ print(line, file=tio)
+
+ def fixup_bpmodify_changes(self, bcpf_bp_file, lines):
+ """Fixup the output of bpmodify.
+
+ The bpmodify tool does not support all the capabilities that this needs
+ so it is used to do what it can, including marking the place in the
+ Android.bp file where it makes its changes and then this gets passed a
+ list of lines from that file which it then modifies to complete the
+ change.
+
+ This analyzes the list of lines to find the indices of the significant
+ lines and then applies some changes. As those changes can insert and
+ delete lines (changing the indices of following lines) the changes are
+ generally done in reverse order starting from the end and working
+ towards the beginning. That ensures that the changes do not invalidate
+ the indices of following lines.
+ """
+
+ # Find the line containing the placeholder that has been inserted.
+ place_holder_index = -1
+ for i, line in enumerate(lines):
+ if _SPECIAL_PLACEHOLDER in line:
+ place_holder_index = i
+ break
+ if place_holder_index == -1:
+ logging.debug("Could not find %s in %s", _SPECIAL_PLACEHOLDER,
+ bcpf_bp_file)
+ return False
+
+ # Remove the place holder. Do this before inserting the comment as that
+ # would change the location of the place holder in the list.
+ place_holder_line = lines[place_holder_index]
+ if place_holder_line.endswith("],"):
+ place_holder_line = place_holder_line.replace(
+ f'"{_SPECIAL_PLACEHOLDER}"', "")
+ lines[place_holder_index] = place_holder_line
+ else:
+ del lines[place_holder_index]
+
+ # Scan forward to the end of the property block to remove a blank line
+ # that bpmodify inserts.
+ end_property_array_index = -1
+ for i in range(place_holder_index, len(lines)):
+ line = lines[i]
+ if line.endswith("],"):
+ end_property_array_index = i
+ break
+ if end_property_array_index == -1:
+ logging.debug("Could not find end of property array in %s",
+ bcpf_bp_file)
+ return False
+
+ # If bdmodify inserted a blank line afterwards then remove it.
+ if (not lines[end_property_array_index + 1] and
+ lines[end_property_array_index + 2].endswith("},")):
+ del lines[end_property_array_index + 1]
+
+ # Scan back to find the preceding property line.
+ property_line_index = -1
+ for i in range(place_holder_index, 0, -1):
+ line = lines[i]
+ if line.lstrip().startswith(f"{self.property_name}: ["):
+ property_line_index = i
+ break
+ if property_line_index == -1:
+ logging.debug("Could not find property line in %s", bcpf_bp_file)
+ return False
+
+ # If this change is replacing the existing values then they need to be
+ # removed and replaced with the new values. That will change the lines
+ # after the property but it is necessary to do here as the following
+ # code operates on earlier lines.
+ if self.action == PropertyChangeAction.REPLACE:
+ # This removes the existing values and replaces them with the new
+ # values.
+ indent = extract_indent(lines[property_line_index + 1])
+ insert = [f'{indent}"{x}",' for x in self.values]
+ lines[property_line_index + 1:end_property_array_index] = insert
+ if not self.values:
+ # If the property has no values then merge the ], onto the
+ # same line as the property name.
+ del lines[property_line_index + 1]
+ lines[property_line_index] = lines[property_line_index] + "],"
+
+ # Only insert a comment if the property does not already have a comment.
+ line_preceding_property = lines[(property_line_index - 1)]
+ if (self.property_comment and
+ not re.match("([ \t]+)// ", line_preceding_property)):
+ # Extract the indent from the property line and use it to format the
+ # comment.
+ indent = extract_indent(lines[property_line_index])
+ comment_lines = format_comment_as_lines(self.property_comment,
+ indent)
+
+ # If the line before the comment is not blank then insert an extra
+ # blank line at the beginning of the comment.
+ if line_preceding_property:
+ comment_lines.insert(0, "")
+
+ # Insert the comment before the property.
+ lines[property_line_index:property_line_index] = comment_lines
+ return True
+
+
+@dataclasses.dataclass()
+class PackagePropertyReason:
+ """Provides the reasons why a package was added to a specific property.
+
+ A split package is one that contains classes from the bootclasspath_fragment
+ and other bootclasspath modules. So, for a split package this contains the
+ corresponding lists of classes.
+
+ A single package is one that contains classes sub-packages from the
+ For a split package this contains a list of classes in that package that are
+ provided by the bootclasspath_fragment and a list of classes
+ """
+
+ # The list of classes/sub-packages that is provided by the
+ # bootclasspath_fragment.
+ bcpf: typing.List[str]
+
+ # The list of classes/sub-packages that is provided by other modules on the
+ # bootclasspath.
+ other: typing.List[str]
+
+
+@dataclasses.dataclass()
+class Result:
+ """Encapsulates the result of the analysis."""
+
+ # The diffs in the flags.
+ diffs: typing.Optional[FlagDiffs] = None
+
+ # A map from package name to the reason why it belongs in the
+ # split_packages property.
+ split_packages: typing.Dict[str, PackagePropertyReason] = dataclasses.field(
+ default_factory=dict)
+
+ # A map from package name to the reason why it belongs in the
+ # single_packages property.
+ single_packages: typing.Dict[str,
+ PackagePropertyReason] = dataclasses.field(
+ default_factory=dict)
+
+ # The list of packages to add to the package_prefixes property.
+ package_prefixes: typing.List[str] = dataclasses.field(default_factory=list)
+
+ # The bootclasspath_fragment hidden API properties changes.
+ property_changes: typing.List[HiddenApiPropertyChange] = dataclasses.field(
+ default_factory=list)
+
+ # The list of file changes.
+ file_changes: typing.List[FileChange] = dataclasses.field(
+ default_factory=list)
+
+
+class ClassProvider(enum.Enum):
+ """The source of a class found during the hidden API processing"""
+ BCPF = "bcpf"
+ OTHER = "other"
+
+
+# A fake member to use when using the signature trie to compute the package
+# properties from hidden API flags. This is needed because while that
+# computation only cares about classes the trie expects a class to be an
+# interior node but without a member it makes the class a leaf node. That causes
+# problems when analyzing inner classes as the outer class is a leaf node for
+# its own entry but is used as an interior node for inner classes.
+_FAKE_MEMBER = ";->fake()V"
+
+
+@dataclasses.dataclass()
+class BcpfAnalyzer:
+ # Path to this tool.
+ tool_path: str
+
+ # Directory pointed to by ANDROID_BUILD_OUT
+ top_dir: str
+
+ # Directory pointed to by OUT_DIR of {top_dir}/out if that is not set.
+ out_dir: str
+
+ # Directory pointed to by ANDROID_PRODUCT_OUT.
+ product_out_dir: str
+
+ # The name of the bootclasspath_fragment module.
+ bcpf: str
+
+ # The name of the apex module containing {bcpf}, only used for
+ # informational purposes.
+ apex: str
+
+ # The name of the sdk module containing {bcpf}, only used for
+ # informational purposes.
+ sdk: str
+
+ # If true then this will attempt to automatically fix any issues that are
+ # found.
+ fix: bool = False
+
+ # All the signatures, loaded from all-flags.csv, initialized by
+ # load_all_flags().
+ _signatures: typing.Set[str] = dataclasses.field(default_factory=set)
+
+ # All the classes, loaded from all-flags.csv, initialized by
+ # load_all_flags().
+ _classes: typing.Set[str] = dataclasses.field(default_factory=set)
+
+ # Information loaded from module-info.json, initialized by
+ # load_module_info().
+ module_info: ModuleInfo = None
+
+ @staticmethod
+ def reformat_report_test(text):
+ return re.sub(r"(.)\n([^\s])", r"\1 \2", text)
+
+ def report(self, text="", **kwargs):
+ # Concatenate lines that are not separated by a blank line together to
+ # eliminate formatting applied to the supplied text to adhere to python
+ # line length limitations.
+ text = self.reformat_report_test(text)
+ logging.info("%s", text, **kwargs)
+
+ def report_dedent(self, text, **kwargs):
+ text = textwrap.dedent(text)
+ self.report(text, **kwargs)
+
+ def run_command(self, cmd, *args, **kwargs):
+ cmd_line = " ".join(cmd)
+ logging.debug("Running %s", cmd_line)
+ subprocess.run(
+ cmd,
+ *args,
+ check=True,
+ cwd=self.top_dir,
+ stderr=subprocess.STDOUT,
+ stdout=log_stream_for_subprocess(),
+ text=True,
+ **kwargs)
+
+ @property
+ def signatures(self):
+ if not self._signatures:
+ raise Exception("signatures has not been initialized")
+ return self._signatures
+
+ @property
+ def classes(self):
+ if not self._classes:
+ raise Exception("classes has not been initialized")
+ return self._classes
+
+ def load_all_flags(self):
+ all_flags = self.find_bootclasspath_fragment_output_file(
+ "all-flags.csv")
+
+ # Extract the set of signatures and a separate set of classes produced
+ # by the bootclasspath_fragment.
+ with open(all_flags, "r", encoding="utf8") as f:
+ for line in newline_stripping_iter(f.readline):
+ signature = self.line_to_signature(line)
+ self._signatures.add(signature)
+ class_name = self.signature_to_class(signature)
+ self._classes.add(class_name)
+
+ def load_module_info(self):
+ module_info_file = os.path.join(self.product_out_dir,
+ "module-info.json")
+ self.report(f"\nMaking sure that {module_info_file} is up to date.\n")
+ output = self.build_file_read_output(module_info_file)
+ lines = output.lines()
+ for line in lines:
+ logging.debug("%s", line)
+ output.wait(timeout=10)
+ if output.returncode:
+ raise Exception(f"Error building {module_info_file}")
+ abs_module_info_file = os.path.join(self.top_dir, module_info_file)
+ self.module_info = ModuleInfo.load(abs_module_info_file)
+
+ @staticmethod
+ def line_to_signature(line):
+ return line.split(",")[0]
+
+ @staticmethod
+ def signature_to_class(signature):
+ return signature.split(";->")[0]
+
+ @staticmethod
+ def to_parent_package(pkg_or_class):
+ return pkg_or_class.rsplit("/", 1)[0]
+
+ def module_path(self, module_name):
+ return self.module_info.module_path(module_name)
+
+ def module_out_dir(self, module_name):
+ module_path = self.module_path(module_name)
+ return os.path.join(self.out_dir, "soong/.intermediates", module_path,
+ module_name)
+
+ def find_bootclasspath_fragment_output_file(self, basename, required=True):
+ # Find the output file of the bootclasspath_fragment with the specified
+ # base name.
+ found_file = ""
+ bcpf_out_dir = self.module_out_dir(self.bcpf)
+ for (dirpath, _, filenames) in os.walk(bcpf_out_dir):
+ for f in filenames:
+ if f == basename:
+ found_file = os.path.join(dirpath, f)
+ break
+ if not found_file and required:
+ raise Exception(f"Could not find {basename} in {bcpf_out_dir}")
+ return found_file
+
+ def analyze(self):
+ """Analyze a bootclasspath_fragment module.
+
+ Provides help in resolving any existing issues and provides
+ optimizations that can be applied.
+ """
+ self.report(f"Analyzing bootclasspath_fragment module {self.bcpf}")
+ self.report_dedent(f"""
+ Run this tool to help initialize a bootclasspath_fragment module.
+ Before you start make sure that:
+
+ 1. The current checkout is up to date.
+
+ 2. The environment has been initialized using lunch, e.g.
+ lunch aosp_arm64-userdebug
+
+ 3. You have added a bootclasspath_fragment module to the appropriate
+ Android.bp file. Something like this:
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ contents: [
+ "...",
+ ],
+
+ // The bootclasspath_fragments that provide APIs on which this
+ // depends.
+ fragments: [
+ {{
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ }},
+ ],
+ }}
+
+ 4. You have added it to the platform_bootclasspath module in
+ frameworks/base/boot/Android.bp. Something like this:
+
+ platform_bootclasspath {{
+ name: "platform-bootclasspath",
+ fragments: [
+ ...
+ {{
+ apex: "{self.apex}",
+ module: "{self.bcpf}",
+ }},
+ ],
+ }}
+
+ 5. You have added an sdk module. Something like this:
+
+ sdk {{
+ name: "{self.sdk}",
+ bootclasspath_fragments: ["{self.bcpf}"],
+ }}
+ """)
+
+ # Make sure that the module-info.json file is up to date.
+ self.load_module_info()
+
+ self.report_dedent("""
+ Cleaning potentially stale files.
+ """)
+ # Remove the out/soong/hiddenapi files.
+ shutil.rmtree(f"{self.out_dir}/soong/hiddenapi", ignore_errors=True)
+
+ # Remove any bootclasspath_fragment output files.
+ shutil.rmtree(self.module_out_dir(self.bcpf), ignore_errors=True)
+
+ self.build_monolithic_stubs_flags()
+
+ result = Result()
+
+ self.build_monolithic_flags(result)
+ self.analyze_hiddenapi_package_properties(result)
+ self.explain_how_to_check_signature_patterns()
+
+ # If there were any changes that need to be made to the Android.bp
+ # file then either apply or report them.
+ if result.property_changes:
+ bcpf_dir = self.module_info.module_path(self.bcpf)
+ bcpf_bp_file = os.path.join(self.top_dir, bcpf_dir, "Android.bp")
+ if self.fix:
+ tool_dir = os.path.dirname(self.tool_path)
+ bpmodify_path = os.path.join(tool_dir, "bpmodify")
+ bpmodify_runner = BpModifyRunner(bpmodify_path)
+ for property_change in result.property_changes:
+ property_change.fix_bp_file(bcpf_bp_file, self.bcpf,
+ bpmodify_runner)
+
+ result.file_changes.append(
+ self.new_file_change(
+ bcpf_bp_file,
+ f"Updated hidden_api properties of '{self.bcpf}'"))
+
+ else:
+ hiddenapi_snippet = ""
+ for property_change in result.property_changes:
+ hiddenapi_snippet += property_change.snippet(" ")
+
+ # Remove leading and trailing blank lines.
+ hiddenapi_snippet = hiddenapi_snippet.strip("\n")
+
+ result.file_changes.append(
+ self.new_file_change(
+ bcpf_bp_file, f"""
+Add the following snippet into the {self.bcpf} bootclasspath_fragment module
+in the {bcpf_dir}/Android.bp file. If the hidden_api block already exists then
+merge these properties into it.
+
+ hidden_api: {{
+{hiddenapi_snippet}
+ }},
+"""))
+
+ if result.file_changes:
+ if self.fix:
+ file_change_message = textwrap.dedent("""
+ The following files were modified by this script:
+ """)
+ else:
+ file_change_message = textwrap.dedent("""
+ The following modifications need to be made:
+ """)
+
+ self.report(file_change_message)
+ result.file_changes.sort()
+ for file_change in result.file_changes:
+ self.report(f" {file_change.path}")
+ self.report(f" {file_change.description}")
+ self.report()
+
+ if not self.fix:
+ self.report_dedent("""
+ Run the command again with the --fix option to automatically
+ make the above changes.
+ """.lstrip("\n"))
+
+ def new_file_change(self, file, description):
+ return FileChange(
+ path=os.path.relpath(file, self.top_dir), description=description)
+
+ def check_inconsistent_flag_lines(self, significant, module_line,
+ monolithic_line, separator_line):
+ if not (module_line.startswith("< ") and
+ monolithic_line.startswith("> ") and not separator_line):
+ # Something went wrong.
+ self.report("Invalid build output detected:")
+ self.report(f" module_line: '{module_line}'")
+ self.report(f" monolithic_line: '{monolithic_line}'")
+ self.report(f" separator_line: '{separator_line}'")
+ sys.exit(1)
+
+ if significant:
+ logging.debug("%s", module_line)
+ logging.debug("%s", monolithic_line)
+ logging.debug("%s", separator_line)
+
+ def scan_inconsistent_flags_report(self, lines):
+ """Scans a hidden API flags report
+
+ The hidden API inconsistent flags report which looks something like
+ this.
+
+ < out/soong/.intermediates/.../filtered-stub-flags.csv
+ > out/soong/hiddenapi/hiddenapi-stub-flags.txt
+
+ < Landroid/compat/Compatibility;->clearOverrides()V
+ > Landroid/compat/Compatibility;->clearOverrides()V,core-platform-api
+
+ """
+
+ # The basic format of an entry in the inconsistent flags report is:
+ # <module specific flag>
+ # <monolithic flag>
+ # <separator>
+ #
+ # Wrap the lines iterator in an iterator which returns a tuple
+ # consisting of the three separate lines.
+ triples = zip(lines, lines, lines)
+
+ module_line, monolithic_line, separator_line = next(triples)
+ significant = False
+ bcpf_dir = self.module_info.module_path(self.bcpf)
+ if os.path.join(bcpf_dir, self.bcpf) in module_line:
+ # These errors are related to the bcpf being analyzed so
+ # keep them.
+ significant = True
+ else:
+ self.report(f"Filtering out errors related to {module_line}")
+
+ self.check_inconsistent_flag_lines(significant, module_line,
+ monolithic_line, separator_line)
+
+ diffs = {}
+ for module_line, monolithic_line, separator_line in triples:
+ self.check_inconsistent_flag_lines(significant, module_line,
+ monolithic_line, "")
+
+ module_parts = module_line.removeprefix("< ").split(",")
+ module_signature = module_parts[0]
+ module_flags = module_parts[1:]
+
+ monolithic_parts = monolithic_line.removeprefix("> ").split(",")
+ monolithic_signature = monolithic_parts[0]
+ monolithic_flags = monolithic_parts[1:]
+
+ if module_signature != monolithic_signature:
+ # Something went wrong.
+ self.report("Inconsistent signatures detected:")
+ self.report(f" module_signature: '{module_signature}'")
+ self.report(f" monolithic_signature: '{monolithic_signature}'")
+ sys.exit(1)
+
+ diffs[module_signature] = (module_flags, monolithic_flags)
+
+ if separator_line:
+ # If the separator line is not blank then it is the end of the
+ # current report, and possibly the start of another.
+ return separator_line, diffs
+
+ return "", diffs
+
+ def build_file_read_output(self, filename):
+ # Make sure the filename is relative to top if possible as the build
+ # may be using relative paths as the target.
+ rel_filename = filename.removeprefix(self.top_dir)
+ cmd = ["build/soong/soong_ui.bash", "--make-mode", rel_filename]
+ cmd_line = " ".join(cmd)
+ logging.debug("%s", cmd_line)
+ # pylint: disable=consider-using-with
+ output = subprocess.Popen(
+ cmd,
+ cwd=self.top_dir,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE,
+ text=True,
+ )
+ return BuildOperation(popen=output)
+
+ def build_hiddenapi_flags(self, filename):
+ output = self.build_file_read_output(filename)
+
+ lines = output.lines()
+ diffs = None
+ for line in lines:
+ logging.debug("%s", line)
+ while line == _INCONSISTENT_FLAGS:
+ line, diffs = self.scan_inconsistent_flags_report(lines)
+
+ output.wait(timeout=10)
+ if output.returncode != 0:
+ logging.debug("Command failed with %s", output.returncode)
+ else:
+ logging.debug("Command succeeded")
+
+ return diffs
+
+ def build_monolithic_stubs_flags(self):
+ self.report_dedent(f"""
+ Attempting to build {_STUB_FLAGS_FILE} to verify that the
+ bootclasspath_fragment has the correct API stubs available...
+ """)
+
+ # Build the hiddenapi-stubs-flags.txt file.
+ diffs = self.build_hiddenapi_flags(_STUB_FLAGS_FILE)
+ if diffs:
+ self.report_dedent(f"""
+ There is a discrepancy between the stub API derived flags
+ created by the bootclasspath_fragment and the
+ platform_bootclasspath. See preceding error messages to see
+ which flags are inconsistent. The inconsistencies can occur for
+ a couple of reasons:
+
+ If you are building against prebuilts of the Android SDK, e.g.
+ by using TARGET_BUILD_APPS then the prebuilt versions of the
+ APIs this bootclasspath_fragment depends upon are out of date
+ and need updating. See go/update-prebuilts for help.
+
+ Otherwise, this is happening because there are some stub APIs
+ that are either provided by or used by the contents of the
+ bootclasspath_fragment but which are not available to it. There
+ are 4 ways to handle this:
+
+ 1. A java_sdk_library in the contents property will
+ automatically make its stub APIs available to the
+ bootclasspath_fragment so nothing needs to be done.
+
+ 2. If the API provided by the bootclasspath_fragment is created
+ by an api_only java_sdk_library (or a java_library that compiles
+ files generated by a separate droidstubs module then it cannot
+ be added to the contents and instead must be added to the
+ api.stubs property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ api: {{
+ stubs: ["$MODULE-api-only"],"
+ }},
+ }}
+
+ 3. If the contents use APIs provided by another
+ bootclasspath_fragment then it needs to be added to the
+ fragments property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ ...
+ {{
+ apex: "com.android.other",
+ module: "com.android.other-bootclasspath-fragment",
+ }},
+ ],
+ }}
+
+ 4. If the contents use APIs from a module that is not part of
+ another bootclasspath_fragment then it must be added to the
+ additional_stubs property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ additional_stubs: ["android-non-updatable"],
+ }}
+
+ Like the api.stubs property these are typically
+ java_sdk_library modules but can be java_library too.
+
+ Note: The "android-non-updatable" is treated as if it was a
+ java_sdk_library which it is not at the moment but will be in
+ future.
+ """)
+
+ return diffs
+
+ def build_monolithic_flags(self, result):
+ self.report_dedent(f"""
+ Attempting to build {_FLAGS_FILE} to verify that the
+ bootclasspath_fragment has the correct hidden API flags...
+ """)
+
+ # Build the hiddenapi-flags.csv file and extract any differences in
+ # the flags between this bootclasspath_fragment and the monolithic
+ # files.
+ result.diffs = self.build_hiddenapi_flags(_FLAGS_FILE)
+
+ # Load information from the bootclasspath_fragment's all-flags.csv file.
+ self.load_all_flags()
+
+ if result.diffs:
+ self.report_dedent(f"""
+ There is a discrepancy between the hidden API flags created by
+ the bootclasspath_fragment and the platform_bootclasspath. See
+ preceding error messages to see which flags are inconsistent.
+ The inconsistencies can occur for a couple of reasons:
+
+ If you are building against prebuilts of this
+ bootclasspath_fragment then the prebuilt version of the sdk
+ snapshot (specifically the hidden API flag files) are
+ inconsistent with the prebuilt version of the apex {self.apex}.
+ Please ensure that they are both updated from the same build.
+
+ 1. There are custom hidden API flags specified in the one of the
+ files in frameworks/base/boot/hiddenapi which apply to the
+ bootclasspath_fragment but which are not supplied to the
+ bootclasspath_fragment module.
+
+ 2. The bootclasspath_fragment specifies invalid
+ "split_packages", "single_packages" and/of "package_prefixes"
+ properties that match packages and classes that it does not
+ provide.
+ """)
+
+ # Check to see if there are any hiddenapi related properties that
+ # need to be added to the
+ self.report_dedent("""
+ Checking custom hidden API flags....
+ """)
+ self.check_frameworks_base_boot_hidden_api_files(result)
+
+ def report_hidden_api_flag_file_changes(self, result, property_name,
+ flags_file, rel_bcpf_flags_file,
+ bcpf_flags_file):
+ matched_signatures = set()
+ # Open the flags file to read the flags from.
+ with open(flags_file, "r", encoding="utf8") as f:
+ for signature in newline_stripping_iter(f.readline):
+ if signature in self.signatures:
+ # The signature is provided by the bootclasspath_fragment so
+ # it will need to be moved to the bootclasspath_fragment
+ # specific file.
+ matched_signatures.add(signature)
+
+ # If the bootclasspath_fragment specific flags file is not empty
+ # then it contains flags. That could either be new flags just moved
+ # from frameworks/base or previous contents of the file. In either
+ # case the file must not be removed.
+ if matched_signatures:
+ insert = textwrap.indent("\n".join(matched_signatures),
+ " ")
+ result.file_changes.append(
+ self.new_file_change(
+ flags_file, f"""Remove the following entries:
+{insert}
+"""))
+
+ result.file_changes.append(
+ self.new_file_change(
+ bcpf_flags_file, f"""Add the following entries:
+{insert}
+"""))
+
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name=property_name,
+ values=[rel_bcpf_flags_file],
+ ))
+
+ def fix_hidden_api_flag_files(self, result, property_name, flags_file,
+ rel_bcpf_flags_file, bcpf_flags_file):
+ # Read the file in frameworks/base/boot/hiddenapi/<file> copy any
+ # flags that relate to the bootclasspath_fragment into a local
+ # file in the hiddenapi subdirectory.
+ tmp_flags_file = flags_file + ".tmp"
+
+ # Make sure the directory containing the bootclasspath_fragment specific
+ # hidden api flags exists.
+ os.makedirs(os.path.dirname(bcpf_flags_file), exist_ok=True)
+
+ bcpf_flags_file_exists = os.path.exists(bcpf_flags_file)
+
+ matched_signatures = set()
+ # Open the flags file to read the flags from.
+ with open(flags_file, "r", encoding="utf8") as f:
+ # Open a temporary file to write the flags (minus any removed
+ # flags).
+ with open(tmp_flags_file, "w", encoding="utf8") as t:
+ # Open the bootclasspath_fragment file for append just in
+ # case it already exists.
+ with open(bcpf_flags_file, "a", encoding="utf8") as b:
+ for line in iter(f.readline, ""):
+ signature = line.rstrip()
+ if signature in self.signatures:
+ # The signature is provided by the
+ # bootclasspath_fragment so write it to the new
+ # bootclasspath_fragment specific file.
+ print(line, file=b, end="")
+ matched_signatures.add(signature)
+ else:
+ # The signature is NOT provided by the
+ # bootclasspath_fragment. Copy it to the new
+ # monolithic file.
+ print(line, file=t, end="")
+
+ # If the bootclasspath_fragment specific flags file is not empty
+ # then it contains flags. That could either be new flags just moved
+ # from frameworks/base or previous contents of the file. In either
+ # case the file must not be removed.
+ if matched_signatures:
+ # There are custom flags related to the bootclasspath_fragment
+ # so replace the frameworks/base/boot/hiddenapi file with the
+ # file that does not contain those flags.
+ shutil.move(tmp_flags_file, flags_file)
+
+ result.file_changes.append(
+ self.new_file_change(flags_file,
+ f"Removed '{self.bcpf}' specific entries"))
+
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name=property_name,
+ values=[rel_bcpf_flags_file],
+ ))
+
+ # Make sure that the files are sorted.
+ self.run_command([
+ "tools/platform-compat/hiddenapi/sort_api.sh",
+ bcpf_flags_file,
+ ])
+
+ if bcpf_flags_file_exists:
+ desc = f"Added '{self.bcpf}' specific entries"
+ else:
+ desc = f"Created with '{self.bcpf}' specific entries"
+ result.file_changes.append(
+ self.new_file_change(bcpf_flags_file, desc))
+ else:
+ # There are no custom flags related to the
+ # bootclasspath_fragment so clean up the working files.
+ os.remove(tmp_flags_file)
+ if not bcpf_flags_file_exists:
+ os.remove(bcpf_flags_file)
+
+ def check_frameworks_base_boot_hidden_api_files(self, result):
+ hiddenapi_dir = os.path.join(self.top_dir,
+ "frameworks/base/boot/hiddenapi")
+ for basename in sorted(os.listdir(hiddenapi_dir)):
+ if not (basename.startswith("hiddenapi-") and
+ basename.endswith(".txt")):
+ continue
+
+ flags_file = os.path.join(hiddenapi_dir, basename)
+
+ logging.debug("Checking %s for flags related to %s", flags_file,
+ self.bcpf)
+
+ # Map the file name in frameworks/base/boot/hiddenapi into a
+ # slightly more meaningful name for use by the
+ # bootclasspath_fragment.
+ if basename == "hiddenapi-max-target-o.txt":
+ basename = "hiddenapi-max-target-o-low-priority.txt"
+ elif basename == "hiddenapi-max-target-r-loprio.txt":
+ basename = "hiddenapi-max-target-r-low-priority.txt"
+
+ property_name = basename.removeprefix("hiddenapi-")
+ property_name = property_name.removesuffix(".txt")
+ property_name = property_name.replace("-", "_")
+
+ rel_bcpf_flags_file = f"hiddenapi/{basename}"
+ bcpf_dir = self.module_info.module_path(self.bcpf)
+ bcpf_flags_file = os.path.join(self.top_dir, bcpf_dir,
+ rel_bcpf_flags_file)
+
+ if self.fix:
+ self.fix_hidden_api_flag_files(result, property_name,
+ flags_file, rel_bcpf_flags_file,
+ bcpf_flags_file)
+ else:
+ self.report_hidden_api_flag_file_changes(
+ result, property_name, flags_file, rel_bcpf_flags_file,
+ bcpf_flags_file)
+
+ @staticmethod
+ def split_package_comment(split_packages):
+ if split_packages:
+ return textwrap.dedent("""
+ The following packages contain classes from other modules on the
+ bootclasspath. That means that the hidden API flags for this
+ module has to explicitly list every single class this module
+ provides in that package to differentiate them from the classes
+ provided by other modules. That can include private classes that
+ are not part of the API.
+ """).strip("\n")
+
+ return "This module does not contain any split packages."
+
+ @staticmethod
+ def package_prefixes_comment():
+ return textwrap.dedent("""
+ The following packages and all their subpackages currently only
+ contain classes from this bootclasspath_fragment. Listing a package
+ here won't prevent other bootclasspath modules from adding classes
+ in any of those packages but it will prevent them from adding those
+ classes into an API surface, e.g. public, system, etc.. Doing so
+ will result in a build failure due to inconsistent flags.
+ """).strip("\n")
+
+ def analyze_hiddenapi_package_properties(self, result):
+ self.compute_hiddenapi_package_properties(result)
+
+ def indent_lines(lines):
+ return "\n".join([f" {cls}" for cls in lines])
+
+ # TODO(b/202154151): Find those classes in split packages that are not
+ # part of an API, i.e. are an internal implementation class, and so
+ # can, and should, be safely moved out of the split packages.
+
+ split_packages = result.split_packages.keys()
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=split_packages,
+ property_comment=self.split_package_comment(split_packages),
+ action=PropertyChangeAction.REPLACE,
+ ))
+
+ if split_packages:
+ self.report_dedent(f"""
+ bootclasspath_fragment {self.bcpf} contains classes in packages
+ that also contain classes provided by other bootclasspath
+ modules. Those packages are called split packages. Split
+ packages should be avoided where possible but are often
+ unavoidable when modularizing existing code.
+
+ The hidden api processing needs to know which packages are split
+ (and conversely which are not) so that it can optimize the
+ hidden API flags to remove unnecessary implementation details.
+
+ By default (for backwards compatibility) the
+ bootclasspath_fragment assumes that all packages are split
+ unless one of the package_prefixes or split_packages properties
+ are specified. While that is safe it is not optimal and can lead
+ to unnecessary implementation details leaking into the hidden
+ API flags. Adding an empty split_packages property allows the
+ flags to be optimized and remove any unnecessary implementation
+ details.
+ """)
+
+ for package in split_packages:
+ reason = result.split_packages[package]
+ self.report(f"""
+ Package {package} is split because while this bootclasspath_fragment
+ provides the following classes:
+{indent_lines(reason.bcpf)}
+
+ Other module(s) on the bootclasspath provides the following classes in
+ that package:
+{indent_lines(reason.other)}
+""")
+
+ single_packages = result.single_packages.keys()
+ if single_packages:
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name="single_packages",
+ values=single_packages,
+ property_comment=textwrap.dedent("""
+ The following packages currently only contain classes from
+ this bootclasspath_fragment but some of their sub-packages
+ contain classes from other bootclasspath modules. Packages
+ should only be listed here when necessary for legacy
+ purposes, new packages should match a package prefix.
+ """),
+ action=PropertyChangeAction.REPLACE,
+ ))
+
+ self.report_dedent(f"""
+ bootclasspath_fragment {self.bcpf} contains classes from
+ packages that has sub-packages which contain classes provided by
+ other bootclasspath modules. Those packages are called single
+ packages. Single packages should be avoided where possible but
+ are often unavoidable when modularizing existing code.
+
+ Because some sub-packages contains classes from other
+ bootclasspath modules it is not possible to use the package as a
+ package prefix as that treats the package and all its
+ sub-packages as being provided by this module.
+ """)
+ for package in single_packages:
+ reason = result.single_packages[package]
+ self.report(f"""
+ Package {package} is not a package prefix because while this
+ bootclasspath_fragment provides the following sub-packages:
+{indent_lines(reason.bcpf)}
+
+ Other module(s) on the bootclasspath provide the following sub-packages:
+{indent_lines(reason.other)}
+""")
+
+ package_prefixes = result.package_prefixes
+ if package_prefixes:
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name="package_prefixes",
+ values=package_prefixes,
+ property_comment=self.package_prefixes_comment(),
+ action=PropertyChangeAction.REPLACE,
+ ))
+
+ def explain_how_to_check_signature_patterns(self):
+ signature_patterns_files = self.find_bootclasspath_fragment_output_file(
+ "signature-patterns.csv", required=False)
+ if signature_patterns_files:
+ signature_patterns_files = signature_patterns_files.removeprefix(
+ self.top_dir)
+
+ self.report_dedent(f"""
+ The purpose of the hiddenapi split_packages and package_prefixes
+ properties is to allow the removal of implementation details
+ from the hidden API flags to reduce the coupling between sdk
+ snapshots and the APEX runtime. It cannot eliminate that
+ coupling completely though. Doing so may require changes to the
+ code.
+
+ This tool provides support for managing those properties but it
+ cannot decide whether the set of package prefixes suggested is
+ appropriate that needs the input of the developer.
+
+ Please run the following command:
+ m {signature_patterns_files}
+
+ And then check the '{signature_patterns_files}' for any mention
+ of implementation classes and packages (i.e. those
+ classes/packages that do not contain any part of an API surface,
+ including the hidden API). If they are found then the code
+ should ideally be moved to a package unique to this module that
+ is contained within a package that is part of an API surface.
+
+ The format of the file is a list of patterns:
+
+ * Patterns for split packages will list every class in that package.
+
+ * Patterns for package prefixes will end with .../**.
+
+ * Patterns for packages which are not split but cannot use a
+ package prefix because there are sub-packages which are provided
+ by another module will end with .../*.
+ """)
+
+ def compute_hiddenapi_package_properties(self, result):
+ trie = signature_trie()
+ # Populate the trie with the classes that are provided by the
+ # bootclasspath_fragment tagging them to make it clear where they
+ # are from.
+ sorted_classes = sorted(self.classes)
+ for class_name in sorted_classes:
+ trie.add(class_name + _FAKE_MEMBER, ClassProvider.BCPF)
+
+ # Now the same for monolithic classes.
+ monolithic_classes = set()
+ abs_flags_file = os.path.join(self.top_dir, _FLAGS_FILE)
+ with open(abs_flags_file, "r", encoding="utf8") as f:
+ for line in iter(f.readline, ""):
+ signature = self.line_to_signature(line)
+ class_name = self.signature_to_class(signature)
+ if (class_name not in monolithic_classes and
+ class_name not in self.classes):
+ trie.add(
+ class_name + _FAKE_MEMBER,
+ ClassProvider.OTHER,
+ only_if_matches=True)
+ monolithic_classes.add(class_name)
+
+ self.recurse_hiddenapi_packages_trie(trie, result)
+
+ @staticmethod
+ def selector_to_java_reference(node):
+ return node.selector.replace("/", ".")
+
+ @staticmethod
+ def determine_reason_for_single_package(node):
+ bcpf_packages = []
+ other_packages = []
+
+ def recurse(n):
+ if n.type != "package":
+ return
+
+ providers = n.get_matching_rows("*")
+ package_ref = BcpfAnalyzer.selector_to_java_reference(n)
+ if ClassProvider.BCPF in providers:
+ bcpf_packages.append(package_ref)
+ else:
+ other_packages.append(package_ref)
+
+ children = n.child_nodes()
+ if children:
+ for child in children:
+ recurse(child)
+
+ recurse(node)
+ return PackagePropertyReason(bcpf=bcpf_packages, other=other_packages)
+
+ @staticmethod
+ def determine_reason_for_split_package(node):
+ bcpf_classes = []
+ other_classes = []
+ for child in node.child_nodes():
+ if child.type != "class":
+ continue
+
+ providers = child.values(lambda _: True)
+ class_ref = BcpfAnalyzer.selector_to_java_reference(child)
+ if ClassProvider.BCPF in providers:
+ bcpf_classes.append(class_ref)
+ else:
+ other_classes.append(class_ref)
+
+ return PackagePropertyReason(bcpf=bcpf_classes, other=other_classes)
+
+ def recurse_hiddenapi_packages_trie(self, node, result):
+ nodes = node.child_nodes()
+ if nodes:
+ for child in nodes:
+ # Ignore any non-package nodes.
+ if child.type != "package":
+ continue
+
+ package = self.selector_to_java_reference(child)
+
+ providers = set(child.get_matching_rows("**"))
+ if not providers:
+ # The package and all its sub packages contain no
+ # classes. This should never happen.
+ pass
+ elif providers == {ClassProvider.BCPF}:
+ # The package and all its sub packages only contain
+ # classes provided by the bootclasspath_fragment.
+ logging.debug("Package '%s.**' is not split", package)
+ result.package_prefixes.append(package)
+ # There is no point traversing into the sub packages.
+ continue
+ elif providers == {ClassProvider.OTHER}:
+ # The package and all its sub packages contain no
+ # classes provided by the bootclasspath_fragment.
+ # There is no point traversing into the sub packages.
+ logging.debug("Package '%s.**' contains no classes from %s",
+ package, self.bcpf)
+ continue
+ elif ClassProvider.BCPF in providers:
+ # The package and all its sub packages contain classes
+ # provided by the bootclasspath_fragment and other
+ # sources.
+ logging.debug(
+ "Package '%s.**' contains classes from "
+ "%s and other sources", package, self.bcpf)
+
+ providers = set(child.get_matching_rows("*"))
+ if not providers:
+ # The package contains no classes.
+ logging.debug("Package: %s contains no classes", package)
+ elif providers == {ClassProvider.BCPF}:
+ # The package only contains classes provided by the
+ # bootclasspath_fragment.
+ logging.debug(
+ "Package '%s.*' is not split but does have "
+ "sub-packages from other modules", package)
+
+ # Partition the sub-packages into those that are provided by
+ # this bootclasspath_fragment and those provided by other
+ # modules. They can be used to explain the reason for the
+ # single package to developers.
+ reason = self.determine_reason_for_single_package(child)
+ result.single_packages[package] = reason
+
+ elif providers == {ClassProvider.OTHER}:
+ # The package contains no classes provided by the
+ # bootclasspath_fragment. Child nodes make contain such
+ # classes.
+ logging.debug("Package '%s.*' contains no classes from %s",
+ package, self.bcpf)
+ elif ClassProvider.BCPF in providers:
+ # The package contains classes provided by both the
+ # bootclasspath_fragment and some other source.
+ logging.debug("Package '%s.*' is split", package)
+
+ # Partition the classes in this split package into those
+ # that come from this bootclasspath_fragment and those that
+ # come from other modules. That can be used to explain the
+ # reason for the split package to developers.
+ reason = self.determine_reason_for_split_package(child)
+ result.split_packages[package] = reason
+
+ self.recurse_hiddenapi_packages_trie(child, result)
+
+
+def newline_stripping_iter(iterator):
+ """Return an iterator over the iterator that strips trailing white space."""
+ lines = iter(iterator, "")
+ lines = (line.rstrip() for line in lines)
+ return lines
+
+
+def format_comment_as_text(text, indent):
+ return "".join(
+ [f"{line}\n" for line in format_comment_as_lines(text, indent)])
+
+
+def format_comment_as_lines(text, indent):
+ lines = textwrap.wrap(text.strip("\n"), width=77 - len(indent))
+ lines = [f"{indent}// {line}" for line in lines]
+ return lines
+
+
+def log_stream_for_subprocess():
+ stream = subprocess.DEVNULL
+ for handler in logging.root.handlers:
+ if handler.level == logging.DEBUG:
+ if isinstance(handler, logging.StreamHandler):
+ stream = handler.stream
+ return stream
+
+
+def main(argv):
+ args_parser = argparse.ArgumentParser(
+ description="Analyze a bootclasspath_fragment module.")
+ args_parser.add_argument(
+ "--bcpf",
+ help="The bootclasspath_fragment module to analyze",
+ required=True,
+ )
+ args_parser.add_argument(
+ "--apex",
+ help="The apex module to which the bootclasspath_fragment belongs. It "
+ "is not strictly necessary at the moment but providing it will "
+ "allow this script to give more useful messages and it may be"
+ "required in future.",
+ default="SPECIFY-APEX-OPTION")
+ args_parser.add_argument(
+ "--sdk",
+ help="The sdk module to which the bootclasspath_fragment belongs. It "
+ "is not strictly necessary at the moment but providing it will "
+ "allow this script to give more useful messages and it may be"
+ "required in future.",
+ default="SPECIFY-SDK-OPTION")
+ args_parser.add_argument(
+ "--fix",
+ help="Attempt to fix any issues found automatically.",
+ action="store_true",
+ default=False)
+ args = args_parser.parse_args(argv[1:])
+ top_dir = os.environ["ANDROID_BUILD_TOP"] + "/"
+ out_dir = os.environ.get("OUT_DIR", os.path.join(top_dir, "out"))
+ product_out_dir = os.environ.get("ANDROID_PRODUCT_OUT", top_dir)
+ # Make product_out_dir relative to the top so it can be used as part of a
+ # build target.
+ product_out_dir = product_out_dir.removeprefix(top_dir)
+ log_fd, abs_log_file = tempfile.mkstemp(
+ suffix="_analyze_bcpf.log", text=True)
+
+ with os.fdopen(log_fd, "w") as log_file:
+ # Set up debug logging to the log file.
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="%(levelname)-8s %(message)s",
+ stream=log_file)
+
+ # define a Handler which writes INFO messages or higher to the
+ # sys.stdout with just the message.
+ console = logging.StreamHandler()
+ console.setLevel(logging.INFO)
+ console.setFormatter(logging.Formatter("%(message)s"))
+ # add the handler to the root logger
+ logging.getLogger("").addHandler(console)
+
+ print(f"Writing log to {abs_log_file}")
+ try:
+ analyzer = BcpfAnalyzer(
+ tool_path=argv[0],
+ top_dir=top_dir,
+ out_dir=out_dir,
+ product_out_dir=product_out_dir,
+ bcpf=args.bcpf,
+ apex=args.apex,
+ sdk=args.sdk,
+ fix=args.fix,
+ )
+ analyzer.analyze()
+ finally:
+ print(f"Log written to {abs_log_file}")
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/scripts/hiddenapi/analyze_bcpf_test.py b/scripts/hiddenapi/analyze_bcpf_test.py
new file mode 100644
index 0000000..a32ffd0
--- /dev/null
+++ b/scripts/hiddenapi/analyze_bcpf_test.py
@@ -0,0 +1,661 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Unit tests for analyzing bootclasspath_fragment modules."""
+import os.path
+import shutil
+import tempfile
+import unittest
+import unittest.mock
+
+import sys
+
+import analyze_bcpf as ab
+
+_FRAMEWORK_HIDDENAPI = "frameworks/base/boot/hiddenapi"
+_MAX_TARGET_O = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-o.txt"
+_MAX_TARGET_P = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-p.txt"
+_MAX_TARGET_Q = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-q.txt"
+_MAX_TARGET_R = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-r-loprio.txt"
+
+_MULTI_LINE_COMMENT = """
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu justo,
+bibendum eu malesuada vel, fringilla in odio. Etiam gravida ultricies sem
+tincidunt luctus.""".replace("\n", " ").strip()
+
+
+class FakeBuildOperation(ab.BuildOperation):
+
+ def __init__(self, lines, return_code):
+ ab.BuildOperation.__init__(self, None)
+ self._lines = lines
+ self.returncode = return_code
+
+ def lines(self):
+ return iter(self._lines)
+
+ def wait(self, *args, **kwargs):
+ return
+
+
+class TestAnalyzeBcpf(unittest.TestCase):
+
+ def setUp(self):
+ # Create a temporary directory
+ self.test_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ # Remove the directory after the test
+ shutil.rmtree(self.test_dir)
+
+ @staticmethod
+ def write_abs_file(abs_path, contents):
+ os.makedirs(os.path.dirname(abs_path), exist_ok=True)
+ with open(abs_path, "w", encoding="utf8") as f:
+ print(contents.removeprefix("\n"), file=f, end="")
+
+ def populate_fs(self, fs):
+ for path, contents in fs.items():
+ abs_path = os.path.join(self.test_dir, path)
+ self.write_abs_file(abs_path, contents)
+
+ def create_analyzer_for_test(self,
+ fs=None,
+ bcpf="bcpf",
+ apex="apex",
+ sdk="sdk",
+ fix=False):
+ if fs:
+ self.populate_fs(fs)
+
+ top_dir = self.test_dir
+ out_dir = os.path.join(self.test_dir, "out")
+ product_out_dir = "out/product"
+
+ bcpf_dir = f"{bcpf}-dir"
+ modules = {bcpf: {"path": [bcpf_dir]}}
+ module_info = ab.ModuleInfo(modules)
+
+ analyzer = ab.BcpfAnalyzer(
+ tool_path=os.path.join(out_dir, "bin"),
+ top_dir=top_dir,
+ out_dir=out_dir,
+ product_out_dir=product_out_dir,
+ bcpf=bcpf,
+ apex=apex,
+ sdk=sdk,
+ fix=fix,
+ module_info=module_info,
+ )
+ analyzer.load_all_flags()
+ return analyzer
+
+ def test_reformat_report_text(self):
+ lines = """
+99. An item in a numbered list
+that traverses multiple lines.
+
+ An indented example
+ that should not be reformatted.
+"""
+ reformatted = ab.BcpfAnalyzer.reformat_report_test(lines)
+ self.assertEqual(
+ """
+99. An item in a numbered list that traverses multiple lines.
+
+ An indented example
+ that should not be reformatted.
+""", reformatted)
+
+ def do_test_build_flags(self, fix):
+ lines = """
+ERROR: Hidden API flags are inconsistent:
+< out/soong/.intermediates/bcpf-dir/bcpf-dir/filtered-flags.csv
+> out/soong/hiddenapi/hiddenapi-flags.csv
+
+< Lacme/test/Class;-><init>()V,blocked
+> Lacme/test/Class;-><init>()V,max-target-o
+
+< Lacme/test/Other;->getThing()Z,blocked
+> Lacme/test/Other;->getThing()Z,max-target-p
+
+< Lacme/test/Widget;-><init()V,blocked
+> Lacme/test/Widget;-><init()V,max-target-q
+
+< Lacme/test/Gadget;->NAME:Ljava/lang/String;,blocked
+> Lacme/test/Gadget;->NAME:Ljava/lang/String;,lo-prio,max-target-r
+16:37:32 ninja failed with: exit status 1
+""".strip().splitlines()
+ operation = FakeBuildOperation(lines=lines, return_code=1)
+
+ fs = {
+ _MAX_TARGET_O:
+ """
+Lacme/items/Magnet;->size:I
+Lacme/test/Class;-><init>()V
+""",
+ _MAX_TARGET_P:
+ """
+Lacme/items/Rocket;->size:I
+Lacme/test/Other;->getThing()Z
+""",
+ _MAX_TARGET_Q:
+ """
+Lacme/items/Rock;->size:I
+Lacme/test/Widget;-><init()V
+""",
+ _MAX_TARGET_R:
+ """
+Lacme/items/Lever;->size:I
+Lacme/test/Gadget;->NAME:Ljava/lang/String;
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt":
+ """
+Lacme/old/Class;->getWidget()Lacme/test/Widget;
+""",
+ "out/soong/.intermediates/bcpf-dir/bcpf/all-flags.csv":
+ """
+Lacme/test/Gadget;->NAME:Ljava/lang/String;,blocked
+Lacme/test/Widget;-><init()V,blocked
+Lacme/test/Class;-><init>()V,blocked
+Lacme/test/Other;->getThing()Z,blocked
+""",
+ }
+
+ analyzer = self.create_analyzer_for_test(fs, fix=fix)
+
+ # Override the build_file_read_output() method to just return a fake
+ # build operation.
+ analyzer.build_file_read_output = unittest.mock.Mock(
+ return_value=operation)
+
+ # Override the run_command() method to do nothing.
+ analyzer.run_command = unittest.mock.Mock()
+
+ result = ab.Result()
+
+ analyzer.build_monolithic_flags(result)
+ expected_diffs = {
+ "Lacme/test/Gadget;->NAME:Ljava/lang/String;":
+ (["blocked"], ["lo-prio", "max-target-r"]),
+ "Lacme/test/Widget;-><init()V": (["blocked"], ["max-target-q"]),
+ "Lacme/test/Class;-><init>()V": (["blocked"], ["max-target-o"]),
+ "Lacme/test/Other;->getThing()Z": (["blocked"], ["max-target-p"])
+ }
+ self.assertEqual(expected_diffs, result.diffs, msg="flag differences")
+
+ expected_property_changes = [
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_o_low_priority",
+ values=["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ property_comment=""),
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_p",
+ values=["hiddenapi/hiddenapi-max-target-p.txt"],
+ property_comment=""),
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_q",
+ values=["hiddenapi/hiddenapi-max-target-q.txt"],
+ property_comment=""),
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_r_low_priority",
+ values=["hiddenapi/hiddenapi-max-target-r-low-priority.txt"],
+ property_comment=""),
+ ]
+ self.assertEqual(
+ expected_property_changes,
+ result.property_changes,
+ msg="property changes")
+
+ return result
+
+ def test_build_flags_report(self):
+ result = self.do_test_build_flags(fix=False)
+
+ expected_file_changes = [
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-o-low-priority.txt",
+ description="""Add the following entries:
+ Lacme/test/Class;-><init>()V
+""",
+ ),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt",
+ description="""Add the following entries:
+ Lacme/test/Other;->getThing()Z
+""",
+ ),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-q.txt",
+ description="""Add the following entries:
+ Lacme/test/Widget;-><init()V
+"""),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-r-low-priority.txt",
+ description="""Add the following entries:
+ Lacme/test/Gadget;->NAME:Ljava/lang/String;
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-o.txt",
+ description="""Remove the following entries:
+ Lacme/test/Class;-><init>()V
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-p.txt",
+ description="""Remove the following entries:
+ Lacme/test/Other;->getThing()Z
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-q.txt",
+ description="""Remove the following entries:
+ Lacme/test/Widget;-><init()V
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-r-loprio.txt",
+ description="""Remove the following entries:
+ Lacme/test/Gadget;->NAME:Ljava/lang/String;
+""")
+ ]
+ result.file_changes.sort()
+ self.assertEqual(
+ expected_file_changes, result.file_changes, msg="file_changes")
+
+ def test_build_flags_fix(self):
+ result = self.do_test_build_flags(fix=True)
+
+ expected_file_changes = [
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-o-low-priority.txt",
+ description="Created with 'bcpf' specific entries"),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt",
+ description="Added 'bcpf' specific entries"),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-q.txt",
+ description="Created with 'bcpf' specific entries"),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-r-low-priority.txt",
+ description="Created with 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_O,
+ description="Removed 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_P,
+ description="Removed 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_Q,
+ description="Removed 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_R,
+ description="Removed 'bcpf' specific entries")
+ ]
+
+ result.file_changes.sort()
+ self.assertEqual(
+ expected_file_changes, result.file_changes, msg="file_changes")
+
+ expected_file_contents = {
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-o-low-priority.txt":
+ """
+Lacme/test/Class;-><init>()V
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt":
+ """
+Lacme/old/Class;->getWidget()Lacme/test/Widget;
+Lacme/test/Other;->getThing()Z
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-q.txt":
+ """
+Lacme/test/Widget;-><init()V
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-r-low-priority.txt":
+ """
+Lacme/test/Gadget;->NAME:Ljava/lang/String;
+""",
+ _MAX_TARGET_O:
+ """
+Lacme/items/Magnet;->size:I
+""",
+ _MAX_TARGET_P:
+ """
+Lacme/items/Rocket;->size:I
+""",
+ _MAX_TARGET_Q:
+ """
+Lacme/items/Rock;->size:I
+""",
+ _MAX_TARGET_R:
+ """
+Lacme/items/Lever;->size:I
+""",
+ }
+ for file_change in result.file_changes:
+ path = file_change.path
+ expected_contents = expected_file_contents[path].lstrip()
+ abs_path = os.path.join(self.test_dir, path)
+ with open(abs_path, "r", encoding="utf8") as tio:
+ contents = tio.read()
+ self.assertEqual(
+ expected_contents, contents, msg=f"{path} contents")
+
+ def test_compute_hiddenapi_package_properties(self):
+ fs = {
+ "out/soong/.intermediates/bcpf-dir/bcpf/all-flags.csv":
+ """
+La/b/C;->m()V
+La/b/c/D;->m()V
+La/b/c/E;->m()V
+Lb/c/D;->m()V
+Lb/c/E;->m()V
+Lb/c/d/E;->m()V
+""",
+ "out/soong/hiddenapi/hiddenapi-flags.csv":
+ """
+La/b/C;->m()V
+La/b/D;->m()V
+La/b/E;->m()V
+La/b/c/D;->m()V
+La/b/c/E;->m()V
+La/b/c/d/E;->m()V
+La/b/c/d/e/F;->m()V
+Lb/c/D;->m()V
+Lb/c/E;->m()V
+Lb/c/d/E;->m()V
+"""
+ }
+ analyzer = self.create_analyzer_for_test(fs)
+ analyzer.load_all_flags()
+
+ result = ab.Result()
+ analyzer.compute_hiddenapi_package_properties(result)
+ self.assertEqual(["a.b"], list(result.split_packages.keys()))
+
+ reason = result.split_packages["a.b"]
+ self.assertEqual(["a.b.C"], reason.bcpf)
+ self.assertEqual(["a.b.D", "a.b.E"], reason.other)
+
+ self.assertEqual(["a.b.c"], list(result.single_packages.keys()))
+
+ reason = result.single_packages["a.b.c"]
+ self.assertEqual(["a.b.c"], reason.bcpf)
+ self.assertEqual(["a.b.c.d", "a.b.c.d.e"], reason.other)
+
+ self.assertEqual(["b"], result.package_prefixes)
+
+
+class TestHiddenApiPropertyChange(unittest.TestCase):
+
+ def setUp(self):
+ # Create a temporary directory
+ self.test_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ # Remove the directory after the test
+ shutil.rmtree(self.test_dir)
+
+ def check_change_fix(self, change, bpmodify_output, expected):
+ file = os.path.join(self.test_dir, "Android.bp")
+
+ with open(file, "w", encoding="utf8") as tio:
+ tio.write(bpmodify_output.strip("\n"))
+
+ bpmodify_runner = ab.BpModifyRunner(
+ os.path.join(os.path.dirname(sys.argv[0]), "bpmodify"))
+ change.fix_bp_file(file, "bcpf", bpmodify_runner)
+
+ with open(file, "r", encoding="utf8") as tio:
+ contents = tio.read()
+ self.assertEqual(expected.lstrip("\n"), contents)
+
+ def check_change_snippet(self, change, expected):
+ snippet = change.snippet(" ")
+ self.assertEqual(expected, snippet)
+
+ def test_change_property_with_value_no_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=["android.provider"],
+ )
+
+ self.check_change_snippet(
+ change, """
+ split_packages: [
+ "android.provider",
+ ],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "android.provider",
+ ],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "android.provider",
+ ],
+ },
+}
+""")
+
+ def test_change_property_with_value_and_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=["android.provider"],
+ property_comment=_MULTI_LINE_COMMENT,
+ )
+
+ self.check_change_snippet(
+ change, """
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "android.provider",
+ ],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "android.provider",
+ ],
+
+ single_packages: [
+ "android.system",
+ ],
+
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "android.provider",
+ ],
+
+ single_packages: [
+ "android.system",
+ ],
+
+ },
+}
+""")
+
+ def test_set_property_with_value_and_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=["another.provider", "other.system"],
+ property_comment=_MULTI_LINE_COMMENT,
+ action=ab.PropertyChangeAction.REPLACE,
+ )
+
+ self.check_change_snippet(
+ change, """
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+ },
+}
+""")
+
+ def test_set_property_with_no_value_or_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=[],
+ action=ab.PropertyChangeAction.REPLACE,
+ )
+
+ self.check_change_snippet(change, """
+ split_packages: [],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+ package_prefixes: ["android.provider"],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [],
+ package_prefixes: ["android.provider"],
+ },
+}
+""")
+
+ def test_set_empty_property_with_no_value_or_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=[],
+ action=ab.PropertyChangeAction.REPLACE,
+ )
+
+ self.check_change_snippet(change, """
+ split_packages: [],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [],
+ package_prefixes: ["android.provider"],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [],
+ package_prefixes: ["android.provider"],
+ },
+}
+""")
+
+
+if __name__ == "__main__":
+ unittest.main(verbosity=3)
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
index 35e0948..6546c7f 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -27,6 +27,7 @@
FLAG_MAX_TARGET_P = 'max-target-p'
FLAG_MAX_TARGET_Q = 'max-target-q'
FLAG_MAX_TARGET_R = 'max-target-r'
+FLAG_MAX_TARGET_S = 'max-target-s'
FLAG_CORE_PLATFORM_API = 'core-platform-api'
FLAG_PUBLIC_API = 'public-api'
FLAG_SYSTEM_API = 'system-api'
@@ -41,6 +42,7 @@
FLAG_MAX_TARGET_P,
FLAG_MAX_TARGET_Q,
FLAG_MAX_TARGET_R,
+ FLAG_MAX_TARGET_S,
]
ALL_FLAGS = FLAGS_API_LIST + [
FLAG_CORE_PLATFORM_API,
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
index e75ee95..5a82be7 100755
--- a/scripts/hiddenapi/signature_patterns.py
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -25,92 +25,138 @@
import sys
-def dict_reader(csvfile):
+def dict_reader(csv_file):
return csv.DictReader(
- csvfile, delimiter=',', quotechar='|', fieldnames=['signature'])
+ csv_file, delimiter=',', quotechar='|', fieldnames=['signature'])
-def dotPackageToSlashPackage(pkg):
+def dot_package_to_slash_package(pkg):
return pkg.replace('.', '/')
-def slashPackageToDotPackage(pkg):
+def dot_packages_to_slash_packages(pkgs):
+ return [dot_package_to_slash_package(p) for p in pkgs]
+
+
+def slash_package_to_dot_package(pkg):
return pkg.replace('/', '.')
-def isSplitPackage(splitPackages, pkg):
- return splitPackages and (pkg in splitPackages or '*' in splitPackages)
+def slash_packages_to_dot_packages(pkgs):
+ return [slash_package_to_dot_package(p) for p in pkgs]
-def matchedByPackagePrefixPattern(packagePrefixes, prefix):
- for packagePrefix in packagePrefixes:
+def is_split_package(split_packages, pkg):
+ return split_packages and (pkg in split_packages or '*' in split_packages)
+
+
+def matched_by_package_prefix_pattern(package_prefixes, prefix):
+ for packagePrefix in package_prefixes:
if prefix == packagePrefix:
return packagePrefix
- elif prefix.startswith(packagePrefix) and prefix[len(
- packagePrefix)] == '/':
+ if (prefix.startswith(packagePrefix) and
+ prefix[len(packagePrefix)] == '/'):
return packagePrefix
return False
-def validate_package_prefixes(splitPackages, packagePrefixes):
+def validate_package_is_not_matched_by_package_prefix(package_type, pkg,
+ package_prefixes):
+ package_prefix = matched_by_package_prefix_pattern(package_prefixes, pkg)
+ if package_prefix:
+ # A package prefix matches the package.
+ package_for_output = slash_package_to_dot_package(pkg)
+ package_prefix_for_output = slash_package_to_dot_package(package_prefix)
+ return [
+ f'{package_type} {package_for_output} is matched by '
+ f'package prefix {package_prefix_for_output}'
+ ]
+ return []
+
+
+def validate_package_prefixes(split_packages, single_packages,
+ package_prefixes):
# If there are no package prefixes then there is no possible conflict
# between them and the split packages.
- if len(packagePrefixes) == 0:
- return
+ if len(package_prefixes) == 0:
+ return []
# Check to make sure that the split packages and package prefixes do not
# overlap.
errors = []
- for splitPackage in splitPackages:
- if splitPackage == '*':
+ for split_package in split_packages:
+ if split_package == '*':
# A package prefix matches a split package.
- packagePrefixesForOutput = ', '.join(
- map(slashPackageToDotPackage, packagePrefixes))
+ package_prefixes_for_output = ', '.join(
+ slash_packages_to_dot_packages(package_prefixes))
errors.append(
- 'split package "*" conflicts with all package prefixes %s\n'
- ' add split_packages:[] to fix' % packagePrefixesForOutput)
+ "split package '*' conflicts with all package prefixes "
+ f'{package_prefixes_for_output}\n'
+ ' add split_packages:[] to fix')
else:
- packagePrefix = matchedByPackagePrefixPattern(
- packagePrefixes, splitPackage)
- if packagePrefix:
- # A package prefix matches a split package.
- splitPackageForOutput = slashPackageToDotPackage(splitPackage)
- packagePrefixForOutput = slashPackageToDotPackage(packagePrefix)
- errors.append(
- 'split package %s is matched by package prefix %s' %
- (splitPackageForOutput, packagePrefixForOutput))
+ errs = validate_package_is_not_matched_by_package_prefix(
+ 'split package', split_package, package_prefixes)
+ errors.extend(errs)
+
+ # Check to make sure that the single packages and package prefixes do not
+ # overlap.
+ for single_package in single_packages:
+ errs = validate_package_is_not_matched_by_package_prefix(
+ 'single package', single_package, package_prefixes)
+ errors.extend(errs)
return errors
-def validate_split_packages(splitPackages):
+def validate_split_packages(split_packages):
errors = []
- if '*' in splitPackages and len(splitPackages) > 1:
+ if '*' in split_packages and len(split_packages) > 1:
errors.append('split packages are invalid as they contain both the'
' wildcard (*) and specific packages, use the wildcard or'
' specific packages, not a mixture')
return errors
-def produce_patterns_from_file(file, splitPackages=None, packagePrefixes=None):
- with open(file, 'r') as f:
- return produce_patterns_from_stream(f, splitPackages, packagePrefixes)
+def validate_single_packages(split_packages, single_packages):
+ overlaps = []
+ for single_package in single_packages:
+ if single_package in split_packages:
+ overlaps.append(single_package)
+ if overlaps:
+ indented = ''.join([f'\n {o}' for o in overlaps])
+ return [
+ f'single_packages and split_packages overlap, please ensure the '
+ f'following packages are only present in one:{indented}'
+ ]
+ return []
+
+
+def produce_patterns_from_file(file,
+ split_packages=None,
+ single_packages=None,
+ package_prefixes=None):
+ with open(file, 'r', encoding='utf8') as f:
+ return produce_patterns_from_stream(f, split_packages, single_packages,
+ package_prefixes)
def produce_patterns_from_stream(stream,
- splitPackages=None,
- packagePrefixes=None):
- splitPackages = set(splitPackages or [])
- packagePrefixes = list(packagePrefixes or [])
+ split_packages=None,
+ single_packages=None,
+ package_prefixes=None):
+ split_packages = set(split_packages or [])
+ single_packages = set(single_packages or [])
+ package_prefixes = list(package_prefixes or [])
# Read in all the signatures into a list and remove any unnecessary class
# and member names.
patterns = set()
+ unmatched_packages = set()
for row in dict_reader(stream):
signature = row['signature']
text = signature.removeprefix('L')
# Remove the class specific member signature
pieces = text.split(';->')
- qualifiedClassName = pieces[0]
- pieces = qualifiedClassName.rsplit('/', maxsplit=1)
+ qualified_class_name = pieces[0]
+ pieces = qualified_class_name.rsplit('/', maxsplit=1)
pkg = pieces[0]
# If the package is split across multiple modules then it cannot be used
# to select the subset of the monolithic flags that this module
@@ -121,27 +167,54 @@
# If the package is not split then every class in the package must be
# provided by this module so there is no need to list the classes
# explicitly so just use the package name instead.
- if isSplitPackage(splitPackages, pkg):
+ if is_split_package(split_packages, pkg):
# Remove inner class names.
- pieces = qualifiedClassName.split('$', maxsplit=1)
+ pieces = qualified_class_name.split('$', maxsplit=1)
pattern = pieces[0]
- else:
+ patterns.add(pattern)
+ elif pkg in single_packages:
# Add a * to ensure that the pattern matches the classes in that
# package.
pattern = pkg + '/*'
- patterns.add(pattern)
+ patterns.add(pattern)
+ else:
+ unmatched_packages.add(pkg)
+
+ # Remove any unmatched packages that would be matched by a package prefix
+ # pattern.
+ unmatched_packages = [
+ p for p in unmatched_packages
+ if not matched_by_package_prefix_pattern(package_prefixes, p)
+ ]
+ errors = []
+ if unmatched_packages:
+ unmatched_packages.sort()
+ indented = ''.join([
+ f'\n {slash_package_to_dot_package(p)}'
+ for p in unmatched_packages
+ ])
+ errors.append('The following packages were unexpected, please add them '
+ 'to one of the hidden_api properties, split_packages, '
+ f'single_packages or package_prefixes:{indented}')
# Remove any patterns that would be matched by a package prefix pattern.
- patterns = list(
- filter(lambda p: not matchedByPackagePrefixPattern(packagePrefixes, p),
- patterns))
+ patterns = [
+ p for p in patterns
+ if not matched_by_package_prefix_pattern(package_prefixes, p)
+ ]
# Add the package prefix patterns to the list. Add a ** to ensure that each
# package prefix pattern will match the classes in that package and all
# sub-packages.
- patterns = patterns + list(map(lambda x: x + '/**', packagePrefixes))
+ patterns = patterns + [f'{p}/**' for p in package_prefixes]
# Sort the patterns.
patterns.sort()
- return patterns
+ return patterns, errors
+
+
+def print_and_exit(errors):
+ for error in errors:
+ print(error)
+ sys.exit(1)
def main(args):
@@ -155,35 +228,50 @@
args_parser.add_argument(
'--split-package',
action='append',
- help='A package that is split across multiple bootclasspath_fragment modules'
- )
+ help='A package that is split across multiple bootclasspath_fragment '
+ 'modules')
args_parser.add_argument(
'--package-prefix',
action='append',
help='A package prefix unique to this set of flags')
+ args_parser.add_argument(
+ '--single-package',
+ action='append',
+ help='A single package unique to this set of flags')
args_parser.add_argument('--output', help='Generated signature prefixes')
args = args_parser.parse_args(args)
- splitPackages = set(map(dotPackageToSlashPackage, args.split_package or []))
- errors = validate_split_packages(splitPackages)
+ split_packages = set(
+ dot_packages_to_slash_packages(args.split_package or []))
+ errors = validate_split_packages(split_packages)
+ if errors:
+ print_and_exit(errors)
- packagePrefixes = list(
- map(dotPackageToSlashPackage, args.package_prefix or []))
+ single_packages = list(
+ dot_packages_to_slash_packages(args.single_package or []))
- if not errors:
- errors = validate_package_prefixes(splitPackages, packagePrefixes)
+ errors = validate_single_packages(split_packages, single_packages)
+ if errors:
+ print_and_exit(errors)
+
+ package_prefixes = dot_packages_to_slash_packages(args.package_prefix or [])
+
+ errors = validate_package_prefixes(split_packages, single_packages,
+ package_prefixes)
+ if errors:
+ print_and_exit(errors)
+
+ patterns = []
+ # Read in all the patterns into a list.
+ patterns, errors = produce_patterns_from_file(args.flags, split_packages,
+ single_packages,
+ package_prefixes)
if errors:
- for error in errors:
- print(error)
- sys.exit(1)
-
- # Read in all the patterns into a list.
- patterns = produce_patterns_from_file(args.flags, splitPackages,
- packagePrefixes)
+ print_and_exit(errors)
# Write out all the patterns.
- with open(args.output, 'w') as outputFile:
+ with open(args.output, 'w', encoding='utf8') as outputFile:
for pattern in patterns:
outputFile.write(pattern)
outputFile.write('\n')
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
index b59dfd7..90b3d0b 100755
--- a/scripts/hiddenapi/signature_patterns_test.py
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -17,37 +17,52 @@
import io
import unittest
-from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
+import signature_patterns
class TestGeneratedPatterns(unittest.TestCase):
- csvFlags = """
+ csv_flags = """
Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
"""
- def produce_patterns_from_string(self,
- csv,
- splitPackages=None,
- packagePrefixes=None):
- with io.StringIO(csv) as f:
- return produce_patterns_from_stream(f, splitPackages,
- packagePrefixes)
+ @staticmethod
+ def produce_patterns_from_string(csv_text,
+ split_packages=None,
+ single_packages=None,
+ package_prefixes=None):
+ with io.StringIO(csv_text) as f:
+ return signature_patterns.produce_patterns_from_stream(
+ f, split_packages, single_packages, package_prefixes)
+
+ def test_generate_unmatched(self):
+ _, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags)
+ self.assertEqual([
+ 'The following packages were unexpected, please add them to one of '
+ 'the hidden_api properties, split_packages, single_packages or '
+ 'package_prefixes:\n'
+ ' java.lang'
+ ], errors)
def test_generate_default(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags)
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, single_packages=['java/lang'])
+ self.assertEqual([], errors)
+
expected = [
'java/lang/*',
]
self.assertEqual(expected, patterns)
def test_generate_split_package(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, splitPackages={'java/lang'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, split_packages={'java/lang'})
+ self.assertEqual([], errors)
+
expected = [
'java/lang/Character',
'java/lang/Object',
@@ -56,8 +71,10 @@
self.assertEqual(expected, patterns)
def test_generate_split_package_wildcard(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, splitPackages={'*'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, split_packages={'*'})
+ self.assertEqual([], errors)
+
expected = [
'java/lang/Character',
'java/lang/Object',
@@ -66,23 +83,27 @@
self.assertEqual(expected, patterns)
def test_generate_package_prefix(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, packagePrefixes={'java/lang'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, package_prefixes={'java/lang'})
+ self.assertEqual([], errors)
+
expected = [
'java/lang/**',
]
self.assertEqual(expected, patterns)
def test_generate_package_prefix_top_package(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, packagePrefixes={'java'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, package_prefixes={'java'})
+ self.assertEqual([], errors)
+
expected = [
'java/**',
]
self.assertEqual(expected, patterns)
def test_split_package_wildcard_conflicts_with_other_split_packages(self):
- errors = validate_split_packages({'*', 'java'})
+ errors = signature_patterns.validate_split_packages({'*', 'java'})
expected = [
'split packages are invalid as they contain both the wildcard (*)'
' and specific packages, use the wildcard or specific packages,'
@@ -91,21 +112,39 @@
self.assertEqual(expected, errors)
def test_split_package_wildcard_conflicts_with_package_prefixes(self):
- errors = validate_package_prefixes({'*'}, packagePrefixes={'java'})
+ errors = signature_patterns.validate_package_prefixes(
+ {'*'}, [], package_prefixes={'java'})
expected = [
- 'split package "*" conflicts with all package prefixes java\n'
+ "split package '*' conflicts with all package prefixes java\n"
' add split_packages:[] to fix',
]
self.assertEqual(expected, errors)
- def test_split_package_conflict(self):
- errors = validate_package_prefixes({'java/split'},
- packagePrefixes={'java'})
+ def test_split_package_conflicts_with_package_prefixes(self):
+ errors = signature_patterns.validate_package_prefixes(
+ {'java/split'}, [], package_prefixes={'java'})
expected = [
'split package java.split is matched by package prefix java',
]
self.assertEqual(expected, errors)
+ def test_single_package_conflicts_with_package_prefixes(self):
+ errors = signature_patterns.validate_package_prefixes(
+ {}, ['java/single'], package_prefixes={'java'})
+ expected = [
+ 'single package java.single is matched by package prefix java',
+ ]
+ self.assertEqual(expected, errors)
+
+ def test_single_package_conflicts_with_split_packages(self):
+ errors = signature_patterns.validate_single_packages({'java/pkg'},
+ ['java/pkg'])
+ expected = [
+ 'single_packages and split_packages overlap, please ensure the '
+ 'following packages are only present in one:\n java/pkg'
+ ]
+ self.assertEqual(expected, errors)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
new file mode 100644
index 0000000..3650fa1
--- /dev/null
+++ b/scripts/hiddenapi/signature_trie.py
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Verify that one set of hidden API flags is a subset of another."""
+import dataclasses
+import typing
+
+from itertools import chain
+
+
+@dataclasses.dataclass()
+class Node:
+ """A node in the signature trie."""
+
+ # The type of the node.
+ #
+ # Leaf nodes are of type "member".
+ # Interior nodes can be either "package", or "class".
+ type: str
+
+ # The selector of the node.
+ #
+ # That is a string that can be used to select the node, e.g. in a pattern
+ # that is passed to InteriorNode.get_matching_rows().
+ selector: str
+
+ def values(self, selector):
+ """Get the values from a set of selected nodes.
+
+ :param selector: a function that can be applied to a key in the nodes
+ attribute to determine whether to return its values.
+
+ :return: A list of iterables of all the values associated with
+ this node and its children.
+ """
+ values = []
+ self.append_values(values, selector)
+ return values
+
+ def append_values(self, values, selector):
+ """Append the values associated with this node and its children.
+
+ For each item (key, child) in nodes the child node's values are returned
+ if and only if the selector returns True when called on its key. A child
+ node's values are all the values associated with it and all its
+ descendant nodes.
+
+ :param selector: a function that can be applied to a key in the nodes
+ attribute to determine whether to return its values.
+ :param values: a list of a iterables of values.
+ """
+ raise NotImplementedError("Please Implement this method")
+
+ def child_nodes(self):
+ """Get an iterable of the child nodes of this node."""
+ raise NotImplementedError("Please Implement this method")
+
+
+# pylint: disable=line-too-long
+@dataclasses.dataclass()
+class InteriorNode(Node):
+ """An interior node in a trie.
+
+ Each interior node has a dict that maps from an element of a signature to
+ either another interior node or a leaf. Each interior node represents either
+ a package, class or nested class. Class members are represented by a Leaf.
+
+ Associating the set of flags [public-api] with the signature
+ "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
+ nodes to be created:
+ Node()
+ ^- package:java -> Node()
+ ^- package:lang -> Node()
+ ^- class:Object -> Node()
+ ^- member:String()Ljava/lang/String; -> Leaf([public-api])
+
+ Associating the set of flags [blocked,core-platform-api] with the signature
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
+ will cause the following nodes to be created:
+ Node()
+ ^- package:java -> Node()
+ ^- package:lang -> Node()
+ ^- class:Character -> Node()
+ ^- class:UnicodeScript -> Node()
+ ^- member:of(I)Ljava/lang/Character$UnicodeScript;
+ -> Leaf([blocked,core-platform-api])
+ """
+
+ # pylint: enable=line-too-long
+
+ # A dict from an element of the signature to the Node/Leaf containing the
+ # next element/value.
+ nodes: typing.Dict[str, Node] = dataclasses.field(default_factory=dict)
+
+ # pylint: disable=line-too-long
+ @staticmethod
+ def signature_to_elements(signature):
+ """Split a signature or a prefix into a number of elements:
+
+ 1. The packages (excluding the leading L preceding the first package).
+ 2. The class names, from outermost to innermost.
+ 3. The member signature.
+ e.g.
+ Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+ will be broken down into these elements:
+ 1. package:java
+ 2. package:lang
+ 3. class:Character
+ 4. class:UnicodeScript
+ 5. member:of(I)Ljava/lang/Character$UnicodeScript;
+ """
+ # Remove the leading L.
+ # - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+ text = signature.removeprefix("L")
+ # Split the signature between qualified class name and the class member
+ # signature.
+ # 0 - java/lang/Character$UnicodeScript
+ # 1 - of(I)Ljava/lang/Character$UnicodeScript;
+ parts = text.split(";->")
+ # If there is no member then this will be an empty list.
+ member = parts[1:]
+ # Split the qualified class name into packages, and class name.
+ # 0 - java
+ # 1 - lang
+ # 2 - Character$UnicodeScript
+ elements = parts[0].split("/")
+ last_element = elements[-1]
+ wildcard = []
+ classes = []
+ if "*" in last_element:
+ if last_element not in ("*", "**"):
+ raise Exception(f"Invalid signature '{signature}': invalid "
+ f"wildcard '{last_element}'")
+ packages = elements[0:-1]
+ # Cannot specify a wildcard and target a specific member
+ if member:
+ raise Exception(f"Invalid signature '{signature}': contains "
+ f"wildcard '{last_element}' and "
+ f"member signature '{member[0]}'")
+ wildcard = [last_element]
+ elif last_element.islower():
+ raise Exception(f"Invalid signature '{signature}': last element "
+ f"'{last_element}' is lower case but should be an "
+ f"upper case class name or wildcard")
+ else:
+ packages = elements[0:-1]
+ # Split the class name into outer / inner classes
+ # 0 - Character
+ # 1 - UnicodeScript
+ classes = last_element.removesuffix(";").split("$")
+
+ # Assemble the parts into a single list, adding prefixes to identify
+ # the different parts. If a wildcard is provided then it looks something
+ # like this:
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - *
+ #
+ # Otherwise, it looks something like this:
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - class:Character
+ # 3 - class:UnicodeScript
+ # 4 - member:of(I)Ljava/lang/Character$UnicodeScript;
+ return list(
+ chain([("package", x) for x in packages],
+ [("class", x) for x in classes],
+ [("member", x) for x in member],
+ [("wildcard", x) for x in wildcard]))
+
+ # pylint: enable=line-too-long
+
+ @staticmethod
+ def split_element(element):
+ element_type, element_value = element
+ return element_type, element_value
+
+ @staticmethod
+ def element_type(element):
+ element_type, _ = InteriorNode.split_element(element)
+ return element_type
+
+ @staticmethod
+ def elements_to_selector(elements):
+ """Compute a selector for a set of elements.
+
+ A selector uniquely identifies a specific Node in the trie. It is
+ essentially a prefix of a signature (without the leading L).
+
+ e.g. a trie containing "Ljava/lang/Object;->String()Ljava/lang/String;"
+ would contain nodes with the following selectors:
+ * "java"
+ * "java/lang"
+ * "java/lang/Object"
+ * "java/lang/Object;->String()Ljava/lang/String;"
+ """
+ signature = ""
+ preceding_type = ""
+ for element in elements:
+ element_type, element_value = InteriorNode.split_element(element)
+ separator = ""
+ if element_type == "package":
+ separator = "/"
+ elif element_type == "class":
+ if preceding_type == "class":
+ separator = "$"
+ else:
+ separator = "/"
+ elif element_type == "wildcard":
+ separator = "/"
+ elif element_type == "member":
+ separator += ";->"
+
+ if signature:
+ signature += separator
+
+ signature += element_value
+
+ preceding_type = element_type
+
+ return signature
+
+ def add(self, signature, value, only_if_matches=False):
+ """Associate the value with the specific signature.
+
+ :param signature: the member signature
+ :param value: the value to associated with the signature
+ :param only_if_matches: True if the value is added only if the signature
+ matches at least one of the existing top level packages.
+ :return: n/a
+ """
+ # Split the signature into elements.
+ elements = self.signature_to_elements(signature)
+ # Find the Node associated with the deepest class.
+ node = self
+ for index, element in enumerate(elements[:-1]):
+ if element in node.nodes:
+ node = node.nodes[element]
+ elif only_if_matches and index == 0:
+ return
+ else:
+ selector = self.elements_to_selector(elements[0:index + 1])
+ next_node = InteriorNode(
+ type=InteriorNode.element_type(element), selector=selector)
+ node.nodes[element] = next_node
+ node = next_node
+ # Add a Leaf containing the value and associate it with the member
+ # signature within the class.
+ last_element = elements[-1]
+ last_element_type = self.element_type(last_element)
+ if last_element_type != "member":
+ raise Exception(
+ f"Invalid signature: {signature}, does not identify a "
+ "specific member")
+ if last_element in node.nodes:
+ raise Exception(f"Duplicate signature: {signature}")
+ leaf = Leaf(
+ type=last_element_type,
+ selector=signature,
+ value=value,
+ )
+ node.nodes[last_element] = leaf
+
+ def get_matching_rows(self, pattern):
+ """Get the values (plural) associated with the pattern.
+
+ e.g. If the pattern is a full signature then this will return a list
+ containing the value associated with that signature.
+
+ If the pattern is a class then this will return a list containing the
+ values associated with all members of that class.
+
+ If the pattern ends with "*" then the preceding part is treated as a
+ package and this will return a list containing the values associated
+ with all the members of all the classes in that package.
+
+ If the pattern ends with "**" then the preceding part is treated
+ as a package and this will return a list containing the values
+ associated with all the members of all the classes in that package and
+ all sub-packages.
+
+ :param pattern: the pattern which could be a complete signature or a
+ class, or package wildcard.
+ :return: an iterable containing all the values associated with the
+ pattern.
+ """
+ elements = self.signature_to_elements(pattern)
+ node = self
+
+ # Include all values from this node and all its children.
+ selector = lambda x: True
+
+ last_element = elements[-1]
+ last_element_type, last_element_value = self.split_element(last_element)
+ if last_element_type == "wildcard":
+ elements = elements[:-1]
+ if last_element_value == "*":
+ # Do not include values from sub-packages.
+ selector = lambda x: InteriorNode.element_type(x) != "package"
+
+ for element in elements:
+ if element in node.nodes:
+ node = node.nodes[element]
+ else:
+ return []
+
+ return node.values(selector)
+
+ def append_values(self, values, selector):
+ for key, node in self.nodes.items():
+ if selector(key):
+ node.append_values(values, lambda x: True)
+
+ def child_nodes(self):
+ return self.nodes.values()
+
+
+@dataclasses.dataclass()
+class Leaf(Node):
+ """A leaf of the trie"""
+
+ # The value associated with this leaf.
+ value: typing.Any
+
+ def append_values(self, values, selector):
+ values.append(self.value)
+
+ def child_nodes(self):
+ return []
+
+
+def signature_trie():
+ return InteriorNode(type="root", selector="")
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
new file mode 100755
index 0000000..6d4e660
--- /dev/null
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Unit tests for verify_overlaps_test.py."""
+import io
+import unittest
+
+from signature_trie import InteriorNode
+from signature_trie import signature_trie
+
+
+class TestSignatureToElements(unittest.TestCase):
+
+ @staticmethod
+ def signature_to_elements(signature):
+ return InteriorNode.signature_to_elements(signature)
+
+ @staticmethod
+ def elements_to_signature(elements):
+ return InteriorNode.elements_to_selector(elements)
+
+ def test_nested_inner_classes(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "ProcessBuilder"),
+ ("class", "Redirect"),
+ ("class", "1"),
+ ("member", "<init>()V"),
+ ]
+ signature = "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_basic_member(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "Object"),
+ ("member", "hashCode()I"),
+ ]
+ signature = "Ljava/lang/Object;->hashCode()I"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_double_dollar_class(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "CharSequence"),
+ ("class", ""),
+ ("class", "ExternalSyntheticLambda0"),
+ ("member", "<init>(Ljava/lang/CharSequence;)V"),
+ ]
+ signature = "Ljava/lang/CharSequence$$ExternalSyntheticLambda0;" \
+ "-><init>(Ljava/lang/CharSequence;)V"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_no_member(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "CharSequence"),
+ ("class", ""),
+ ("class", "ExternalSyntheticLambda0"),
+ ]
+ signature = "Ljava/lang/CharSequence$$ExternalSyntheticLambda0"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_wildcard(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("wildcard", "*"),
+ ]
+ signature = "java/lang/*"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_recursive_wildcard(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("wildcard", "**"),
+ ]
+ signature = "java/lang/**"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_no_packages_wildcard(self):
+ elements = [
+ ("wildcard", "*"),
+ ]
+ signature = "*"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_no_packages_recursive_wildcard(self):
+ elements = [
+ ("wildcard", "**"),
+ ]
+ signature = "**"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_invalid_no_class_or_wildcard(self):
+ signature = "java/lang"
+ with self.assertRaises(Exception) as context:
+ self.signature_to_elements(signature)
+ self.assertIn(
+ "last element 'lang' is lower case but should be an "
+ "upper case class name or wildcard", str(context.exception))
+
+ def test_non_standard_class_name(self):
+ elements = [
+ ("package", "javax"),
+ ("package", "crypto"),
+ ("class", "extObjectInputStream"),
+ ]
+ signature = "Ljavax/crypto/extObjectInputStream"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_invalid_pattern_wildcard(self):
+ pattern = "Ljava/lang/Class*"
+ with self.assertRaises(Exception) as context:
+ self.signature_to_elements(pattern)
+ self.assertIn("invalid wildcard 'Class*'", str(context.exception))
+
+ def test_invalid_pattern_wildcard_and_member(self):
+ pattern = "Ljava/lang/*;->hashCode()I"
+ with self.assertRaises(Exception) as context:
+ self.signature_to_elements(pattern)
+ self.assertIn(
+ "contains wildcard '*' and member signature 'hashCode()I'",
+ str(context.exception))
+
+
+class TestValues(unittest.TestCase):
+ def test_add_then_get(self):
+ trie = signature_trie()
+ trie.add("La/b/C;->l()", 1)
+ trie.add("La/b/C$D;->m()", "A")
+ trie.add("La/b/C$D;->n()", {})
+
+ package_a_node = next(iter(trie.child_nodes()))
+ self.assertEqual("package", package_a_node.type)
+ self.assertEqual("a", package_a_node.selector)
+
+ package_b_node = next(iter(package_a_node.child_nodes()))
+ self.assertEqual("package", package_b_node.type)
+ self.assertEqual("a/b", package_b_node.selector)
+
+ class_c_node = next(iter(package_b_node.child_nodes()))
+ self.assertEqual("class", class_c_node.type)
+ self.assertEqual("a/b/C", class_c_node.selector)
+
+ self.assertEqual([1, "A", {}], class_c_node.values(lambda _: True))
+
+class TestGetMatchingRows(unittest.TestCase):
+ extractInput = """
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+Ljava/lang/Character;->serialVersionUID:J
+Ljava/lang/Object;->hashCode()I
+Ljava/lang/Object;->toString()Ljava/lang/String;
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V
+Ljava/util/zip/ZipFile;-><clinit>()V
+"""
+
+ def read_trie(self):
+ trie = signature_trie()
+ with io.StringIO(self.extractInput.strip()) as f:
+ for line in iter(f.readline, ""):
+ line = line.rstrip()
+ trie.add(line, line)
+ return trie
+
+ def check_patterns(self, pattern, expected):
+ trie = self.read_trie()
+ self.check_node_patterns(trie, pattern, expected)
+
+ def check_node_patterns(self, node, pattern, expected):
+ actual = list(node.get_matching_rows(pattern))
+ actual.sort()
+ self.assertEqual(expected, actual)
+
+ def test_member_pattern(self):
+ self.check_patterns("java/util/zip/ZipFile;-><clinit>()V",
+ ["Ljava/util/zip/ZipFile;-><clinit>()V"])
+
+ def test_class_pattern(self):
+ self.check_patterns("java/lang/Object", [
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ])
+
+ # pylint: disable=line-too-long
+ def test_nested_class_pattern(self):
+ self.check_patterns("java/lang/Character", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ ])
+
+ def test_wildcard(self):
+ self.check_patterns("java/lang/*", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ ])
+
+ def test_recursive_wildcard(self):
+ self.check_patterns("java/**", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ "Ljava/util/zip/ZipFile;-><clinit>()V",
+ ])
+
+ def test_node_wildcard(self):
+ trie = self.read_trie()
+ node = list(trie.child_nodes())[0]
+ self.check_node_patterns(node, "**", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ "Ljava/util/zip/ZipFile;-><clinit>()V",
+ ])
+
+ # pylint: enable=line-too-long
+
+
+if __name__ == "__main__":
+ unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index 4cd7e63..f985a49 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -13,253 +13,28 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Verify that one set of hidden API flags is a subset of another.
-"""
+"""Verify that one set of hidden API flags is a subset of another."""
import argparse
import csv
import sys
from itertools import chain
-#pylint: disable=line-too-long
-class InteriorNode:
- """An interior node in a trie.
-
- Each interior node has a dict that maps from an element of a signature to
- either another interior node or a leaf. Each interior node represents either
- a package, class or nested class. Class members are represented by a Leaf.
-
- Associating the set of flags [public-api] with the signature
- "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
- nodes to be created:
- Node()
- ^- package:java -> Node()
- ^- package:lang -> Node()
- ^- class:Object -> Node()
- ^- member:String()Ljava/lang/String; -> Leaf([public-api])
-
- Associating the set of flags [blocked,core-platform-api] with the signature
- "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
- will cause the following nodes to be created:
- Node()
- ^- package:java -> Node()
- ^- package:lang -> Node()
- ^- class:Character -> Node()
- ^- class:UnicodeScript -> Node()
- ^- member:of(I)Ljava/lang/Character$UnicodeScript;
- -> Leaf([blocked,core-platform-api])
-
- Attributes:
- nodes: a dict from an element of the signature to the Node/Leaf
- containing the next element/value.
- """
- #pylint: enable=line-too-long
-
- def __init__(self):
- self.nodes = {}
-
- #pylint: disable=line-too-long
- def signatureToElements(self, signature):
- """Split a signature or a prefix into a number of elements:
- 1. The packages (excluding the leading L preceding the first package).
- 2. The class names, from outermost to innermost.
- 3. The member signature.
- e.g.
- Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
- will be broken down into these elements:
- 1. package:java
- 2. package:lang
- 3. class:Character
- 4. class:UnicodeScript
- 5. member:of(I)Ljava/lang/Character$UnicodeScript;
- """
- # Remove the leading L.
- # - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
- text = signature.removeprefix("L")
- # Split the signature between qualified class name and the class member
- # signature.
- # 0 - java/lang/Character$UnicodeScript
- # 1 - of(I)Ljava/lang/Character$UnicodeScript;
- parts = text.split(";->")
- member = parts[1:]
- # Split the qualified class name into packages, and class name.
- # 0 - java
- # 1 - lang
- # 2 - Character$UnicodeScript
- elements = parts[0].split("/")
- packages = elements[0:-1]
- className = elements[-1]
- if className in ("*" , "**"): #pylint: disable=no-else-return
- # Cannot specify a wildcard and target a specific member
- if len(member) != 0:
- raise Exception(
- "Invalid signature %s: contains wildcard %s and member " \
- "signature %s"
- % (signature, className, member[0]))
- wildcard = [className]
- # Assemble the parts into a single list, adding prefixes to identify
- # the different parts.
- # 0 - package:java
- # 1 - package:lang
- # 2 - *
- return list(
- chain(["package:" + x for x in packages], wildcard))
- else:
- # Split the class name into outer / inner classes
- # 0 - Character
- # 1 - UnicodeScript
- classes = className.split("$")
- # Assemble the parts into a single list, adding prefixes to identify
- # the different parts.
- # 0 - package:java
- # 1 - package:lang
- # 2 - class:Character
- # 3 - class:UnicodeScript
- # 4 - member:of(I)Ljava/lang/Character$UnicodeScript;
- return list(
- chain(
- ["package:" + x for x in packages],
- ["class:" + x for x in classes],
- ["member:" + x for x in member]))
- #pylint: enable=line-too-long
-
- def add(self, signature, value):
- """Associate the value with the specific signature.
-
- :param signature: the member signature
- :param value: the value to associated with the signature
- :return: n/a
- """
- # Split the signature into elements.
- elements = self.signatureToElements(signature)
- # Find the Node associated with the deepest class.
- node = self
- for element in elements[:-1]:
- if element in node.nodes:
- node = node.nodes[element]
- else:
- next_node = InteriorNode()
- node.nodes[element] = next_node
- node = next_node
- # Add a Leaf containing the value and associate it with the member
- # signature within the class.
- lastElement = elements[-1]
- if not lastElement.startswith("member:"):
- raise Exception(
- "Invalid signature: %s, does not identify a specific member" %
- signature)
- if lastElement in node.nodes:
- raise Exception("Duplicate signature: %s" % signature)
- node.nodes[lastElement] = Leaf(value)
-
- def getMatchingRows(self, pattern):
- """Get the values (plural) associated with the pattern.
-
- e.g. If the pattern is a full signature then this will return a list
- containing the value associated with that signature.
-
- If the pattern is a class then this will return a list containing the
- values associated with all members of that class.
-
- If the pattern is a package then this will return a list containing the
- values associated with all the members of all the classes in that
- package and sub-packages.
-
- If the pattern ends with "*" then the preceding part is treated as a
- package and this will return a list containing the values associated
- with all the members of all the classes in that package.
-
- If the pattern ends with "**" then the preceding part is treated
- as a package and this will return a list containing the values
- associated with all the members of all the classes in that package and
- all sub-packages.
-
- :param pattern: the pattern which could be a complete signature or a
- class, or package wildcard.
- :return: an iterable containing all the values associated with the
- pattern.
- """
- elements = self.signatureToElements(pattern)
- node = self
- # Include all values from this node and all its children.
- selector = lambda x: True
- lastElement = elements[-1]
- if lastElement in ("*", "**"):
- elements = elements[:-1]
- if lastElement == "*":
- # Do not include values from sub-packages.
- selector = lambda x: not x.startswith("package:")
- for element in elements:
- if element in node.nodes:
- node = node.nodes[element]
- else:
- return []
- return chain.from_iterable(node.values(selector))
-
- def values(self, selector):
- """:param selector: a function that can be applied to a key in the nodes
- attribute to determine whether to return its values.
-
- :return: A list of iterables of all the values associated with
- this node and its children.
- """
- values = []
- self.appendValues(values, selector)
- return values
-
- def appendValues(self, values, selector):
- """Append the values associated with this node and its children to the
- list.
-
- For each item (key, child) in nodes the child node's values are returned
- if and only if the selector returns True when called on its key. A child
- node's values are all the values associated with it and all its
- descendant nodes.
-
- :param selector: a function that can be applied to a key in the nodes
- attribute to determine whether to return its values.
- :param values: a list of a iterables of values.
- """
- for key, node in self.nodes.items():
- if selector(key):
- node.appendValues(values, lambda x: True)
+from signature_trie import signature_trie
-class Leaf:
- """A leaf of the trie
-
- Attributes:
- value: the value associated with this leaf.
- """
-
- def __init__(self, value):
- self.value = value
-
- def values(self, selector): #pylint: disable=unused-argument
- """:return: A list of a list of the value associated with this node.
- """
- return [[self.value]]
-
- def appendValues(self, values, selector): #pylint: disable=unused-argument
- """Appends a list of the value associated with this node to the list.
-
- :param values: a list of a iterables of values.
- """
- values.append([self.value])
-
-
-def dict_reader(csvfile):
+def dict_reader(csv_file):
return csv.DictReader(
- csvfile, delimiter=",", quotechar="|", fieldnames=["signature"])
+ csv_file, delimiter=",", quotechar="|", fieldnames=["signature"])
def read_flag_trie_from_file(file):
- with open(file, "r") as stream:
+ with open(file, "r", encoding="utf8") as stream:
return read_flag_trie_from_stream(stream)
def read_flag_trie_from_stream(stream):
- trie = InteriorNode()
+ trie = signature_trie()
reader = dict_reader(stream)
for row in reader:
signature = row["signature"]
@@ -268,26 +43,24 @@
def extract_subset_from_monolithic_flags_as_dict_from_file(
- monolithicTrie, patternsFile):
- """Extract a subset of flags from the dict containing all the monolithic
- flags.
+ monolithic_trie, patterns_file):
+ """Extract a subset of flags from the dict of monolithic flags.
- :param monolithicFlagsDict: the dict containing all the monolithic flags.
- :param patternsFile: a file containing a list of signature patterns that
+ :param monolithic_trie: the trie containing all the monolithic flags.
+ :param patterns_file: a file containing a list of signature patterns that
define the subset.
:return: the dict from signature to row.
"""
- with open(patternsFile, "r") as stream:
+ with open(patterns_file, "r", encoding="utf8") as stream:
return extract_subset_from_monolithic_flags_as_dict_from_stream(
- monolithicTrie, stream)
+ monolithic_trie, stream)
def extract_subset_from_monolithic_flags_as_dict_from_stream(
- monolithicTrie, stream):
- """Extract a subset of flags from the trie containing all the monolithic
- flags.
+ monolithic_trie, stream):
+ """Extract a subset of flags from the trie of monolithic flags.
- :param monolithicTrie: the trie containing all the monolithic flags.
+ :param monolithic_trie: the trie containing all the monolithic flags.
:param stream: a stream containing a list of signature patterns that define
the subset.
:return: the dict from signature to row.
@@ -295,7 +68,7 @@
dict_signature_to_row = {}
for pattern in stream:
pattern = pattern.rstrip()
- rows = monolithicTrie.getMatchingRows(pattern)
+ rows = monolithic_trie.get_matching_rows(pattern)
for row in rows:
signature = row["signature"]
dict_signature_to_row[signature] = row
@@ -303,8 +76,10 @@
def read_signature_csv_from_stream_as_dict(stream):
- """Read the csv contents from the stream into a dict. The first column is
- assumed to be the signature and used as the key.
+ """Read the csv contents from the stream into a dict.
+
+ The first column is assumed to be the signature and used as the
+ key.
The whole row is stored as the value.
:param stream: the csv contents to read
@@ -318,85 +93,110 @@
return dict_signature_to_row
-def read_signature_csv_from_file_as_dict(csvFile):
- """Read the csvFile into a dict. The first column is assumed to be the
- signature and used as the key.
+def read_signature_csv_from_file_as_dict(csv_file):
+ """Read the csvFile into a dict.
+
+ The first column is assumed to be the signature and used as the
+ key.
The whole row is stored as the value.
- :param csvFile: the csv file to read
+ :param csv_file: the csv file to read
:return: the dict from signature to row.
"""
- with open(csvFile, "r") as f:
+ with open(csv_file, "r", encoding="utf8") as f:
return read_signature_csv_from_stream_as_dict(f)
-def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
+def compare_signature_flags(monolithic_flags_dict, modular_flags_dict,
+ implementation_flags):
"""Compare the signature flags between the two dicts.
- :param monolithicFlagsDict: the dict containing the subset of the monolithic
- flags that should be equal to the modular flags.
- :param modularFlagsDict:the dict containing the flags produced by a single
+ :param monolithic_flags_dict: the dict containing the subset of the
+ monolithic flags that should be equal to the modular flags.
+ :param modular_flags_dict:the dict containing the flags produced by a single
bootclasspath_fragment module.
:return: list of mismatches., each mismatch is a tuple where the first item
is the signature, and the second and third items are lists of the flags from
modular dict, and monolithic dict respectively.
"""
- mismatchingSignatures = []
+ mismatching_signatures = []
# Create a sorted set of all the signatures from both the monolithic and
# modular dicts.
- allSignatures = sorted(
- set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
- for signature in allSignatures:
- monolithicRow = monolithicFlagsDict.get(signature, {})
- monolithicFlags = monolithicRow.get(None, [])
- if signature in modularFlagsDict:
- modularRow = modularFlagsDict.get(signature, {})
- modularFlags = modularRow.get(None, [])
+ all_signatures = sorted(
+ set(chain(monolithic_flags_dict.keys(), modular_flags_dict.keys())))
+ for signature in all_signatures:
+ monolithic_row = monolithic_flags_dict.get(signature, {})
+ monolithic_flags = monolithic_row.get(None, [])
+ if signature in modular_flags_dict:
+ modular_row = modular_flags_dict.get(signature, {})
+ modular_flags = modular_row.get(None, [])
else:
- modularFlags = ["blocked"]
- if monolithicFlags != modularFlags:
- mismatchingSignatures.append(
- (signature, modularFlags, monolithicFlags))
- return mismatchingSignatures
+ modular_flags = implementation_flags
+ if monolithic_flags != modular_flags:
+ mismatching_signatures.append(
+ (signature, modular_flags, monolithic_flags))
+ return mismatching_signatures
def main(argv):
args_parser = argparse.ArgumentParser(
description="Verify that sets of hidden API flags are each a subset of "
- "the monolithic flag file."
- )
- args_parser.add_argument("monolithicFlags", help="The monolithic flag file")
+ "the monolithic flag file. For each module this uses the provided "
+ "signature patterns to select a subset of the monolithic flags and "
+ "then it compares that subset against the filtered flags provided by "
+ "the module. If the module's filtered flags does not contain flags for "
+ "a signature then it is assumed to have been filtered out because it "
+ "was not part of an API and so is assumed to have the implementation "
+ "flags.")
args_parser.add_argument(
- "modularFlags",
- nargs=argparse.REMAINDER,
- help="Flags produced by individual bootclasspath_fragment modules")
+ "--monolithic-flags", help="The monolithic flag file")
+ args_parser.add_argument(
+ "--module-flags",
+ action="append",
+ help="A colon separated pair of paths. The first is a path to a "
+ "filtered set of flags, and the second is a path to a set of "
+ "signature patterns that identify the set of classes belonging to "
+ "a single bootclasspath_fragment module. Specify once for each module "
+ "that needs to be checked.")
+ args_parser.add_argument(
+ "--implementation-flag",
+ action="append",
+ help="A flag in the set of flags that identifies a signature which is "
+ "not part of an API, i.e. is the signature of a private implementation "
+ "member. Specify as many times as necessary to define the "
+ "implementation flag set. If this is not specified then the "
+ "implementation flag set is empty.")
args = args_parser.parse_args(argv[1:])
# Read in all the flags into the trie
- monolithicFlagsPath = args.monolithicFlags
- monolithicTrie = read_flag_trie_from_file(monolithicFlagsPath)
+ monolithic_flags_path = args.monolithic_flags
+ monolithic_trie = read_flag_trie_from_file(monolithic_flags_path)
+
+ implementation_flags = args.implementation_flag or []
# For each subset specified on the command line, create dicts for the flags
# provided by the subset and the corresponding flags from the complete set
# of flags and compare them.
failed = False
- for modularPair in args.modularFlags:
- parts = modularPair.split(":")
- modularFlagsPath = parts[0]
- modularPatternsPath = parts[1]
- modularFlagsDict = read_signature_csv_from_file_as_dict(
- modularFlagsPath)
- monolithicFlagsSubsetDict = \
+ module_pairs = args.module_flags or []
+ for modular_pair in module_pairs:
+ parts = modular_pair.split(":")
+ modular_flags_path = parts[0]
+ modular_patterns_path = parts[1]
+ modular_flags_dict = read_signature_csv_from_file_as_dict(
+ modular_flags_path)
+ monolithic_flags_subset_dict = \
extract_subset_from_monolithic_flags_as_dict_from_file(
- monolithicTrie, modularPatternsPath)
- mismatchingSignatures = compare_signature_flags(
- monolithicFlagsSubsetDict, modularFlagsDict)
- if mismatchingSignatures:
+ monolithic_trie, modular_patterns_path)
+ mismatching_signatures = compare_signature_flags(
+ monolithic_flags_subset_dict, modular_flags_dict,
+ implementation_flags)
+ if mismatching_signatures:
failed = True
print("ERROR: Hidden API flags are inconsistent:")
- print("< " + modularFlagsPath)
- print("> " + monolithicFlagsPath)
- for mismatch in mismatchingSignatures:
+ print("< " + modular_flags_path)
+ print("> " + monolithic_flags_path)
+ for mismatch in mismatching_signatures:
signature = mismatch[0]
print()
print("< " + ",".join([signature] + mismatch[1]))
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index 22a1cdf..0a489ee 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -17,69 +17,26 @@
import io
import unittest
-from verify_overlaps import * #pylint: disable=unused-wildcard-import,wildcard-import
+import verify_overlaps as vo
-class TestSignatureToElements(unittest.TestCase):
-
- def signatureToElements(self, signature):
- return InteriorNode().signatureToElements(signature)
-
- def test_signatureToElements_1(self):
- expected = [
- 'package:java',
- 'package:lang',
- 'class:ProcessBuilder',
- 'class:Redirect',
- 'class:1',
- 'member:<init>()V',
- ]
- self.assertEqual(
- expected,
- self.signatureToElements(
- 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V'))
-
- def test_signatureToElements_2(self):
- expected = [
- 'package:java',
- 'package:lang',
- 'class:Object',
- 'member:hashCode()I',
- ]
- self.assertEqual(
- expected,
- self.signatureToElements('Ljava/lang/Object;->hashCode()I'))
-
- def test_signatureToElements_3(self):
- expected = [
- 'package:java',
- 'package:lang',
- 'class:CharSequence',
- 'class:',
- 'class:ExternalSyntheticLambda0',
- 'member:<init>(Ljava/lang/CharSequence;)V',
- ]
- self.assertEqual(
- expected,
- self.signatureToElements(
- 'Ljava/lang/CharSequence$$ExternalSyntheticLambda0;'
- '-><init>(Ljava/lang/CharSequence;)V'))
-
-#pylint: disable=line-too-long
class TestDetectOverlaps(unittest.TestCase):
- def read_flag_trie_from_string(self, csvdata):
+ @staticmethod
+ def read_flag_trie_from_string(csvdata):
with io.StringIO(csvdata) as f:
- return read_flag_trie_from_stream(f)
+ return vo.read_flag_trie_from_stream(f)
- def read_signature_csv_from_string_as_dict(self, csvdata):
+ @staticmethod
+ def read_signature_csv_from_string_as_dict(csvdata):
with io.StringIO(csvdata) as f:
- return read_signature_csv_from_stream_as_dict(f)
+ return vo.read_signature_csv_from_stream_as_dict(f)
+ @staticmethod
def extract_subset_from_monolithic_flags_as_dict_from_string(
- self, monolithic, patterns):
+ monolithic, patterns):
with io.StringIO(patterns) as f:
- return extract_subset_from_monolithic_flags_as_dict_from_stream(
+ return vo.extract_subset_from_monolithic_flags_as_dict_from_stream(
monolithic, f)
extractInput = """
@@ -95,14 +52,14 @@
monolithic = self.read_flag_trie_from_string(
TestDetectOverlaps.extractInput)
- patterns = 'Ljava/lang/Object;->hashCode()I'
+ patterns = "Ljava/lang/Object;->hashCode()I"
subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
monolithic, patterns)
expected = {
- 'Ljava/lang/Object;->hashCode()I': {
- None: ['public-api', 'system-api', 'test-api'],
- 'signature': 'Ljava/lang/Object;->hashCode()I',
+ "Ljava/lang/Object;->hashCode()I": {
+ None: ["public-api", "system-api", "test-api"],
+ "signature": "Ljava/lang/Object;->hashCode()I",
},
}
self.assertEqual(expected, subset)
@@ -111,18 +68,18 @@
monolithic = self.read_flag_trie_from_string(
TestDetectOverlaps.extractInput)
- patterns = 'java/lang/Object'
+ patterns = "java/lang/Object"
subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
monolithic, patterns)
expected = {
- 'Ljava/lang/Object;->hashCode()I': {
- None: ['public-api', 'system-api', 'test-api'],
- 'signature': 'Ljava/lang/Object;->hashCode()I',
+ "Ljava/lang/Object;->hashCode()I": {
+ None: ["public-api", "system-api", "test-api"],
+ "signature": "Ljava/lang/Object;->hashCode()I",
},
- 'Ljava/lang/Object;->toString()Ljava/lang/String;': {
- None: ['blocked'],
- 'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+ "Ljava/lang/Object;->toString()Ljava/lang/String;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Object;->toString()Ljava/lang/String;",
},
}
self.assertEqual(expected, subset)
@@ -131,20 +88,20 @@
monolithic = self.read_flag_trie_from_string(
TestDetectOverlaps.extractInput)
- patterns = 'java/lang/Character'
+ patterns = "java/lang/Character"
subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
monolithic, patterns)
expected = {
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
- {
- None: ['blocked'],
- 'signature':
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
- },
- 'Ljava/lang/Character;->serialVersionUID:J': {
- None: ['sdk'],
- 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+ "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;",
+ },
+ "Ljava/lang/Character;->serialVersionUID:J": {
+ None: ["sdk"],
+ "signature": "Ljava/lang/Character;->serialVersionUID:J",
},
}
self.assertEqual(expected, subset)
@@ -153,17 +110,17 @@
monolithic = self.read_flag_trie_from_string(
TestDetectOverlaps.extractInput)
- patterns = 'java/lang/Character$UnicodeScript'
+ patterns = "java/lang/Character$UnicodeScript"
subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
monolithic, patterns)
expected = {
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
- {
- None: ['blocked'],
- 'signature':
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
- },
+ "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;",
+ },
}
self.assertEqual(expected, subset)
@@ -171,32 +128,32 @@
monolithic = self.read_flag_trie_from_string(
TestDetectOverlaps.extractInput)
- patterns = 'java/lang/*'
+ patterns = "java/lang/*"
subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
monolithic, patterns)
expected = {
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
- {
- None: ['blocked'],
- 'signature':
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
- },
- 'Ljava/lang/Character;->serialVersionUID:J': {
- None: ['sdk'],
- 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+ "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;",
},
- 'Ljava/lang/Object;->hashCode()I': {
- None: ['public-api', 'system-api', 'test-api'],
- 'signature': 'Ljava/lang/Object;->hashCode()I',
+ "Ljava/lang/Character;->serialVersionUID:J": {
+ None: ["sdk"],
+ "signature": "Ljava/lang/Character;->serialVersionUID:J",
},
- 'Ljava/lang/Object;->toString()Ljava/lang/String;': {
- None: ['blocked'],
- 'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+ "Ljava/lang/Object;->hashCode()I": {
+ None: ["public-api", "system-api", "test-api"],
+ "signature": "Ljava/lang/Object;->hashCode()I",
},
- 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
- None: ['blocked'],
- 'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+ "Ljava/lang/Object;->toString()Ljava/lang/String;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ },
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
},
}
self.assertEqual(expected, subset)
@@ -205,59 +162,47 @@
monolithic = self.read_flag_trie_from_string(
TestDetectOverlaps.extractInput)
- patterns = 'java/**'
+ patterns = "java/**"
subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
monolithic, patterns)
expected = {
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
- {
- None: ['blocked'],
- 'signature':
- 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
- },
- 'Ljava/lang/Character;->serialVersionUID:J': {
- None: ['sdk'],
- 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+ "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Character$UnicodeScript;"
+ "->of(I)Ljava/lang/Character$UnicodeScript;",
},
- 'Ljava/lang/Object;->hashCode()I': {
- None: ['public-api', 'system-api', 'test-api'],
- 'signature': 'Ljava/lang/Object;->hashCode()I',
+ "Ljava/lang/Character;->serialVersionUID:J": {
+ None: ["sdk"],
+ "signature": "Ljava/lang/Character;->serialVersionUID:J",
},
- 'Ljava/lang/Object;->toString()Ljava/lang/String;': {
- None: ['blocked'],
- 'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+ "Ljava/lang/Object;->hashCode()I": {
+ None: ["public-api", "system-api", "test-api"],
+ "signature": "Ljava/lang/Object;->hashCode()I",
},
- 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
- None: ['blocked'],
- 'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+ "Ljava/lang/Object;->toString()Ljava/lang/String;": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/Object;->toString()Ljava/lang/String;",
},
- 'Ljava/util/zip/ZipFile;-><clinit>()V': {
- None: ['blocked'],
- 'signature': 'Ljava/util/zip/ZipFile;-><clinit>()V',
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V": {
+ None: ["blocked"],
+ "signature": "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ },
+ "Ljava/util/zip/ZipFile;-><clinit>()V": {
+ None: ["blocked"],
+ "signature": "Ljava/util/zip/ZipFile;-><clinit>()V",
},
}
self.assertEqual(expected, subset)
- def test_extract_subset_invalid_pattern_wildcard_and_member(self):
- monolithic = self.read_flag_trie_from_string(
- TestDetectOverlaps.extractInput)
-
- patterns = 'Ljava/lang/*;->hashCode()I'
-
- with self.assertRaises(Exception) as context:
- self.extract_subset_from_monolithic_flags_as_dict_from_string(
- monolithic, patterns)
- self.assertTrue('contains wildcard * and member signature hashCode()I'
- in str(context.exception))
-
def test_read_trie_duplicate(self):
with self.assertRaises(Exception) as context:
self.read_flag_trie_from_string("""
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
Ljava/lang/Object;->hashCode()I,blocked
""")
- self.assertTrue('Duplicate signature: Ljava/lang/Object;->hashCode()I'
+ self.assertTrue("Duplicate signature: Ljava/lang/Object;->hashCode()I"
in str(context.exception))
def test_read_trie_missing_member(self):
@@ -266,8 +211,8 @@
Ljava/lang/Object,public-api,system-api,test-api
""")
self.assertTrue(
- 'Invalid signature: Ljava/lang/Object, does not identify a specific member'
- in str(context.exception))
+ "Invalid signature: Ljava/lang/Object, "
+ "does not identify a specific member" in str(context.exception))
def test_match(self):
monolithic = self.read_signature_csv_from_string_as_dict("""
@@ -276,7 +221,8 @@
modular = self.read_signature_csv_from_string_as_dict("""
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
""")
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = []
self.assertEqual(expected, mismatches)
@@ -287,12 +233,13 @@
modular = self.read_signature_csv_from_string_as_dict("""
Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
""")
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = [
(
- 'Ljava/lang/Object;->toString()Ljava/lang/String;',
- ['public-api', 'system-api', 'test-api'],
- ['public-api'],
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ["public-api", "system-api", "test-api"],
+ ["public-api"],
),
]
self.assertEqual(expected, mismatches)
@@ -304,12 +251,13 @@
modular = self.read_signature_csv_from_string_as_dict("""
Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
""")
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = [
(
- 'Ljava/lang/Object;->toString()Ljava/lang/String;',
- ['public-api', 'system-api', 'test-api'],
- ['blocked'],
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ["public-api", "system-api", "test-api"],
+ ["blocked"],
),
]
self.assertEqual(expected, mismatches)
@@ -321,26 +269,28 @@
modular = self.read_signature_csv_from_string_as_dict("""
Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
""")
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = [
(
- 'Ljava/lang/Object;->toString()Ljava/lang/String;',
- ['blocked'],
- ['public-api', 'system-api', 'test-api'],
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ["blocked"],
+ ["public-api", "system-api", "test-api"],
),
]
self.assertEqual(expected, mismatches)
def test_match_treat_missing_from_modular_as_blocked(self):
- monolithic = self.read_signature_csv_from_string_as_dict('')
+ monolithic = self.read_signature_csv_from_string_as_dict("")
modular = self.read_signature_csv_from_string_as_dict("""
Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
""")
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = [
(
- 'Ljava/lang/Object;->toString()Ljava/lang/String;',
- ['public-api', 'system-api', 'test-api'],
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ["public-api", "system-api", "test-api"],
[],
),
]
@@ -351,12 +301,13 @@
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
""")
modular = {}
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = [
(
- 'Ljava/lang/Object;->hashCode()I',
- ['blocked'],
- ['public-api', 'system-api', 'test-api'],
+ "Ljava/lang/Object;->hashCode()I",
+ ["blocked"],
+ ["public-api", "system-api", "test-api"],
),
]
self.assertEqual(expected, mismatches)
@@ -366,10 +317,50 @@
Ljava/lang/Object;->hashCode()I,blocked
""")
modular = {}
- mismatches = compare_signature_flags(monolithic, modular)
+ mismatches = vo.compare_signature_flags(monolithic, modular,
+ ["blocked"])
expected = []
self.assertEqual(expected, mismatches)
-#pylint: enable=line-too-long
-if __name__ == '__main__':
+ def test_match_treat_missing_from_modular_as_empty(self):
+ monolithic = self.read_signature_csv_from_string_as_dict("")
+ modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+""")
+ mismatches = vo.compare_signature_flags(monolithic, modular, [])
+ expected = [
+ (
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ["public-api", "system-api", "test-api"],
+ [],
+ ),
+ ]
+ self.assertEqual(expected, mismatches)
+
+ def test_mismatch_treat_missing_from_modular_as_empty(self):
+ monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+""")
+ modular = {}
+ mismatches = vo.compare_signature_flags(monolithic, modular, [])
+ expected = [
+ (
+ "Ljava/lang/Object;->hashCode()I",
+ [],
+ ["public-api", "system-api", "test-api"],
+ ),
+ ]
+ self.assertEqual(expected, mismatches)
+
+ def test_empty_missing_from_modular(self):
+ monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I
+""")
+ modular = {}
+ mismatches = vo.compare_signature_flags(monolithic, modular, [])
+ expected = []
+ self.assertEqual(expected, mismatches)
+
+
+if __name__ == "__main__":
unittest.main(verbosity=2)
diff --git a/scripts/list_image.sh b/scripts/list_image.sh
new file mode 100755
index 0000000..0542fa6
--- /dev/null
+++ b/scripts/list_image.sh
@@ -0,0 +1,51 @@
+#! /bin/bash
+
+# Recursively list Android image directory.
+set -eu
+set -o pipefail
+
+function die() { format=$1; shift; printf "$format\n" "$@"; exit 1; }
+
+# Figure out the filer utility.
+declare filer=
+[[ -z "${ANDROID_HOST_OUT:-}" ]] || filer=${ANDROID_HOST_OUT}/bin/debugfs_static
+if [[ "${1:-}" =~ --debugfs_path=(.*) ]]; then
+ filer=${BASH_REMATCH[1]}
+ shift
+fi
+if [[ -z "${filer:-}" ]]; then
+ maybefiler="$(dirname $0)/debugfs_static"
+ [[ ! -x "$maybefiler" ]] || filer="$maybefiler"
+fi
+
+(( $# >0 )) || die "%s [--debugfs_path=<path>] IMAGE" "$0"
+
+[[ -n "${filer:-}" ]] || die "cannot locate 'debugfs' executable: \
+--debugfs_path= is missing, ANDROID_HOST_OUT is not set, \
+and 'debugfs_static' is not colocated with this script"
+declare -r image="$1"
+
+function dolevel() {
+ printf "%s/\n" "$1"
+ # Each line of the file output consists of 6 fields separated with '/'.
+ # The second one contains the file's attributes, and the fifth its name.
+ $filer -R "ls -l -p $1" "$image" 2>/dev/null |\
+ sed -nr 's|^/.*/(.*)/.*/.*/(.+)/.*/$|\2 \1|p' | LANG=C sort | \
+ while read name attr; do
+ [[ "$name" != '.' && "$name" != '..' ]] || continue
+ path="$1/$name"
+ # If the second char of the attributes is '4', it is a directory.
+ if [[ $attr =~ ^.4 ]]; then
+ dolevel "$path"
+ else
+ printf "%s\n" "$path"
+ fi
+ done
+}
+
+# The filer always prints its version on stderr, so we are going
+# to redirect it to the bit bucket. On the other hand, the filer's
+# return code on error is still 0. Let's run it once to without
+# redirecting stderr to see that there is at least one entry.
+$filer -R "ls -l -p" "$image" | grep -q -m1 -P '^/.*/.*/.*/.*/.+/.*/$'
+dolevel .
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index c8d4f76..0216fc0 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -20,9 +20,11 @@
import argparse
import json
+import os
import re
import subprocess
import sys
+from collections import OrderedDict
from xml.dom import minidom
from manifest import android_ns
@@ -43,11 +45,13 @@
'--uses-library',
dest='uses_libraries',
action='append',
+ default=[],
help='specify uses-library entries known to the build system')
parser.add_argument(
'--optional-uses-library',
dest='optional_uses_libraries',
action='append',
+ default=[],
help='specify uses-library entries known to the build system with '
'required:false'
)
@@ -74,9 +78,14 @@
help='print the targetSdkVersion from the manifest')
parser.add_argument(
'--dexpreopt-config',
- dest='dexpreopt_configs',
+ dest='dexpreopt_config',
+ help='a path to dexpreopt.config file for this library/app')
+ parser.add_argument(
+ '--dexpreopt-dep-config',
+ dest='dexpreopt_dep_configs',
action='append',
- help='a paths to a dexpreopt.config of some library')
+ default=[],
+ help='a path to dexpreopt.config file for a dependency library')
parser.add_argument('--aapt', dest='aapt', help='path to aapt executable')
parser.add_argument(
'--output', '-o', dest='output', help='output AndroidManifest.xml file')
@@ -295,25 +304,53 @@
return target_attr.value
-def load_dexpreopt_configs(configs):
+def remove_duplicates(l):
+ return list(OrderedDict.fromkeys(l))
+
+
+def load_dexpreopt_configs(args):
"""Load dexpreopt.config files and map module names to library names."""
module_to_libname = {}
- if configs is None:
- configs = []
+ # Go over dexpreopt.config files for uses-library dependencies and create
+ # a mapping from module name to real library name (they may differ).
+ for config in args.dexpreopt_dep_configs:
+ # Empty dexpreopt.config files are expected for some dependencies.
+ if os.stat(config).st_size != 0:
+ with open(config, 'r') as f:
+ contents = json.load(f)
+ module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
- for config in configs:
- with open(config, 'r') as f:
+ required = translate_libnames(args.uses_libraries, module_to_libname)
+ optional = translate_libnames(args.optional_uses_libraries, module_to_libname)
+
+ # Add extra uses-libraries from the library/app's own dexpreopt.config.
+ # Extra libraries may be propagated via dependencies' dexpreopt.config files
+ # (not only uses-library ones, but also transitively via static libraries).
+ if args.dexpreopt_config:
+ with open(args.dexpreopt_config, 'r') as f:
contents = json.load(f)
- module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
+ for clc in contents['ClassLoaderContexts']['any']:
+ ulib = clc['Name']
+ if clc['Optional']:
+ optional.append(ulib)
+ else:
+ required.append(ulib)
- return module_to_libname
+ required = remove_duplicates(required)
+ optional = remove_duplicates(optional)
+
+ # If the same library is both in optional and required, prefer required.
+ # This may happen for compatibility libraries, e.g. org.apache.http.legacy.
+ for lib in required:
+ if lib in optional:
+ optional.remove(lib)
+
+ return required, optional
def translate_libnames(modules, module_to_libname):
"""Translate module names into library names using the mapping."""
- if modules is None:
- modules = []
libnames = []
for name in modules:
@@ -346,10 +383,7 @@
# `optional_uses_libs`, `LOCAL_USES_LIBRARIES`,
# `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while
# the manifest addresses libraries by their name.
- mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
- required = translate_libnames(args.uses_libraries, mod_to_lib)
- optional = translate_libnames(args.optional_uses_libraries,
- mod_to_lib)
+ required, optional = load_dexpreopt_configs(args)
# Check if the <uses-library> lists in the build system agree with
# those in the manifest. Raise an exception on mismatch, unless the
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index d80a617..2d3103b 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -65,6 +65,9 @@
parser.add_argument('--has-no-code', dest='has_no_code', action='store_true',
help=('adds hasCode="false" attribute to application. Ignored if application elem '
'already has a hasCode attribute.'))
+ parser.add_argument('--test-only', dest='test_only', action='store_true',
+ help=('adds testOnly="true" attribute to application. Assign true value if application elem '
+ 'already has a testOnly attribute.'))
parser.add_argument('input', help='input AndroidManifest.xml file')
parser.add_argument('output', help='output AndroidManifest.xml file')
return parser.parse_args()
@@ -318,6 +321,26 @@
attr.value = 'false'
application.setAttributeNode(attr)
+def set_test_only_flag_to_true(doc):
+ manifest = parse_manifest(doc)
+ elems = get_children_with_tag(manifest, 'application')
+ application = elems[0] if len(elems) == 1 else None
+ if len(elems) > 1:
+ raise RuntimeError('found multiple <application> tags')
+ elif not elems:
+ application = doc.createElement('application')
+ indent = get_indent(manifest.firstChild, 1)
+ first = manifest.firstChild
+ manifest.insertBefore(doc.createTextNode(indent), first)
+ manifest.insertBefore(application, first)
+
+ attr = application.getAttributeNodeNS(android_ns, 'testOnly')
+ if attr is not None:
+ # Do nothing If the application already has a testOnly attribute.
+ return
+ attr = doc.createAttributeNS(android_ns, 'android:testOnly')
+ attr.value = 'true'
+ application.setAttributeNode(attr)
def main():
"""Program entry point."""
@@ -349,6 +372,9 @@
if args.has_no_code:
set_has_code_to_false(doc)
+ if args.test_only:
+ set_test_only_flag_to_true(doc)
+
if args.extract_native_libs is not None:
add_extract_native_libs(doc, args.extract_native_libs)
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index f6fcaaf..199b279 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -521,12 +521,55 @@
self.assert_xml_equal(output, manifest_input)
def test_has_application_has_code_true(self):
- """ Do nothing if there's already an application elemeent even if its
+ """ Do nothing if there's already an application element even if its
hasCode attribute is true. """
manifest_input = self.manifest_tmpl % ' <application android:hasCode="true"/>\n'
output = self.run_test(manifest_input)
self.assert_xml_equal(output, manifest_input)
+class AddTestOnlyApplicationTest(unittest.TestCase):
+ """Unit tests for set_test_only_flag_to_true function."""
+
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+ def run_test(self, input_manifest):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.set_test_only_flag_to_true(doc)
+ output = io.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ '%s'
+ '</manifest>\n')
+
+ def test_no_application(self):
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, expected)
+
+ def test_has_application_no_test_only(self):
+ manifest_input = self.manifest_tmpl % ' <application/>\n'
+ expected = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, expected)
+
+ def test_has_application_test_only_true(self):
+ """ If there's already an application element."""
+ manifest_input = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, manifest_input)
+
+ def test_has_application_test_only_false(self):
+ """ If there's already an application element with the testOnly attribute as false."""
+ manifest_input = self.manifest_tmpl % ' <application android:testOnly="false"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, manifest_input)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/mergenotice.py b/scripts/mergenotice.py
deleted file mode 100755
index fe99073..0000000
--- a/scripts/mergenotice.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""
-Merges input notice files to the output file while ignoring duplicated files
-This script shouldn't be confused with build/soong/scripts/generate-notice-files.py
-which is responsible for creating the final notice file for all artifacts
-installed. This script has rather limited scope; it is meant to create a merged
-notice file for a set of modules that are packaged together, e.g. in an APEX.
-The merged notice file does not reveal the individual files in the package.
-"""
-
-import sys
-import argparse
-
-def get_args():
- parser = argparse.ArgumentParser(description='Merge notice files.')
- parser.add_argument('--output', help='output file path.')
- parser.add_argument('inputs', metavar='INPUT', nargs='+',
- help='input notice file')
- return parser.parse_args()
-
-def main(argv):
- args = get_args()
-
- processed = set()
- with open(args.output, 'w+') as output:
- for input in args.inputs:
- with open(input, 'r') as f:
- data = f.read().strip()
- if data not in processed:
- processed.add(data)
- output.write('%s\n\n' % data)
-
-if __name__ == '__main__':
- main(sys.argv)
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index 5e702e0..192b38f 100644
--- a/scripts/microfactory.bash
+++ b/scripts/microfactory.bash
@@ -59,7 +59,7 @@
BUILDDIR=$(getoutdir) \
SRCDIR=${TOP} \
BLUEPRINTDIR=${TOP}/build/blueprint \
- EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path google.golang.org/protobuf=${TOP}/external/golang-protobuf" \
+ EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path rbcrun=${TOP}/build/make/tools/rbcrun -pkg-path google.golang.org/protobuf=${TOP}/external/golang-protobuf -pkg-path go.starlark.net=${TOP}/external/starlark-go" \
build_go $@
}
diff --git a/scripts/rbc-run b/scripts/rbc-run
index 7243421..8d93f0e 100755
--- a/scripts/rbc-run
+++ b/scripts/rbc-run
@@ -6,12 +6,13 @@
set -eu
declare -r output_root="${OUT_DIR:-out}"
-declare -r runner="${output_root}/soong/rbcrun"
-declare -r converter="${output_root}/soong/mk2rbc"
+declare -r runner="${output_root}/rbcrun"
+declare -r converter="${output_root}/mk2rbc"
declare -r launcher="${output_root}/rbc/launcher.rbc"
+declare -r makefile_list="${output_root}/.module_paths/configuration.list"
declare -r makefile="$1"
declare -r input_variables="$2"
shift 2
-"${converter}" -mode=write -r --outdir "${output_root}/rbc" --input_variables "${input_variables}" --launcher="${launcher}" "${makefile}"
+"${converter}" -mode=write -r --outdir "${output_root}/rbc" --input_variables "${input_variables}" --launcher="${launcher}" --makefile_list="${makefile_list}" "${makefile}"
"${runner}" RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ "${launcher}"
diff --git a/scripts/rustfmt.toml b/scripts/rustfmt.toml
index 617d425..cefaa42 100644
--- a/scripts/rustfmt.toml
+++ b/scripts/rustfmt.toml
@@ -1,5 +1,5 @@
# Android Format Style
-edition = "2018"
+edition = "2021"
use_small_heuristics = "Max"
newline_style = "Unix"
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index c150e8c..3dbc22e 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -28,6 +28,8 @@
from manifest import parse_test_config
from manifest import write_xml
+KNOWN_PREPARERS = ['com.android.tradefed.targetprep.TestAppInstallSetup',
+ 'com.android.tradefed.targetprep.suite.SuiteApkInstaller']
def parse_args():
"""Parse commandline arguments."""
@@ -64,7 +66,7 @@
tests = get_children_with_tag(test_config, 'target_preparer')
for test in tests:
- if test.getAttribute('class') == "com.android.tradefed.targetprep.TestAppInstallSetup":
+ if test.getAttribute('class') in KNOWN_PREPARERS:
options = get_children_with_tag(test, 'option')
for option in options:
if option.getAttribute('name') == "test-file-name":
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
index d00a593..39ce5b3 100644
--- a/scripts/test_config_fixer_test.py
+++ b/scripts/test_config_fixer_test.py
@@ -70,7 +70,7 @@
class OverwriteTestFileNameTest(unittest.TestCase):
""" Unit tests for overwrite_test_file_name function """
- test_config = (
+ test_config_test_app_install_setup = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<configuration description="Runs some tests.">\n'
' <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">\n'
@@ -82,15 +82,38 @@
' </test>\n'
'</configuration>\n')
- def test_all(self):
- doc = minidom.parseString(self.test_config % ("foo.apk"))
+ test_config_suite_apk_installer = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<configuration description="Runs some tests.">\n'
+ ' <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">\n'
+ ' <option name="test-file-name" value="%s"/>\n'
+ ' </target_preparer>\n'
+ ' <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+ ' <option name="package" value="com.android.foo"/>\n'
+ ' <option name="runtime-hint" value="20s"/>\n'
+ ' </test>\n'
+ '</configuration>\n')
+
+ def test_testappinstallsetup(self):
+ doc = minidom.parseString(self.test_config_test_app_install_setup % ("foo.apk"))
test_config_fixer.overwrite_test_file_name(doc, "bar.apk")
output = io.StringIO()
test_config_fixer.write_xml(output, doc)
# Only the matching package name in a test node should be updated.
- expected = self.test_config % ("bar.apk")
+ expected = self.test_config_test_app_install_setup % ("bar.apk")
+ self.assertEqual(expected, output.getvalue())
+
+ def test_suiteapkinstaller(self):
+ doc = minidom.parseString(self.test_config_suite_apk_installer % ("foo.apk"))
+
+ test_config_fixer.overwrite_test_file_name(doc, "bar.apk")
+ output = io.StringIO()
+ test_config_fixer.write_xml(output, doc)
+
+ # Only the matching package name in a test node should be updated.
+ expected = self.test_config_suite_apk_installer % ("bar.apk")
self.assertEqual(expected, output.getvalue())
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 2dacdb5..93ad172 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -101,6 +101,9 @@
image_name: "art",
contents: ["mybootlib"],
apex_available: ["com.android.art"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
apex_key {
@@ -124,7 +127,7 @@
preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
prebuilt_bootclasspath_fragment {
@@ -152,41 +155,6 @@
jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_bootclasspath_fragment {
- name: "mysdk_mybootclasspathfragment@current",
- sdk_member_name: "mybootclasspathfragment",
- visibility: ["//visibility:public"],
- apex_available: ["com.android.art"],
- image_name: "art",
- contents: ["mysdk_mybootlib@current"],
- hidden_api: {
- annotation_flags: "hiddenapi/annotation-flags.csv",
- metadata: "hiddenapi/metadata.csv",
- index: "hiddenapi/index.csv",
- signature_patterns: "hiddenapi/signature-patterns.csv",
- filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
- filtered_flags: "hiddenapi/filtered-flags.csv",
- },
-}
-
-java_import {
- name: "mysdk_mybootlib@current",
- sdk_member_name: "mybootlib",
- visibility: ["//visibility:public"],
- apex_available: ["com.android.art"],
- jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
- java_boot_libs: ["mysdk_mybootlib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
@@ -270,6 +238,9 @@
// This should be automatically added to the sdk_snapshot as a java_sdk_libs module.
stub_libs: ["mycoreplatform"],
},
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -317,7 +288,7 @@
preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
prebuilt_bootclasspath_fragment {
@@ -402,103 +373,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_bootclasspath_fragment {
- name: "mysdk_mybootclasspathfragment@current",
- sdk_member_name: "mybootclasspathfragment",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- contents: [
- "mysdk_mybootlib@current",
- "mysdk_myothersdklibrary@current",
- ],
- api: {
- stub_libs: ["mysdk_mysdklibrary@current"],
- },
- core_platform_api: {
- stub_libs: ["mysdk_mycoreplatform@current"],
- },
- hidden_api: {
- annotation_flags: "hiddenapi/annotation-flags.csv",
- metadata: "hiddenapi/metadata.csv",
- index: "hiddenapi/index.csv",
- signature_patterns: "hiddenapi/signature-patterns.csv",
- filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
- filtered_flags: "hiddenapi/filtered-flags.csv",
- },
-}
-
-java_import {
- name: "mysdk_mybootlib@current",
- sdk_member_name: "mybootlib",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
- permitted_packages: ["mybootlib"],
-}
-
-java_sdk_library_import {
- name: "mysdk_myothersdklibrary@current",
- sdk_member_name: "myothersdklibrary",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- shared_library: true,
- compile_dex: true,
- permitted_packages: ["myothersdklibrary"],
- public: {
- jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
- stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
- current_api: "sdk_library/public/myothersdklibrary.txt",
- removed_api: "sdk_library/public/myothersdklibrary-removed.txt",
- sdk_version: "current",
- },
-}
-
-java_sdk_library_import {
- name: "mysdk_mysdklibrary@current",
- sdk_member_name: "mysdklibrary",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- shared_library: false,
- public: {
- jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
- stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
- current_api: "sdk_library/public/mysdklibrary.txt",
- removed_api: "sdk_library/public/mysdklibrary-removed.txt",
- sdk_version: "current",
- },
-}
-
-java_sdk_library_import {
- name: "mysdk_mycoreplatform@current",
- sdk_member_name: "mycoreplatform",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- shared_library: true,
- compile_dex: true,
- public: {
- jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
- stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
- current_api: "sdk_library/public/mycoreplatform.txt",
- removed_api: "sdk_library/public/mycoreplatform-removed.txt",
- sdk_version: "current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
- java_boot_libs: ["mysdk_mybootlib@current"],
- java_sdk_libs: [
- "mysdk_myothersdklibrary@current",
- "mysdk_mysdklibrary@current",
- "mysdk_mycoreplatform@current",
- ],
-}
- `),
checkAllCopyRules(`
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
@@ -585,6 +459,9 @@
contents: [
"myotherlib",
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -614,6 +491,9 @@
module: "myotherbootclasspathfragment"
},
],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_sdk_library {
@@ -630,7 +510,7 @@
preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
prebuilt_bootclasspath_fragment {
@@ -693,6 +573,9 @@
image_name: "art",
contents: ["mybootlib"],
apex_available: ["myapex"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_library {
@@ -800,6 +683,7 @@
unsupported_packages: [
"my-unsupported-packages.txt",
],
+ split_packages: ["*"],
},
}
@@ -828,7 +712,7 @@
preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
prebuilt_bootclasspath_fragment {
diff --git a/sdk/bp.go b/sdk/bp.go
index e2dace8..7ff85a1 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -298,15 +298,15 @@
return module
}
-func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t identityTransformation) transformPropertySetBeforeContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
return propertySet, tag
}
-func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t identityTransformation) transformPropertySetAfterContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
return propertySet, tag
}
-func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t identityTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
return value, tag
}
@@ -332,7 +332,7 @@
return &moduleCopy
}
-func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t deepCopyTransformation) transformPropertySetBeforeContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
// Create a shallow copy of the properties map. Any mutable property values will be copied by the
// transformer.
propertiesCopy := make(map[string]interface{})
@@ -354,7 +354,7 @@
}, tag
}
-func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t deepCopyTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
// Copy string slice, otherwise return value.
if values, ok := value.([]string); ok {
valuesCopy := make([]string, len(values))
@@ -372,7 +372,7 @@
order []*bpModule
}
-// Add a module.
+// AddModule adds a module to this.
//
// The module must have had its "name" property set to a string value that
// is unique within this file.
diff --git a/sdk/build_release.go b/sdk/build_release.go
index a3f0899..4c2277e 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -85,7 +85,7 @@
// Add the build releases from oldest to newest.
buildReleaseS = initBuildRelease("S")
- buildReleaseT = initBuildRelease("T")
+ buildReleaseT = initBuildRelease("Tiramisu")
)
// initBuildRelease creates a new build release with the specified name.
@@ -230,51 +230,108 @@
return container.Field(fieldIndex)
}
- zeroValue := reflect.Zero(field.Type)
- fieldPruner := func(container reflect.Value) {
- if containingStructAccessor != nil {
- // This is an embedded structure so first access the field for the embedded
- // structure.
- container = containingStructAccessor(container)
+ fieldType := field.Type
+ if selector(name, field) {
+ zeroValue := reflect.Zero(fieldType)
+ fieldPruner := func(container reflect.Value) {
+ if containingStructAccessor != nil {
+ // This is an embedded structure so first access the field for the embedded
+ // structure.
+ container = containingStructAccessor(container)
+ }
+
+ // Skip through interface and pointer values to find the structure.
+ container = getStructValue(container)
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s\n\tfor field (index %d, name %s)", r, fieldIndex, name))
+ }
+ }()
+
+ // Set the field.
+ container.Field(fieldIndex).Set(zeroValue)
}
- // Skip through interface and pointer values to find the structure.
- container = getStructValue(container)
-
- defer func() {
- if r := recover(); r != nil {
- panic(fmt.Errorf("%s for fieldIndex %d of field %s of container %#v", r, fieldIndex, name, container.Interface()))
- }
- }()
-
- // Set the field.
- container.Field(fieldIndex).Set(zeroValue)
- }
-
- if selector(name, field) {
property := prunerProperty{
name,
fieldPruner,
}
p.properties = append(p.properties, property)
- } else if field.Type.Kind() == reflect.Struct {
- // Gather fields from the nested or embedded structure.
- var subNamePrefix string
- if field.Anonymous {
- subNamePrefix = namePrefix
- } else {
- subNamePrefix = name + "."
+ } else {
+ switch fieldType.Kind() {
+ case reflect.Struct:
+ // Gather fields from the nested or embedded structure.
+ var subNamePrefix string
+ if field.Anonymous {
+ subNamePrefix = namePrefix
+ } else {
+ subNamePrefix = name + "."
+ }
+ p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
+
+ case reflect.Map:
+ // Get the type of the values stored in the map.
+ valueType := fieldType.Elem()
+ // Skip over * types.
+ if valueType.Kind() == reflect.Ptr {
+ valueType = valueType.Elem()
+ }
+ if valueType.Kind() == reflect.Struct {
+ // If this is not referenced by a pointer then it is an error as it is impossible to
+ // modify a struct that is stored directly as a value in a map.
+ if fieldType.Elem().Kind() != reflect.Ptr {
+ panic(fmt.Errorf("Cannot prune struct %s stored by value in map %s, map values must"+
+ " be pointers to structs",
+ fieldType.Elem(), name))
+ }
+
+ // Create a new pruner for the values of the map.
+ valuePruner := newPropertyPrunerForStructType(valueType, selector)
+
+ // Create a new fieldPruner that will iterate over all the items in the map and call the
+ // pruner on them.
+ fieldPruner := func(container reflect.Value) {
+ mapValue := fieldGetter(container)
+
+ for _, keyValue := range mapValue.MapKeys() {
+ itemValue := mapValue.MapIndex(keyValue)
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s\n\tfor key %q", r, keyValue))
+ }
+ }()
+
+ valuePruner.pruneProperties(itemValue.Interface())
+ }
+ }
+
+ // Add the map field pruner to the list of property pruners.
+ property := prunerProperty{
+ name + "[*]",
+ fieldPruner,
+ }
+ p.properties = append(p.properties, property)
+ }
}
- p.gatherFields(field.Type, fieldGetter, subNamePrefix, selector)
}
}
}
-// pruneProperties will prune (set to zero value) any properties in the supplied struct.
+// pruneProperties will prune (set to zero value) any properties in the struct referenced by the
+// supplied struct pointer.
//
// The struct must be of the same type as was originally passed to newPropertyPruner to create this
// propertyPruner.
func (p *propertyPruner) pruneProperties(propertiesStruct interface{}) {
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s\n\tof container %#v", r, propertiesStruct))
+ }
+ }()
+
structValue := reflect.ValueOf(propertiesStruct)
for _, property := range p.properties {
property.prunerFunc(structValue)
@@ -292,6 +349,13 @@
// of properties.
func newPropertyPruner(propertiesStruct interface{}, selector fieldSelectorFunc) *propertyPruner {
structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
+ return newPropertyPrunerForStructType(structType, selector)
+}
+
+// newPropertyPruner creates a new property pruner for the supplied properties struct type.
+//
+// The returned pruner can be used on any properties structure of the supplied type.
+func newPropertyPrunerForStructType(structType reflect.Type, selector fieldSelectorFunc) *propertyPruner {
pruner := &propertyPruner{}
pruner.gatherFields(structType, nil, "", selector)
return pruner
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index dff276d..6f1ef9e 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -15,6 +15,7 @@
package sdk
import (
+ "encoding/json"
"fmt"
"testing"
@@ -59,7 +60,7 @@
t.Run("closed range", func(t *testing.T) {
set, err := parseBuildReleaseSet("S-F1")
android.AssertDeepEquals(t, "errors", nil, err)
- android.AssertStringEquals(t, "set", "[S,T,F1]", set.String())
+ android.AssertStringEquals(t, "set", "[S,Tiramisu,F1]", set.String())
})
invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
t.Run("invalid release", func(t *testing.T) {
@@ -78,7 +79,7 @@
android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
})
t.Run("invalid release in closed range end", func(t *testing.T) {
- set, err := parseBuildReleaseSet("T-A")
+ set, err := parseBuildReleaseSet("Tiramisu-A")
android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set)
android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
})
@@ -125,61 +126,102 @@
F1_only string `supported_build_releases:"F1"`
}
- type testBuildReleasePruner struct {
- Default string
- S_and_T_only string `supported_build_releases:"S-T"`
- T_later string `supported_build_releases:"T+"`
- Nested nested
+ type mapped struct {
+ Default string
+ T_only string `supported_build_releases:"Tiramisu"`
}
- input := testBuildReleasePruner{
- Default: "Default",
- S_and_T_only: "S_and_T_only",
- T_later: "T_later",
- Nested: nested{
- F1_only: "F1_only",
- },
+ type testBuildReleasePruner struct {
+ Default string
+ S_and_T_only string `supported_build_releases:"S-Tiramisu"`
+ T_later string `supported_build_releases:"Tiramisu+"`
+ Nested nested
+ Mapped map[string]*mapped
+ }
+
+ inputFactory := func() testBuildReleasePruner {
+ return testBuildReleasePruner{
+ Default: "Default",
+ S_and_T_only: "S_and_T_only",
+ T_later: "T_later",
+ Nested: nested{
+ F1_only: "F1_only",
+ },
+ Mapped: map[string]*mapped{
+ "one": {
+ Default: "one-default",
+ T_only: "one-t-only",
+ },
+ "two": {
+ Default: "two-default",
+ T_only: "two-t-only",
+ },
+ },
+ }
+ }
+
+ marshal := func(t interface{}) string {
+ bytes, err := json.MarshalIndent(t, "", " ")
+ if err != nil {
+ panic(err)
+ }
+ return string(bytes)
+ }
+
+ assertJsonEquals := func(t *testing.T, expected, actual interface{}) {
+ t.Helper()
+ expectedJson := marshal(expected)
+ actualJson := marshal(actual)
+ if actualJson != expectedJson {
+ t.Errorf("test struct: expected:\n%s\n got:\n%s", expectedJson, actualJson)
+ }
}
t.Run("target S", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseS)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.T_later = ""
expected.Nested.F1_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ expected.Mapped["one"].T_only = ""
+ expected.Mapped["two"].T_only = ""
+ assertJsonEquals(t, expected, testStruct)
})
t.Run("target T", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseT)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.Nested.F1_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ assertJsonEquals(t, expected, testStruct)
})
t.Run("target F1", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture1)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.S_and_T_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ expected.Mapped["one"].T_only = ""
+ expected.Mapped["two"].T_only = ""
+ assertJsonEquals(t, expected, testStruct)
})
t.Run("target F2", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture2)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.S_and_T_only = ""
expected.Nested.F1_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ expected.Mapped["one"].T_only = ""
+ expected.Mapped["two"].T_only = ""
+ assertJsonEquals(t, expected, testStruct)
})
}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index cd63dac..265579a 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -120,7 +120,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -145,162 +145,12 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
- name: "mysdk_sdkmember@current",
- sdk_member_name: "sdkmember",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- installable: false,
- stl: "none",
- compile_multilib: "64",
- target: {
- host: {
- enabled: false,
- },
- android_arm64: {
- srcs: ["android/arm64/lib/sdkmember.so"],
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- compile_multilib: "64",
- native_shared_libs: ["mysdk_sdkmember@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/sdkmember/android_arm64_armv8-a_shared/sdkmember.so -> android/arm64/lib/sdkmember.so
.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> linux_glibc/x86_64/lib/sdkmember.so
`))
}
-func TestBasicSdkWithCc(t *testing.T) {
- result := testSdkWithCc(t, `
- sdk {
- name: "mysdk",
- native_shared_libs: ["sdkmember"],
- }
-
- cc_library_shared {
- name: "sdkmember",
- system_shared_libs: [],
- stl: "none",
- apex_available: ["mysdkapex"],
- }
-
- sdk_snapshot {
- name: "mysdk@1",
- native_shared_libs: ["sdkmember_mysdk@1"],
- }
-
- sdk_snapshot {
- name: "mysdk@2",
- native_shared_libs: ["sdkmember_mysdk@2"],
- }
-
- cc_prebuilt_library_shared {
- name: "sdkmember",
- srcs: ["libfoo.so"],
- prefer: false,
- system_shared_libs: [],
- stl: "none",
- }
-
- cc_prebuilt_library_shared {
- name: "sdkmember_mysdk@1",
- sdk_member_name: "sdkmember",
- srcs: ["libfoo.so"],
- system_shared_libs: [],
- stl: "none",
- // TODO: remove //apex_available:platform
- apex_available: [
- "//apex_available:platform",
- "myapex",
- ],
- }
-
- cc_prebuilt_library_shared {
- name: "sdkmember_mysdk@2",
- sdk_member_name: "sdkmember",
- srcs: ["libfoo.so"],
- system_shared_libs: [],
- stl: "none",
- // TODO: remove //apex_available:platform
- apex_available: [
- "//apex_available:platform",
- "myapex2",
- ],
- }
-
- cc_library_shared {
- name: "mycpplib",
- srcs: ["Test.cpp"],
- shared_libs: ["sdkmember"],
- system_shared_libs: [],
- stl: "none",
- apex_available: [
- "myapex",
- "myapex2",
- ],
- }
-
- apex {
- name: "myapex",
- native_shared_libs: ["mycpplib"],
- uses_sdks: ["mysdk@1"],
- key: "myapex.key",
- certificate: ":myapex.cert",
- updatable: false,
- }
-
- apex {
- name: "myapex2",
- native_shared_libs: ["mycpplib"],
- uses_sdks: ["mysdk@2"],
- key: "myapex.key",
- certificate: ":myapex.cert",
- updatable: false,
- }
-
- apex {
- name: "mysdkapex",
- native_shared_libs: ["sdkmember"],
- key: "myapex.key",
- certificate: ":myapex.cert",
- updatable: false,
- }
- `)
-
- sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk@1", "android_arm64_armv8-a_shared_apex10000_mysdk_1").Rule("toc").Output
- sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk@2", "android_arm64_armv8-a_shared_apex10000_mysdk_2").Rule("toc").Output
-
- cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_apex10000_mysdk_1")
- cpplibForMyApex2 := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_apex10000_mysdk_2")
-
- // Depending on the uses_sdks value, different libs are linked
- ensureListContains(t, pathsToStrings(cpplibForMyApex.Rule("ld").Implicits), sdkMemberV1.String())
- ensureListContains(t, pathsToStrings(cpplibForMyApex2.Rule("ld").Implicits), sdkMemberV2.String())
-}
-
// Make sure the sdk can use host specific cc libraries static/shared and both.
func TestHostSdkWithCc(t *testing.T) {
testSdkWithCc(t, `
@@ -373,7 +223,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_object {
@@ -397,37 +247,6 @@
},
}
`),
- // Make sure that the generated sdk_snapshot uses the native_objects property.
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_object {
- name: "mysdk_crtobj@current",
- sdk_member_name: "crtobj",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- stl: "none",
- compile_multilib: "both",
- system_shared_libs: [],
- sanitize: {
- never: true,
- },
- arch: {
- arm64: {
- srcs: ["arm64/lib/crtobj.o"],
- },
- arm: {
- srcs: ["arm/lib/crtobj.o"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_objects: ["mysdk_crtobj@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/crtobj/android_arm64_armv8-a/crtobj.o -> arm64/lib/crtobj.o
.intermediates/crtobj/android_arm_armv7-a-neon/crtobj.o -> arm/lib/crtobj.o
@@ -511,7 +330,7 @@
errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -584,7 +403,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -643,7 +462,7 @@
`)
CheckSnapshot(t, result, "mymodule_exports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
@@ -662,33 +481,6 @@
},
}
`),
- // Make sure that the generated sdk_snapshot uses the native_binaries property.
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
- name: "mymodule_exports_mynativebinary@current",
- sdk_member_name: "mynativebinary",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- compile_multilib: "both",
- arch: {
- arm64: {
- srcs: ["arm64/bin/mynativebinary"],
- },
- arm: {
- srcs: ["arm/bin/mynativebinary"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "mymodule_exports@current",
- visibility: ["//visibility:public"],
- native_binaries: ["mymodule_exports_mynativebinary@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/mynativebinary/android_arm64_armv8-a/mynativebinary -> arm64/bin/mynativebinary
.intermediates/mynativebinary/android_arm_armv7-a-neon/mynativebinary -> arm/bin/mynativebinary
@@ -728,7 +520,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
@@ -764,68 +556,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
- name: "myexports_mynativebinary@current",
- sdk_member_name: "mynativebinary",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- target: {
- host: {
- enabled: false,
- },
- linux_glibc: {
- compile_multilib: "both",
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["linux_glibc/x86/bin/mynativebinary"],
- },
- windows: {
- compile_multilib: "64",
- },
- windows_x86_64: {
- enabled: true,
- srcs: ["windows/x86_64/bin/mynativebinary.exe"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- native_binaries: ["myexports_mynativebinary@current"],
- target: {
- windows: {
- compile_multilib: "64",
- },
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- windows_x86_64: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/mynativebinary/linux_glibc_x86_64/mynativebinary -> linux_glibc/x86_64/bin/mynativebinary
.intermediates/mynativebinary/linux_glibc_x86/mynativebinary -> linux_glibc/x86/bin/mynativebinary
@@ -888,7 +618,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
@@ -931,69 +661,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
- name: "myexports_mynativebinary@current",
- sdk_member_name: "mynativebinary",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- compile_multilib: "64",
- target: {
- host: {
- enabled: false,
- },
- linux_bionic_x86_64: {
- enabled: true,
- srcs: ["x86_64/bin/mynativebinary"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
- name: "myexports_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- compile_multilib: "64",
- target: {
- host: {
- enabled: false,
- },
- linux_bionic_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.so"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- compile_multilib: "64",
- native_binaries: ["myexports_mynativebinary@current"],
- native_shared_libs: ["myexports_mynativelib@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_bionic_x86_64: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/mynativebinary/linux_bionic_x86_64/mynativebinary -> x86_64/bin/mynativebinary
.intermediates/mynativelib/linux_bionic_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
@@ -1026,7 +693,7 @@
`)
CheckSnapshot(t, result, "mymodule_exports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
@@ -1055,55 +722,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
- name: "mymodule_exports_linker@current",
- sdk_member_name: "linker",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- compile_multilib: "both",
- static_executable: true,
- nocrt: true,
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/bin/linker"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["x86/bin/linker"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "mymodule_exports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- native_binaries: ["mymodule_exports_linker@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/linker/linux_glibc_x86_64/linker -> x86_64/bin/linker
.intermediates/linker/linux_glibc_x86/linker -> x86/bin/linker
@@ -1134,7 +752,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -1235,7 +853,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -1332,7 +950,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -1363,57 +981,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- sdk_version: "minimum",
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/myinclude"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.so"],
- export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["x86/lib/mynativelib.so"],
- export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- native_shared_libs: ["mysdk_mynativelib@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
@@ -1459,7 +1026,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -1495,68 +1062,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- target: {
- host: {
- enabled: false,
- },
- linux_glibc: {
- compile_multilib: "both",
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["linux_glibc/x86/lib/mynativelib.so"],
- },
- windows: {
- compile_multilib: "64",
- },
- windows_x86_64: {
- enabled: true,
- srcs: ["windows/x86_64/lib/mynativelib.dll"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- native_shared_libs: ["mysdk_mynativelib@current"],
- target: {
- windows: {
- compile_multilib: "64",
- },
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- windows_x86_64: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
@@ -1587,7 +1092,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_static {
@@ -1650,7 +1155,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_static {
@@ -1680,56 +1185,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_static {
- name: "myexports_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/myinclude"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.a"],
- export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["x86/lib/mynativelib.a"],
- export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- native_static_libs: ["myexports_mynativelib@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
@@ -1764,7 +1219,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library {
@@ -1796,46 +1251,6 @@
},
}
`),
- // Make sure that the generated sdk_snapshot uses the native_libs property.
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library {
- name: "myexports_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- vendor_available: true,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/myinclude"],
- arch: {
- arm64: {
- static: {
- srcs: ["arm64/lib/mynativelib.a"],
- },
- shared: {
- srcs: ["arm64/lib/mynativelib.so"],
- },
- },
- arm: {
- static: {
- srcs: ["arm/lib/mynativelib.a"],
- },
- shared: {
- srcs: ["arm/lib/mynativelib.so"],
- },
- },
- },
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- native_libs: ["myexports_mynativelib@current"],
-}
-`),
checkAllCopyRules(`
myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
@@ -1848,6 +1263,229 @@
)
}
+func TestSnapshotSameLibraryWithNativeLibsAndNativeSharedLib(t *testing.T) {
+ result := testSdkWithCc(t, `
+ module_exports {
+ host_supported: true,
+ name: "myexports",
+ target: {
+ android: {
+ native_shared_libs: [
+ "mynativelib",
+ ],
+ },
+ not_windows: {
+ native_libs: [
+ "mynativelib",
+ ],
+ },
+ },
+ }
+
+ cc_library {
+ name: "mynativelib",
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ ],
+ stl: "none",
+ recovery_available: true,
+ vendor_available: true,
+ }
+ `)
+
+ CheckSnapshot(t, result, "myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ host_supported: true,
+ vendor_available: true,
+ stl: "none",
+ compile_multilib: "both",
+ target: {
+ host: {
+ enabled: false,
+ },
+ android_arm64: {
+ shared: {
+ srcs: ["android/arm64/lib/mynativelib.so"],
+ },
+ static: {
+ enabled: false,
+ },
+ },
+ android_arm: {
+ shared: {
+ srcs: ["android/arm/lib/mynativelib.so"],
+ },
+ static: {
+ enabled: false,
+ },
+ },
+ linux_glibc_x86_64: {
+ enabled: true,
+ static: {
+ srcs: ["linux_glibc/x86_64/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+ },
+ },
+ linux_glibc_x86: {
+ enabled: true,
+ static: {
+ srcs: ["linux_glibc/x86/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+ },
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> android/arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> android/arm/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> linux_glibc/x86_64/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> linux_glibc/x86/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+`),
+ )
+}
+
+func TestSnapshotSameLibraryWithAndroidNativeLibsAndHostNativeSharedLib(t *testing.T) {
+ result := testSdkWithCc(t, `
+ module_exports {
+ host_supported: true,
+ name: "myexports",
+ target: {
+ android: {
+ native_libs: [
+ "mynativelib",
+ ],
+ },
+ not_windows: {
+ native_shared_libs: [
+ "mynativelib",
+ ],
+ },
+ },
+ }
+
+ cc_library {
+ name: "mynativelib",
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ ],
+ stl: "none",
+ recovery_available: true,
+ vendor_available: true,
+ }
+ `)
+
+ CheckSnapshot(t, result, "myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ host_supported: true,
+ vendor_available: true,
+ stl: "none",
+ compile_multilib: "both",
+ target: {
+ host: {
+ enabled: false,
+ },
+ android_arm64: {
+ static: {
+ srcs: ["android/arm64/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["android/arm64/lib/mynativelib.so"],
+ },
+ },
+ android_arm: {
+ static: {
+ srcs: ["android/arm/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["android/arm/lib/mynativelib.so"],
+ },
+ },
+ linux_glibc_x86_64: {
+ enabled: true,
+ shared: {
+ srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+ },
+ static: {
+ enabled: false,
+ },
+ },
+ linux_glibc_x86: {
+ enabled: true,
+ shared: {
+ srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+ },
+ static: {
+ enabled: false,
+ },
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> android/arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> android/arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> android/arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> android/arm/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+`),
+ )
+}
+
+func TestSnapshotSameLibraryWithNativeStaticLibsAndNativeSharedLib(t *testing.T) {
+ testSdkError(t, "Incompatible member types", `
+ module_exports {
+ host_supported: true,
+ name: "myexports",
+ target: {
+ android: {
+ native_shared_libs: [
+ "mynativelib",
+ ],
+ },
+ not_windows: {
+ native_static_libs: [
+ "mynativelib",
+ ],
+ },
+ },
+ }
+
+ cc_library {
+ name: "mynativelib",
+ host_supported: true,
+ srcs: [
+ ],
+ stl: "none",
+ recovery_available: true,
+ vendor_available: true,
+ }
+ `)
+}
+
func TestHostSnapshotWithMultiLib64(t *testing.T) {
result := testSdkWithCc(t, `
module_exports {
@@ -1879,7 +1517,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_static {
@@ -1906,51 +1544,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_static {
- name: "myexports_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- installable: false,
- stl: "none",
- compile_multilib: "64",
- export_include_dirs: [
- "include/myinclude",
- "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
- ],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.a"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- compile_multilib: "64",
- native_static_libs: ["myexports_mynativelib@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
@@ -1976,7 +1569,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
@@ -2020,7 +1613,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
@@ -2105,7 +1698,7 @@
`, trait, property))
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(fmt.Sprintf(`
+ checkAndroidBpContents(fmt.Sprintf(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
@@ -2154,7 +1747,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
@@ -2180,51 +1773,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_headers {
- name: "mysdk_mynativeheaders@current",
- sdk_member_name: "mynativeheaders",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/myinclude"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- native_header_libs: ["mysdk_mynativeheaders@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
myinclude/Test.h -> include/myinclude/Test.h
`),
@@ -2256,7 +1804,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
@@ -2287,55 +1835,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_headers {
- name: "mysdk_mynativeheaders@current",
- sdk_member_name: "mynativeheaders",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- stl: "none",
- compile_multilib: "both",
- export_system_include_dirs: ["common_os/include/myinclude"],
- target: {
- host: {
- enabled: false,
- },
- android: {
- export_include_dirs: ["android/include/myinclude-android"],
- },
- linux_glibc: {
- export_include_dirs: ["linux_glibc/include/myinclude-host"],
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- native_header_libs: ["mysdk_mynativeheaders@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
myinclude/Test.h -> common_os/include/myinclude/Test.h
myinclude-android/AndroidTest.h -> android/include/myinclude-android/AndroidTest.h
@@ -2368,7 +1867,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -2441,7 +1940,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -2475,59 +1974,7 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
- name: "mysdk_sslvariants@current",
- sdk_member_name: "sslvariants",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- installable: false,
- compile_multilib: "both",
- target: {
- host: {
- enabled: false,
- },
- android: {
- system_shared_libs: [],
- },
- android_arm64: {
- srcs: ["android/arm64/lib/sslvariants.so"],
- },
- android_arm: {
- srcs: ["android/arm/lib/sslvariants.so"],
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["linux_glibc/x86/lib/sslvariants.so"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- native_shared_libs: ["mysdk_sslvariants@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`))
+ )
}
func TestStubsLibrary(t *testing.T) {
@@ -2552,7 +1999,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -2606,7 +2053,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -2645,64 +2092,7 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
- name: "mysdk_stubslib@current",
- sdk_member_name: "stubslib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- installable: false,
- compile_multilib: "both",
- stubs: {
- versions: [
- "1",
- "2",
- "3",
- "current",
- ],
- },
- target: {
- host: {
- enabled: false,
- },
- android_arm64: {
- srcs: ["android/arm64/lib/stubslib.so"],
- },
- android_arm: {
- srcs: ["android/arm/lib/stubslib.so"],
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["linux_glibc/x86/lib/stubslib.so"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- native_shared_libs: ["mysdk_stubslib@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`))
+ )
}
func TestUniqueHostSoname(t *testing.T) {
@@ -2721,7 +2111,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -2753,57 +2143,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
- name: "mysdk_mylib@current",
- sdk_member_name: "mylib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- installable: false,
- unique_host_soname: true,
- compile_multilib: "both",
- target: {
- host: {
- enabled: false,
- },
- android_arm64: {
- srcs: ["android/arm64/lib/mylib.so"],
- },
- android_arm: {
- srcs: ["android/arm/lib/mylib.so"],
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["linux_glibc/x86/lib/mylib-host.so"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- native_shared_libs: ["mysdk_mylib@current"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/mylib/android_arm64_armv8-a_shared/mylib.so -> android/arm64/lib/mylib.so
.intermediates/mylib/android_arm_armv7-a-neon_shared/mylib.so -> android/arm/lib/mylib.so
@@ -2835,13 +2174,8 @@
}
`)
- // Mixing the snapshot with the source (irrespective of which one is preferred) causes a problem
- // due to missing variants.
- // TODO(b/183204176): Remove this and fix the cause.
- snapshotWithSourceErrorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\QReplaceDependencies could not find identical variant {os:android,image:,arch:arm64_armv8-a,sdk:,link:shared,version:} for module mynativelib\E`)
-
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -2866,7 +2200,5 @@
arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
`),
- snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, snapshotWithSourceErrorHandler),
- snapshotTestErrorHandler(checkSnapshotPreferredWithSource, snapshotWithSourceErrorHandler),
)
}
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index 00073c2..d166add 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -37,23 +37,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_platform_compat_config {
- name: "mysdk_myconfig@current",
- sdk_member_name: "myconfig",
- visibility: ["//visibility:public"],
- metadata: "compat_configs/myconfig/myconfig_meta.xml",
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- compat_configs: ["mysdk_myconfig@current"],
-}
-`),
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
prebuilt_platform_compat_config {
diff --git a/sdk/exports.go b/sdk/exports.go
index 9a0ba4e..7645d3f 100644
--- a/sdk/exports.go
+++ b/sdk/exports.go
@@ -31,7 +31,7 @@
return newSdkModule(true)
}
-// module_exports_snapshot is a versioned snapshot of prebuilt versions of all the exports
+// module_exports_snapshot is a snapshot of prebuilt versions of all the exports
// of a mainline module.
func ModuleExportsSnapshotsFactory() android.Module {
s := newSdkModule(true)
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 17ddf17..2605fd1 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -43,7 +43,7 @@
})
CheckSnapshot(t, result, "myexports", "package",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -54,22 +54,5 @@
jars: ["java/myjavalib.jar"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- java_libs: ["myexports_myjavalib@current"],
-}
-`),
)
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0d9b4a0..d25138f 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -71,90 +71,6 @@
)
}
-func TestBasicSdkWithJavaLibrary(t *testing.T) {
- result := android.GroupFixturePreparers(
- prepareForSdkTestWithJava,
- prepareForSdkTestWithApex,
- ).RunTestWithBp(t, `
- sdk {
- name: "mysdk",
- java_header_libs: ["sdkmember"],
- }
-
- sdk_snapshot {
- name: "mysdk@1",
- java_header_libs: ["sdkmember_mysdk@1"],
- }
-
- sdk_snapshot {
- name: "mysdk@2",
- java_header_libs: ["sdkmember_mysdk@2"],
- }
-
- java_library {
- name: "sdkmember",
- srcs: ["Test.java"],
- system_modules: "none",
- sdk_version: "none",
- host_supported: true,
- }
-
- java_import {
- name: "sdkmember_mysdk@1",
- sdk_member_name: "sdkmember",
- host_supported: true,
- }
-
- java_import {
- name: "sdkmember_mysdk@2",
- sdk_member_name: "sdkmember",
- host_supported: true,
- }
-
- java_library {
- name: "myjavalib",
- srcs: ["Test.java"],
- libs: ["sdkmember"],
- system_modules: "none",
- sdk_version: "none",
- compile_dex: true,
- host_supported: true,
- apex_available: [
- "myapex",
- "myapex2",
- ],
- }
-
- apex {
- name: "myapex",
- java_libs: ["myjavalib"],
- uses_sdks: ["mysdk@1"],
- key: "myapex.key",
- certificate: ":myapex.cert",
- updatable: false,
- }
-
- apex {
- name: "myapex2",
- java_libs: ["myjavalib"],
- uses_sdks: ["mysdk@2"],
- key: "myapex.key",
- certificate: ":myapex.cert",
- updatable: false,
- }
- `)
-
- sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk@1", "android_common").Rule("combineJar").Output
- sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk@2", "android_common").Rule("combineJar").Output
-
- javalibForMyApex := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
- javalibForMyApex2 := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
-
- // Depending on the uses_sdks value, different libs are linked
- ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
- ensureListContains(t, pathsToStrings(javalibForMyApex2.Rule("javac").Implicits), sdkMemberV2.String())
-}
-
func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForSdkTestWithJava,
@@ -180,7 +96,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -192,24 +108,6 @@
permitted_packages: ["pkg.myjavalib"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
- permitted_packages: ["pkg.myjavalib"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -244,7 +142,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -257,27 +155,6 @@
jars: ["java/myjavalib.jar"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -304,7 +181,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -323,32 +200,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- target: {
- android: {
- jars: ["java/android/myjavalib.jar"],
- },
- linux_glibc: {
- jars: ["java/linux_glibc/myjavalib.jar"],
- },
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
@@ -382,7 +233,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -393,23 +244,6 @@
jars: ["java/myjavalib.jar"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- java_libs: ["myexports_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -445,7 +279,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -457,25 +291,6 @@
permitted_packages: ["pkg.myjavalib"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
- permitted_packages: ["pkg.myjavalib"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- java_boot_libs: ["myexports_myjavalib@current"],
-}
-
-`),
checkAllCopyRules(`
.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
`),
@@ -511,7 +326,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -523,24 +338,6 @@
permitted_packages: ["pkg.myjavalib"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar"],
- permitted_packages: ["pkg.myjavalib"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- java_systemserver_libs: ["myexports_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myexports/common_os/empty -> java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar
`),
@@ -574,7 +371,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -587,27 +384,6 @@
jars: ["java/myjavalib.jar"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- java_libs: ["myexports_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -633,7 +409,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_test_import {
@@ -645,24 +421,6 @@
test_config: "java/myjavatests-AndroidTest.xml",
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_test_import {
- name: "myexports_myjavatests@current",
- sdk_member_name: "myjavatests",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavatests.jar"],
- test_config: "java/myjavatests-AndroidTest.xml",
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- java_tests: ["myexports_myjavatests@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavatests/android_common/javac/myjavatests.jar -> java/myjavatests.jar
.intermediates/myjavatests/android_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
@@ -691,7 +449,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_test_import {
@@ -705,28 +463,6 @@
test_config: "java/myjavatests-AndroidTest.xml",
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_test_import {
- name: "myexports_myjavatests@current",
- sdk_member_name: "myjavatests",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- jars: ["java/myjavatests.jar"],
- test_config: "java/myjavatests-AndroidTest.xml",
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- java_tests: ["myexports_myjavatests@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavatests/linux_glibc_common/javac/myjavatests.jar -> java/myjavatests.jar
.intermediates/myjavatests/linux_glibc_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
@@ -735,7 +471,19 @@
}
func TestSnapshotWithJavaSystemModules(t *testing.T) {
- result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithPrebuiltApisAndExtensions(map[string][]string{
+ "31": {"myjavalib"},
+ "32": {"myjavalib"},
+ "current": {"myjavalib"},
+ }, map[string][]string{
+ "1": {"myjavalib"},
+ "2": {"myjavalib"},
+ }),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["exported-system-module"],
@@ -775,7 +523,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -820,59 +568,6 @@
],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_exported-system-module@current",
- sdk_member_name: "exported-system-module",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/exported-system-module.jar"],
-}
-
-java_import {
- name: "mysdk_system-module@current",
- sdk_member_name: "system-module",
- visibility: ["//visibility:private"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/system-module.jar"],
-}
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:anyapex"],
- shared_library: false,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
-}
-
-java_system_modules_import {
- name: "mysdk_my-system-modules@current",
- sdk_member_name: "my-system-modules",
- visibility: ["//visibility:public"],
- libs: [
- "mysdk_system-module@current",
- "mysdk_exported-system-module@current",
- "mysdk_myjavalib.stubs@current",
- ],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_exported-system-module@current"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
- java_system_modules: ["mysdk_my-system-modules@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
.intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
@@ -880,6 +575,53 @@
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
+ checkInfoContents(result.Config, `
+[
+ {
+ "@type": "sdk",
+ "@name": "mysdk",
+ "java_header_libs": [
+ "exported-system-module",
+ "system-module"
+ ],
+ "java_sdk_libs": [
+ "myjavalib"
+ ],
+ "java_system_modules": [
+ "my-system-modules"
+ ]
+ },
+ {
+ "@type": "java_library",
+ "@name": "exported-system-module"
+ },
+ {
+ "@type": "java_system_modules",
+ "@name": "my-system-modules",
+ "@deps": [
+ "exported-system-module",
+ "system-module"
+ ]
+ },
+ {
+ "@type": "java_sdk_library",
+ "@name": "myjavalib",
+ "dist_stem": "myjavalib",
+ "scopes": {
+ "public": {
+ "current_api": "sdk_library/public/myjavalib.txt",
+ "latest_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib.api.public.latest/gen/myjavalib.api.public.latest",
+ "latest_removed_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib-removed.api.public.latest/gen/myjavalib-removed.api.public.latest",
+ "removed_api": "sdk_library/public/myjavalib-removed.txt"
+ }
+ }
+ },
+ {
+ "@type": "java_library",
+ "@name": "system-module"
+ }
+]
+`),
)
}
@@ -910,7 +652,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -932,36 +674,6 @@
libs: ["mysdk_system-module"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_system-module@current",
- sdk_member_name: "system-module",
- visibility: ["//visibility:private"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- jars: ["java/system-module.jar"],
-}
-
-java_system_modules_import {
- name: "mysdk_my-system-modules@current",
- sdk_member_name: "my-system-modules",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- libs: ["mysdk_system-module@current"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- java_system_modules: ["mysdk_my-system-modules@current"],
-}
-`),
checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac/system-module.jar -> java/system-module.jar"),
)
}
@@ -1004,7 +716,7 @@
`)
CheckSnapshot(t, result, "myexports", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -1041,58 +753,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myexports_hostjavalib@current",
- sdk_member_name: "hostjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- jars: ["java/hostjavalib.jar"],
-}
-
-java_import {
- name: "myexports_androidjavalib@current",
- sdk_member_name: "androidjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/androidjavalib.jar"],
-}
-
-java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- target: {
- android: {
- jars: ["java/android/myjavalib.jar"],
- },
- linux_glibc: {
- jars: ["java/linux_glibc/myjavalib.jar"],
- },
- },
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- host_supported: true,
- java_libs: ["myexports_myjavalib@current"],
- target: {
- android: {
- java_header_libs: ["myexports_androidjavalib@current"],
- },
- linux_glibc: {
- java_header_libs: ["myexports_hostjavalib@current"],
- },
- },
-}
-`),
checkAllCopyRules(`
.intermediates/hostjavalib/linux_glibc_common/javac/hostjavalib.jar -> java/hostjavalib.jar
.intermediates/androidjavalib/android_common/turbine-combined/androidjavalib.jar -> java/androidjavalib.jar
@@ -1122,7 +782,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1155,45 +815,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:anyapex"],
- shared_library: false,
- permitted_packages: ["pkg.myjavalib"],
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
- system: {
- jars: ["sdk_library/system/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
- current_api: "sdk_library/system/myjavalib.txt",
- removed_api: "sdk_library/system/myjavalib-removed.txt",
- sdk_version: "system_current",
- },
- test: {
- jars: ["sdk_library/test/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
- current_api: "sdk_library/test/myjavalib.txt",
- removed_api: "sdk_library/test/myjavalib-removed.txt",
- sdk_version: "test_current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1210,12 +831,6 @@
".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
),
- snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
- // Make sure that the name of the child modules created by a versioned java_sdk_library_import
- // module is correct, i.e. the suffix is added before the version and not after.
- result.Module("mysdk_myjavalib.stubs@current", "android_common")
- result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
- }),
)
}
@@ -1243,7 +858,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1290,7 +905,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1319,6 +934,58 @@
)
}
+func TestSnapshotWithJavaSdkLibrary_AnnotationsZip_PreT(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJavaSdkLibrary,
+ android.FixtureMergeEnv(map[string]string{
+ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
+ }),
+ ).RunTestWithBp(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ shared_library: false,
+ annotations_enabled: true,
+ public: {
+ enabled: true,
+ },
+ }
+ `)
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+ `),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+ `),
+ checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"),
+ )
+}
+
func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
@@ -1342,7 +1009,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1409,7 +1076,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1427,30 +1094,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: true,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "none",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1481,7 +1124,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1499,30 +1142,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: true,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "module_current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1556,7 +1175,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1581,37 +1200,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:anyapex"],
- shared_library: true,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
- system: {
- jars: ["sdk_library/system/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
- current_api: "sdk_library/system/myjavalib.txt",
- removed_api: "sdk_library/system/myjavalib-removed.txt",
- sdk_version: "system_current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1652,7 +1240,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1684,44 +1272,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:anyapex"],
- shared_library: true,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
- system: {
- jars: ["sdk_library/system/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
- current_api: "sdk_library/system/myjavalib.txt",
- removed_api: "sdk_library/system/myjavalib-removed.txt",
- sdk_version: "system_current",
- },
- module_lib: {
- jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
- current_api: "sdk_library/module-lib/myjavalib.txt",
- removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
- sdk_version: "module_current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1763,7 +1313,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1788,37 +1338,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:anyapex"],
- shared_library: true,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
- system_server: {
- jars: ["sdk_library/system-server/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/system-server/myjavalib_stub_sources"],
- current_api: "sdk_library/system-server/myjavalib.txt",
- removed_api: "sdk_library/system-server/myjavalib-removed.txt",
- sdk_version: "system_server_current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1854,7 +1373,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1873,31 +1392,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:anyapex"],
- naming_scheme: "default",
- shared_library: true,
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1936,7 +1430,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -1955,31 +1449,6 @@
},
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: true,
- doctag_files: ["doctags/docs/known_doctags"],
- public: {
- jars: ["sdk_library/public/myjavalib-stubs.jar"],
- stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
- current_api: "sdk_library/public/myjavalib.txt",
- removed_api: "sdk_library/public/myjavalib-removed.txt",
- sdk_version: "current",
- },
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
diff --git a/sdk/license_sdk_test.go b/sdk/license_sdk_test.go
index 1ef6fe6..829edf1 100644
--- a/sdk/license_sdk_test.go
+++ b/sdk/license_sdk_test.go
@@ -60,7 +60,7 @@
`)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
package {
@@ -91,44 +91,6 @@
],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-package {
- // A default list here prevents the license LSC from adding its own list which would
- // be unnecessary as every module in the sdk already has its own licenses property.
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- licenses: ["mysdk_mylicense@current"],
- jars: ["java/myjavalib.jar"],
-}
-
-license {
- name: "mysdk_mylicense@current",
- sdk_member_name: "mylicense",
- visibility: ["//visibility:private"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "legacy_unencumbered",
- ],
- license_text: [
- "licenses/NOTICE1",
- "licenses/NOTICE2",
- ],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@current"],
-}
- `),
checkAllCopyRules(`
.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
NOTICE1 -> licenses/NOTICE1
diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go
index a3db189..99caf13 100644
--- a/sdk/member_trait_test.go
+++ b/sdk/member_trait_test.go
@@ -134,7 +134,7 @@
).RunTest(t)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -145,23 +145,6 @@
jars: ["javalibs/myjavalib.jar"],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["javalibs/myjavalib.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- fake_members: ["mysdk_myjavalib@current"],
-}
-`),
)
}
@@ -216,7 +199,7 @@
).RunTest(t)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_test_import {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 84c9a96..aeeedb4 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -39,7 +39,6 @@
ctx.RegisterModuleType("sdk", SdkModuleFactory)
ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
}
type sdk struct {
@@ -76,6 +75,8 @@
snapshotFile android.OptionalPath
+ infoFile android.OptionalPath
+
// The builder, preserved for testing.
builderForTests *snapshotBuilder
}
@@ -145,7 +146,7 @@
return s
}
-// sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
+// sdk_snapshot is a snapshot of an SDK. This is an auto-generated module.
func SnapshotModuleFactory() android.Module {
s := newSdkModule(false)
s.properties.Snapshot = true
@@ -192,27 +193,32 @@
}
// Generate the snapshot from the member info.
- p := s.buildSnapshot(ctx, sdkVariants)
- zip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), p.Base(), p)
- s.snapshotFile = android.OptionalPathForPath(zip)
+ s.buildSnapshot(ctx, sdkVariants)
}
}
func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
- if !s.snapshotFile.Valid() {
+ if !s.snapshotFile.Valid() != !s.infoFile.Valid() {
+ panic("Snapshot (%q) and info file (%q) should both be set or neither should be set.")
+ } else if !s.snapshotFile.Valid() {
return []android.AndroidMkEntries{}
}
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "FAKE",
OutputFile: s.snapshotFile,
- DistFiles: android.MakeDefaultDistFiles(s.snapshotFile.Path()),
+ DistFiles: android.MakeDefaultDistFiles(s.snapshotFile.Path(), s.infoFile.Path()),
Include: "$(BUILD_PHONY_PACKAGE)",
ExtraFooters: []android.AndroidMkExtraFootersFunc{
func(w io.Writer, name, prefix, moduleDir string) {
// Allow the sdk to be built by simply passing its name on the command line.
fmt.Fprintln(w, ".PHONY:", s.Name())
fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
+
+ // Allow the sdk info to be built by simply passing its name on the command line.
+ infoTarget := s.Name() + ".info"
+ fmt.Fprintln(w, ".PHONY:", infoTarget)
+ fmt.Fprintln(w, infoTarget+":", s.infoFile.String())
},
},
}}
@@ -275,21 +281,6 @@
func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("SdkMember", memberMutator).Parallel()
ctx.TopDown("SdkMember_deps", memberDepsMutator).Parallel()
- ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel()
-}
-
-// RegisterPostDepsMutators registers post-deps mutators to support modules implementing SdkAware
-// interface and the sdk module type. This function has been made public to be called by tests
-// outside of the sdk package
-func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
- // These must run AFTER apexMutator. Note that the apex package is imported even though there is
- // no direct dependency to the package here. sdkDepsMutator sets the SDK requirements from an
- // APEX to its dependents. Since different versions of the same SDK can be used by different
- // APEXes, the apex and its dependents (which includes the dependencies to the sdk members)
- // should have been mutated for the apex before the SDK requirements are set.
- ctx.TopDown("SdkDepsMutator", sdkDepsMutator).Parallel()
- ctx.BottomUp("SdkDepsReplaceMutator", sdkDepsReplaceMutator).Parallel()
- ctx.TopDown("SdkRequirementCheck", sdkRequirementsMutator).Parallel()
}
type dependencyTag struct {
@@ -301,38 +292,6 @@
var _ android.ExcludeFromApexContentsTag = dependencyTag{}
-// For dependencies from an in-development version of an SDK member to frozen versions of the same member
-// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
-//
-// The dependency represented by this tag requires that for every APEX variant created for the
-// `from` module that an equivalent APEX variant is created for the 'to' module. This is because an
-// APEX that requires a specific version of an sdk (via the `uses_sdks` property will replace
-// dependencies on the unversioned sdk member with a dependency on the appropriate versioned sdk
-// member. In order for that to work the versioned sdk member needs to have a variant for that APEX.
-// As it is not known at the time that the APEX variants are created which specific APEX variants of
-// a versioned sdk members will be required it is necessary for the versioned sdk members to have
-// variants for any APEX that it could be used within.
-//
-// If the APEX selects a versioned sdk member then it will not have a dependency on the `from`
-// module at all so any dependencies of that module will not affect the APEX. However, if the APEX
-// selects the unversioned sdk member then it must exclude all the versioned sdk members. In no
-// situation would this dependency cause the `to` module to be added to the APEX hence why this tag
-// also excludes the `to` module from being added to the APEX contents.
-type sdkMemberVersionedDepTag struct {
- dependencyTag
- member string
- version string
-}
-
-func (t sdkMemberVersionedDepTag) AlwaysRequireApexVariant() bool {
- return true
-}
-
-// Mark this tag so dependencies that use it are excluded from visibility enforcement.
-func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
-
-var _ android.AlwaysRequireApexVariantTag = sdkMemberVersionedDepTag{}
-
// Step 1: create dependencies from an SDK module to its members.
func memberMutator(mctx android.BottomUpMutatorContext) {
if s, ok := mctx.Module().(*sdk); ok {
@@ -391,125 +350,10 @@
}
}
-// Step 3: create dependencies from the unversioned SDK member to snapshot versions
-// of the same member. By having these dependencies, they are mutated for multiple Mainline modules
-// (apex and apk), each of which might want different sdks to be built with. For example, if both
-// apex A and B are referencing libfoo which is a member of sdk 'mysdk', the two APEXes can be
-// built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
-// using.
-func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
- if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() && m.IsVersioned() {
- if !m.ContainingSdk().Unversioned() {
- memberName := m.MemberName()
- tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
- mctx.AddReverseDependency(mctx.Module(), tag, memberName)
- }
- }
-}
-
// An interface that encapsulates all the functionality needed to manage the sdk dependencies.
//
// It is a mixture of apex and sdk module functionality.
type sdkAndApexModule interface {
android.Module
android.DepIsInSameApex
- android.RequiredSdks
-}
-
-// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
-// descendants
-func sdkDepsMutator(mctx android.TopDownMutatorContext) {
- if parent, ok := mctx.Module().(sdkAndApexModule); ok {
- // Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
- // by reading its own properties like `uses_sdks`.
- requiredSdks := parent.RequiredSdks()
- if len(requiredSdks) > 0 {
- mctx.VisitDirectDeps(func(m android.Module) {
- // Only propagate required sdks from the apex onto its contents.
- if dep, ok := m.(android.SdkAware); ok && android.IsDepInSameApex(mctx, parent, dep) {
- dep.BuildWithSdks(requiredSdks)
- }
- })
- }
- }
-}
-
-// Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
-// versioned module is used instead of the un-versioned (in-development) module libfoo
-func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
- if versionedSdkMember, ok := mctx.Module().(android.SdkAware); ok && versionedSdkMember.IsInAnySdk() && versionedSdkMember.IsVersioned() {
- if sdk := versionedSdkMember.ContainingSdk(); !sdk.Unversioned() {
- // Only replace dependencies to <sdkmember> with <sdkmember@required-version>
- // if the depending module requires it. e.g.
- // foo -> sdkmember
- // will be transformed to:
- // foo -> sdkmember@1
- // if and only if foo is a member of an APEX that requires version 1 of the
- // sdk containing sdkmember.
- memberName := versionedSdkMember.MemberName()
-
- // Convert a panic into a normal error to allow it to be more easily tested for. This is a
- // temporary workaround, once http://b/183204176 has been fixed this can be removed.
- // TODO(b/183204176): Remove this after fixing.
- defer func() {
- if r := recover(); r != nil {
- mctx.ModuleErrorf("sdkDepsReplaceMutator %s", r)
- }
- }()
-
- // Replace dependencies on sdkmember with a dependency on the current module which
- // is a versioned prebuilt of the sdkmember if required.
- mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
- // from - foo
- // to - sdkmember
- replace := false
- if parent, ok := from.(android.RequiredSdks); ok {
- replace = parent.RequiredSdks().Contains(sdk)
- }
- return replace
- })
- }
- }
-}
-
-// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
-func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(sdkAndApexModule); ok {
- requiredSdks := m.RequiredSdks()
- if len(requiredSdks) == 0 {
- return
- }
- mctx.VisitDirectDeps(func(dep android.Module) {
- tag := mctx.OtherModuleDependencyTag(dep)
- if tag == android.DefaultsDepTag {
- // dependency to defaults is always okay
- return
- }
-
- // Ignore the dependency from the unversioned member to any versioned members as an
- // apex that depends on the unversioned member will not also be depending on a versioned
- // member.
- if _, ok := tag.(sdkMemberVersionedDepTag); ok {
- return
- }
-
- // If the dep is outside of the APEX, but is not in any of the required SDKs, we know that the
- // dep is a violation.
- if sa, ok := dep.(android.SdkAware); ok {
- // It is not an error if a dependency that is excluded from the apex due to the tag is not
- // in one of the required SDKs. That is because all of the existing tags that implement it
- // do not depend on modules which can or should belong to an sdk_snapshot.
- if _, ok := tag.(android.ExcludeFromApexContentsTag); ok {
- // The tag defines a dependency that never requires the child module to be part of the
- // same apex.
- return
- }
-
- if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
- mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
- sa.Name(), sa.ContainingSdk(), requiredSdks)
- }
- }
- })
- }
}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 83294f6..1ec12c3 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -37,64 +37,6 @@
os.Exit(m.Run())
}
-func TestDepNotInRequiredSdks(t *testing.T) {
- testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, `
- sdk {
- name: "mysdk",
- java_header_libs: ["sdkmember"],
- }
-
- sdk_snapshot {
- name: "mysdk@1",
- java_header_libs: ["sdkmember_mysdk_1"],
- }
-
- java_import {
- name: "sdkmember",
- prefer: false,
- host_supported: true,
- }
-
- java_import {
- name: "sdkmember_mysdk_1",
- sdk_member_name: "sdkmember",
- host_supported: true,
- }
-
- java_library {
- name: "myjavalib",
- srcs: ["Test.java"],
- libs: [
- "sdkmember",
- "otherlib",
- ],
- system_modules: "none",
- sdk_version: "none",
- compile_dex: true,
- host_supported: true,
- apex_available: ["myapex"],
- }
-
- // this lib is no in mysdk
- java_library {
- name: "otherlib",
- srcs: ["Test.java"],
- system_modules: "none",
- sdk_version: "none",
- compile_dex: true,
- host_supported: true,
- }
-
- apex {
- name: "myapex",
- java_libs: ["myjavalib"],
- uses_sdks: ["mysdk@1"],
- key: "myapex.key",
- certificate: ":myapex.cert",
- }
- `)
-}
-
// Ensure that prebuilt modules have the same effective visibility as the source
// modules.
func TestSnapshotVisibility(t *testing.T) {
@@ -177,18 +119,6 @@
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: [
- "//other/foo",
- "//package",
- "//prebuilts/mysdk",
- ],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-java_import {
name: "myjavalib",
prefer: false,
visibility: [
@@ -201,14 +131,6 @@
}
java_import {
- name: "mysdk_mypublicjavalib@current",
- sdk_member_name: "mypublicjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/mypublicjavalib.jar"],
-}
-
-java_import {
name: "mypublicjavalib",
prefer: false,
visibility: ["//visibility:public"],
@@ -217,18 +139,6 @@
}
java_import {
- name: "mysdk_mydefaultedjavalib@current",
- sdk_member_name: "mydefaultedjavalib",
- visibility: [
- "//other/bar",
- "//package",
- "//prebuilts/mysdk",
- ],
- apex_available: ["//apex_available:platform"],
- jars: ["java/mydefaultedjavalib.jar"],
-}
-
-java_import {
name: "mydefaultedjavalib",
prefer: false,
visibility: [
@@ -241,17 +151,6 @@
}
java_import {
- name: "mysdk_myprivatejavalib@current",
- sdk_member_name: "myprivatejavalib",
- visibility: [
- "//package",
- "//prebuilts/mysdk",
- ],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myprivatejavalib.jar"],
-}
-
-java_import {
name: "myprivatejavalib",
prefer: false,
visibility: [
@@ -261,20 +160,6 @@
apex_available: ["//apex_available:platform"],
jars: ["java/myprivatejavalib.jar"],
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: [
- "//other/foo",
- "//package:__subpackages__",
- ],
- java_header_libs: [
- "mysdk_myjavalib@current",
- "mysdk_mypublicjavalib@current",
- "mysdk_mydefaultedjavalib@current",
- "mysdk_myprivatejavalib@current",
- ],
-}
`))
}
@@ -321,7 +206,10 @@
result := testSdkWithFs(t, sdk, nil)
CheckSnapshot(t, result, "mysdk", "",
- checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`))
+ checkAllOtherCopyRules(`
+.intermediates/mysdk/common_os/mysdk-current.info -> mysdk-current.info
+.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip
+`))
}
type EmbeddedPropertiesStruct struct {
@@ -505,26 +393,12 @@
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-java_import {
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@current"],
-}
`),
)
})
@@ -544,26 +418,12 @@
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-java_import {
name: "myjavalib",
prefer: true,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@current"],
-}
`),
)
})
@@ -583,14 +443,6 @@
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-java_import {
name: "myjavalib",
prefer: false,
use_source_config_var: {
@@ -601,113 +453,10 @@
apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@current"],
-}
`),
)
})
- t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
- result := android.GroupFixturePreparers(
- preparer,
- android.FixtureMergeEnv(map[string]string{
- "SOONG_SDK_SNAPSHOT_VERSION": "unversioned",
- }),
- ).RunTest(t)
-
- checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk.zip")
-
- CheckSnapshot(t, result, "mysdk", "",
- checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "myjavalib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
- `),
- )
- })
-
- t.Run("SOONG_SDK_SNAPSHOT_VERSION=current", func(t *testing.T) {
- result := android.GroupFixturePreparers(
- preparer,
- android.FixtureMergeEnv(map[string]string{
- "SOONG_SDK_SNAPSHOT_VERSION": "current",
- }),
- ).RunTest(t)
-
- checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
-
- CheckSnapshot(t, result, "mysdk", "",
- checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-java_import {
- name: "myjavalib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@current"],
-}
- `),
- )
- })
-
- t.Run("SOONG_SDK_SNAPSHOT_VERSION=2", func(t *testing.T) {
- result := android.GroupFixturePreparers(
- preparer,
- android.FixtureMergeEnv(map[string]string{
- "SOONG_SDK_SNAPSHOT_VERSION": "2",
- }),
- ).RunTest(t)
-
- checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-2.zip")
-
- CheckSnapshot(t, result, "mysdk", "",
- checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_myjavalib@2",
- sdk_member_name: "myjavalib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@2",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_myjavalib@2"],
-}
- `),
- // A versioned snapshot cannot be used on its own so add the source back in.
- snapshotTestPreparer(checkSnapshotWithoutSource, android.FixtureWithRootAndroidBp(bp)),
- )
- })
-
t.Run("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE=S", func(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForSdkTestWithJava,
@@ -724,6 +473,9 @@
name: "mybootclasspathfragment",
apex_available: ["myapex"],
contents: ["mysdklibrary"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
}
java_sdk_library {
@@ -740,7 +492,7 @@
).RunTest(t)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
prebuilt_bootclasspath_fragment {
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 16e3e7f..01692a3 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -83,7 +83,7 @@
).RunTest(t)
CheckSnapshot(t, result, "mysdk", "",
- checkUnversionedAndroidBpContents(`
+ checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
@@ -121,51 +121,5 @@
],
}
`),
- checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
- name: "mysdk_mysdklibrary@current",
- sdk_member_name: "mysdklibrary",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- shared_library: false,
- public: {
- jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
- stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
- current_api: "sdk_library/public/mysdklibrary.txt",
- removed_api: "sdk_library/public/mysdklibrary-removed.txt",
- sdk_version: "current",
- },
-}
-
-java_import {
- name: "mysdk_mylib@current",
- sdk_member_name: "mylib",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
- permitted_packages: ["mylib"],
-}
-
-prebuilt_systemserverclasspath_fragment {
- name: "mysdk_mysystemserverclasspathfragment@current",
- sdk_member_name: "mysystemserverclasspathfragment",
- visibility: ["//visibility:public"],
- apex_available: ["myapex"],
- contents: [
- "mysdk_mylib@current",
- "mysdk_mysdklibrary@current",
- ],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_sdk_libs: ["mysdk_mysdklibrary@current"],
- java_systemserver_libs: ["mysdk_mylib@current"],
- systemserverclasspath_fragments: ["mysdk_mysystemserverclasspathfragment@current"],
-}
-`),
)
}
diff --git a/sdk/testing.go b/sdk/testing.go
index 294f1a5..bed11b2 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -25,6 +25,8 @@
"android/soong/cc"
"android/soong/genrule"
"android/soong/java"
+
+ "github.com/google/blueprint/proptools"
)
// Prepare for running an sdk test with an apex.
@@ -81,6 +83,11 @@
}
}),
+ // Add a build number file.
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildNumberFile = proptools.StringPtr(BUILD_NUMBER_FILE)
+ }),
+
// Make sure that every test provides all the source files.
android.PrepareForTestDisallowNonExistentPaths,
android.MockFS{
@@ -115,34 +122,26 @@
}
}
-func pathsToStrings(paths android.Paths) []string {
- var ret []string
- for _, p := range paths {
- ret = append(ret, p.String())
- }
- return ret
-}
-
// Analyse the sdk build rules to extract information about what it is doing.
//
// e.g. find the src/dest pairs from each cp command, the various zip files
// generated, etc.
func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
info := &snapshotBuildInfo{
- t: t,
- r: result,
- version: sdk.builderForTests.version,
- androidBpContents: sdk.GetAndroidBpContentsForTests(),
- androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
- androidVersionedBpContents: sdk.GetVersionedAndroidBpContentsForTests(),
- snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
- targetBuildRelease: sdk.builderForTests.targetBuildRelease,
+ t: t,
+ r: result,
+ androidBpContents: sdk.GetAndroidBpContentsForTests(),
+ infoContents: sdk.GetInfoContentsForTests(),
+ snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
+ targetBuildRelease: sdk.builderForTests.targetBuildRelease,
}
buildParams := sdk.BuildParamsForTests()
copyRules := &strings.Builder{}
otherCopyRules := &strings.Builder{}
snapshotDirPrefix := sdk.builderForTests.snapshotDir.String() + "/"
+
+ seenBuildNumberFile := false
for _, bp := range buildParams {
switch bp.Rule.String() {
case android.Cp.String():
@@ -152,8 +151,14 @@
src := android.NormalizePathForTesting(bp.Input)
// We differentiate between copy rules for the snapshot, and copy rules for the install file.
if strings.HasPrefix(output.String(), snapshotDirPrefix) {
- // Get source relative to build directory.
- _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
+ // Don't include the build-number.txt file in the copy rules as that would break lots of
+ // tests, just verify that it is copied here as it should appear in every snapshot.
+ if output.Base() == BUILD_NUMBER_FILE {
+ seenBuildNumberFile = true
+ } else {
+ // Get source relative to build directory.
+ _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
+ }
info.snapshotContents = append(info.snapshotContents, dest)
} else {
_, _ = fmt.Fprintf(otherCopyRules, "%s -> %s\n", src, dest)
@@ -189,6 +194,10 @@
}
}
+ if !seenBuildNumberFile {
+ panic(fmt.Sprintf("Every snapshot must include the %s file", BUILD_NUMBER_FILE))
+ }
+
info.copyRules = copyRules.String()
info.otherCopyRules = otherCopyRules.String()
@@ -238,10 +247,7 @@
if dir != "" {
dir = filepath.Clean(dir) + "/"
}
- suffix := ""
- if snapshotBuildInfo.version != soongSdkSnapshotVersionUnversioned {
- suffix = "-" + snapshotBuildInfo.version
- }
+ suffix := "-" + soongSdkSnapshotVersionCurrent
expectedZipPath := fmt.Sprintf(".intermediates/%s%s/%s/%s%s.zip", dir, name, variant, name, suffix)
android.AssertStringEquals(t, "Snapshot zip file in wrong place", expectedZipPath, actual)
@@ -325,33 +331,6 @@
}
}
-// Check that the snapshot's unversioned generated Android.bp is correct.
-//
-// This func should be used to check the general snapshot generation code.
-//
-// Both the expected and actual string are both trimmed before comparing.
-func checkUnversionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- android.AssertTrimmedStringEquals(info.t, "unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents)
- }
-}
-
-// Check that the snapshot's versioned generated Android.bp is correct.
-//
-// This func should only be used to check the version specific snapshot generation code,
-// i.e. the encoding of version into module names and the generation of the _snapshot module. The
-// general snapshot generation code should be checked using the checkUnversionedAndroidBpContents()
-// func.
-//
-// Both the expected and actual string are both trimmed before comparing.
-func checkVersionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- android.AssertTrimmedStringEquals(info.t, "versioned Android.bp contents do not match", expected, info.androidVersionedBpContents)
- }
-}
-
// Check that the snapshot's copy rules are correct.
//
// The copy rules are formatted as <src> -> <dest>, one per line and then compared
@@ -383,6 +362,17 @@
}
}
+// Check that the snapshot's info contents are ciorrect.
+//
+// Both the expected and actual string are both trimmed before comparing.
+func checkInfoContents(config android.Config, expected string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.t.Helper()
+ android.AssertTrimmedStringEquals(info.t, "info contents do not match",
+ expected, android.StringRelativeToTop(config, info.infoContents))
+ }
+}
+
type resultChecker func(t *testing.T, result *android.TestResult)
// snapshotTestPreparer registers a preparer that will be used to customize the specified
@@ -446,19 +436,11 @@
// The result from RunTest()
r *android.TestResult
- // The version of the generated snapshot.
- //
- // See snapshotBuilder.version for more information about this field.
- version string
-
// The contents of the generated Android.bp file
androidBpContents string
- // The contents of the unversioned Android.bp file
- androidUnversionedBpContents string
-
- // The contents of the versioned Android.bp file
- androidVersionedBpContents string
+ // The contents of the info file.
+ infoContents string
// The paths, relative to the snapshot root, of all files and directories copied into the
// snapshot.
diff --git a/sdk/update.go b/sdk/update.go
index 389e845..457828b 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -15,6 +15,8 @@
package sdk
import (
+ "bytes"
+ "encoding/json"
"fmt"
"reflect"
"sort"
@@ -33,7 +35,7 @@
// ========================================================
//
// SOONG_SDK_SNAPSHOT_PREFER
-// By default every unversioned module in the generated snapshot has prefer: false. Building it
+// By default every module in the generated snapshot has prefer: false. Building it
// with SOONG_SDK_SNAPSHOT_PREFER=true will force them to use prefer: true.
//
// SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR
@@ -67,20 +69,6 @@
// maintainable solution has been implemented.
// TODO(b/174997203): Remove when no longer necessary.
//
-// SOONG_SDK_SNAPSHOT_VERSION
-// This provides control over the version of the generated snapshot.
-//
-// SOONG_SDK_SNAPSHOT_VERSION=current will generate unversioned and versioned prebuilts and a
-// versioned snapshot module. This is the default behavior. The zip file containing the
-// generated snapshot will be <sdk-name>-current.zip.
-//
-// SOONG_SDK_SNAPSHOT_VERSION=unversioned will generate unversioned prebuilts only and the zip
-// file containing the generated snapshot will be <sdk-name>.zip.
-//
-// SOONG_SDK_SNAPSHOT_VERSION=<number> will generate versioned prebuilts and a versioned
-// snapshot module only. The zip file containing the generated snapshot will be
-// <sdk-name>-<number>.zip.
-//
// SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE
// This allows the target build release (i.e. the release version of the build within which
// the snapshot will be used) of the snapshot to be specified. If unspecified then it defaults
@@ -128,8 +116,7 @@
)
const (
- soongSdkSnapshotVersionUnversioned = "unversioned"
- soongSdkSnapshotVersionCurrent = "current"
+ soongSdkSnapshotVersionCurrent = "current"
)
type generatedContents struct {
@@ -161,13 +148,13 @@
// IndentedPrintf will add spaces to indent the line to the appropriate level before printing the
// arguments.
func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) {
- fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format, args...)
+ _, _ = fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format, args...)
}
// UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing
// the arguments.
func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) {
- fmt.Fprintf(&(gc.content), format, args...)
+ _, _ = fmt.Fprintf(&(gc.content), format, args...)
}
func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -219,9 +206,19 @@
exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
}
+ var container android.SdkAware
+ if parent != ctx.Module() {
+ container = parent.(android.SdkAware)
+ }
+
export := memberTag.ExportMember()
s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
- s, memberType, child.(android.SdkAware), export, exportedComponentsInfo,
+ sdkVariant: s,
+ memberType: memberType,
+ variant: child.(android.SdkAware),
+ container: container,
+ export: export,
+ exportedComponentsInfo: exportedComponentsInfo,
})
// Recurse down into the member's dependencies as it may have dependencies that need to be
@@ -256,13 +253,19 @@
member = &sdkMember{memberType: memberType, name: name}
byName[name] = member
byType[memberType] = append(byType[memberType], member)
+ } else if member.memberType != memberType {
+ // validate whether this is the same member type or and overriding member type
+ if memberType.Overrides(member.memberType) {
+ member.memberType = memberType
+ } else if !member.memberType.Overrides(memberType) {
+ ctx.ModuleErrorf("Incompatible member types %q %q", member.memberType, memberType)
+ }
}
// Only append new variants to the list. This is needed because a member can be both
// exported by the sdk and also be a transitive sdk member.
member.variants = appendUniqueVariants(member.variants, variant)
}
-
var members []*sdkMember
for _, memberListProperty := range s.memberTypeListProperties() {
membersOfType := byType[memberListProperty.memberType]
@@ -281,6 +284,10 @@
return append(variants, newVariant)
}
+// BUILD_NUMBER_FILE is the name of the file in the snapshot zip that will contain the number of
+// the build from which the snapshot was produced.
+const BUILD_NUMBER_FILE = "snapshot-creation-build-number.txt"
+
// SDK directory structure
// <sdk_root>/
// Android.bp : definition of a 'sdk' module is here. This is a hand-made one.
@@ -299,15 +306,9 @@
// <arch>/lib/
// libFoo.so : a stub library
-// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot
-// This isn't visible to users, so could be changed in future.
-func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
- return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
-}
-
// buildSnapshot is the main function in this source file. It creates rules to copy
// the contents (header files, stub libraries, etc) into the zip file.
-func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
+func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) {
// Aggregate all the sdkMemberVariantDep instances from all the sdk variants.
hasLicenses := false
@@ -356,20 +357,9 @@
}
config := ctx.Config()
- version := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_VERSION", "current")
- // Generate versioned modules in the snapshot unless an unversioned snapshot has been requested.
- generateVersioned := version != soongSdkSnapshotVersionUnversioned
-
- // Generate unversioned modules in the snapshot unless a numbered snapshot has been requested.
- //
- // Unversioned modules are not required in that case because the numbered version will be a
- // finalized version of the snapshot that is intended to be kept separate from the
- generateUnversioned := version == soongSdkSnapshotVersionUnversioned || version == soongSdkSnapshotVersionCurrent
- snapshotZipFileSuffix := ""
- if generateVersioned {
- snapshotZipFileSuffix = "-" + version
- }
+ // Always add -current to the end
+ snapshotFileSuffix := "-current"
currentBuildRelease := latestBuildRelease()
targetBuildReleaseEnv := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", currentBuildRelease.name)
@@ -382,7 +372,6 @@
builder := &snapshotBuilder{
ctx: ctx,
sdk: s,
- version: version,
snapshotDir: snapshotDir.OutputPath,
copies: make(map[string]string),
filesToZip: []android.Path{bp.path},
@@ -432,38 +421,19 @@
s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule))
}
- // Create a transformer that will transform an unversioned module into a versioned module.
- unversionedToVersionedTransformer := unversionedToVersionedTransformation{builder: builder}
-
- // Create a transformer that will transform an unversioned module by replacing any references
+ // Create a transformer that will transform a module by replacing any references
// to internal members with a unique module name and setting prefer: false.
- unversionedTransformer := unversionedTransformation{
+ snapshotTransformer := snapshotTransformation{
builder: builder,
}
- for _, unversioned := range builder.prebuiltOrder {
+ for _, module := range builder.prebuiltOrder {
// Prune any empty property sets.
- unversioned = unversioned.transform(pruneEmptySetTransformer{})
+ module = module.transform(pruneEmptySetTransformer{})
- if generateVersioned {
- // Copy the unversioned module so it can be modified to make it versioned.
- versioned := unversioned.deepCopy()
-
- // Transform the unversioned module into a versioned one.
- versioned.transform(unversionedToVersionedTransformer)
- bpFile.AddModule(versioned)
- }
-
- if generateUnversioned {
- // Transform the unversioned module to make it suitable for use in the snapshot.
- unversioned.transform(unversionedTransformer)
- bpFile.AddModule(unversioned)
- }
- }
-
- if generateVersioned {
- // Add the sdk/module_exports_snapshot module to the bp file.
- s.addSnapshotModule(ctx, builder, sdkVariants, memberVariantDeps)
+ // Transform the module module to make it suitable for use in the snapshot.
+ module.transform(snapshotTransformer)
+ bpFile.AddModule(module)
}
// generate Android.bp
@@ -479,10 +449,13 @@
bp.build(pctx, ctx, nil)
+ // Copy the build number file into the snapshot.
+ builder.CopyToSnapshot(ctx.Config().BuildNumberFile(ctx), BUILD_NUMBER_FILE)
+
filesToZip := builder.filesToZip
// zip them all
- zipPath := fmt.Sprintf("%s%s.zip", ctx.ModuleName(), snapshotZipFileSuffix)
+ zipPath := fmt.Sprintf("%s%s.zip", ctx.ModuleName(), snapshotFileSuffix)
outputZipFile := android.PathForModuleOut(ctx, zipPath).OutputPath
outputDesc := "Building snapshot for " + ctx.ModuleName()
@@ -495,7 +468,7 @@
zipFile = outputZipFile
desc = outputDesc
} else {
- intermediatePath := fmt.Sprintf("%s%s.unmerged.zip", ctx.ModuleName(), snapshotZipFileSuffix)
+ intermediatePath := fmt.Sprintf("%s%s.unmerged.zip", ctx.ModuleName(), snapshotFileSuffix)
zipFile = android.PathForModuleOut(ctx, intermediatePath).OutputPath
desc = "Building intermediate snapshot for " + ctx.ModuleName()
}
@@ -520,7 +493,125 @@
})
}
- return outputZipFile
+ modules := s.generateInfoData(ctx, memberVariantDeps)
+
+ // Output the modules information as pretty printed JSON.
+ info := newGeneratedFile(ctx, fmt.Sprintf("%s%s.info", ctx.ModuleName(), snapshotFileSuffix))
+ output, err := json.MarshalIndent(modules, "", " ")
+ if err != nil {
+ ctx.ModuleErrorf("error generating %q: %s", info, err)
+ }
+ builder.infoContents = string(output)
+ info.generatedContents.UnindentedPrintf("%s", output)
+ info.build(pctx, ctx, nil)
+ infoPath := info.path
+ installedInfo := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), infoPath.Base(), infoPath)
+ s.infoFile = android.OptionalPathForPath(installedInfo)
+
+ // Install the zip, making sure that the info file has been installed as well.
+ installedZip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), outputZipFile.Base(), outputZipFile, installedInfo)
+ s.snapshotFile = android.OptionalPathForPath(installedZip)
+}
+
+type moduleInfo struct {
+ // The type of the module, e.g. java_sdk_library
+ moduleType string
+ // The name of the module.
+ name string
+ // A list of additional dependencies of the module.
+ deps []string
+ // Additional member specific properties.
+ // These will be added into the generated JSON alongside the above properties.
+ memberSpecific map[string]interface{}
+}
+
+func (m *moduleInfo) MarshalJSON() ([]byte, error) {
+ buffer := bytes.Buffer{}
+
+ separator := ""
+ writeObjectPair := func(key string, value interface{}) {
+ buffer.WriteString(fmt.Sprintf("%s%q: ", separator, key))
+ b, err := json.Marshal(value)
+ if err != nil {
+ panic(err)
+ }
+ buffer.Write(b)
+ separator = ","
+ }
+
+ buffer.WriteString("{")
+ writeObjectPair("@type", m.moduleType)
+ writeObjectPair("@name", m.name)
+ if m.deps != nil {
+ writeObjectPair("@deps", m.deps)
+ }
+ for _, k := range android.SortedStringKeys(m.memberSpecific) {
+ v := m.memberSpecific[k]
+ writeObjectPair(k, v)
+ }
+ buffer.WriteString("}")
+ return buffer.Bytes(), nil
+}
+
+var _ json.Marshaler = (*moduleInfo)(nil)
+
+// generateInfoData creates a list of moduleInfo structures that will be marshalled into JSON.
+func (s *sdk) generateInfoData(ctx android.ModuleContext, memberVariantDeps []sdkMemberVariantDep) interface{} {
+ modules := []*moduleInfo{}
+ sdkInfo := moduleInfo{
+ moduleType: "sdk",
+ name: ctx.ModuleName(),
+ memberSpecific: map[string]interface{}{},
+ }
+ modules = append(modules, &sdkInfo)
+
+ name2Info := map[string]*moduleInfo{}
+ getModuleInfo := func(module android.Module) *moduleInfo {
+ name := module.Name()
+ info := name2Info[name]
+ if info == nil {
+ moduleType := ctx.OtherModuleType(module)
+ // Remove any suffix added when creating modules dynamically.
+ moduleType = strings.Split(moduleType, "__")[0]
+ info = &moduleInfo{
+ moduleType: moduleType,
+ name: name,
+ }
+
+ additionalSdkInfo := ctx.OtherModuleProvider(module, android.AdditionalSdkInfoProvider).(android.AdditionalSdkInfo)
+ info.memberSpecific = additionalSdkInfo.Properties
+
+ name2Info[name] = info
+ }
+ return info
+ }
+
+ for _, memberVariantDep := range memberVariantDeps {
+ propertyName := memberVariantDep.memberType.SdkPropertyName()
+ var list []string
+ if v, ok := sdkInfo.memberSpecific[propertyName]; ok {
+ list = v.([]string)
+ }
+
+ memberName := memberVariantDep.variant.Name()
+ list = append(list, memberName)
+ sdkInfo.memberSpecific[propertyName] = android.SortedUniqueStrings(list)
+
+ if memberVariantDep.container != nil {
+ containerInfo := getModuleInfo(memberVariantDep.container)
+ containerInfo.deps = android.SortedUniqueStrings(append(containerInfo.deps, memberName))
+ }
+
+ // Make sure that the module info is created for each module.
+ getModuleInfo(memberVariantDep.variant)
+ }
+
+ for _, memberName := range android.SortedStringKeys(name2Info) {
+ info := name2Info[memberName]
+ modules = append(modules, info)
+ }
+
+ return modules
}
// filterOutComponents removes any item from the deps list that is a component of another item in
@@ -529,7 +620,7 @@
func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
// Collate the set of components that all the modules added to the sdk provide.
components := map[string]*sdkMemberVariantDep{}
- for i, _ := range deps {
+ for i := range deps {
dep := &deps[i]
for _, c := range dep.exportedComponentsInfo.Components {
components[c] = dep
@@ -564,81 +655,6 @@
return filtered
}
-// addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
-func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
- bpFile := builder.bpFile
-
- snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
- var snapshotModuleType string
- if s.properties.Module_exports {
- snapshotModuleType = "module_exports_snapshot"
- } else {
- snapshotModuleType = "sdk_snapshot"
- }
- snapshotModule := bpFile.newModule(snapshotModuleType)
- snapshotModule.AddProperty("name", snapshotName)
-
- // Make sure that the snapshot has the same visibility as the sdk.
- visibility := android.EffectiveVisibilityRules(ctx, s).Strings()
- if len(visibility) != 0 {
- snapshotModule.AddProperty("visibility", visibility)
- }
-
- addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule)
-
- combinedPropertiesList := s.collateSnapshotModuleInfo(ctx, sdkVariants, memberVariantDeps)
- commonCombinedProperties := s.optimizeSnapshotModuleProperties(ctx, combinedPropertiesList)
-
- s.addSnapshotPropertiesToPropertySet(builder, snapshotModule, commonCombinedProperties)
-
- targetPropertySet := snapshotModule.AddPropertySet("target")
-
- // Create a mapping from osType to combined properties.
- osTypeToCombinedProperties := map[android.OsType]*combinedSnapshotModuleProperties{}
- for _, combined := range combinedPropertiesList {
- osTypeToCombinedProperties[combined.sdkVariant.Os()] = combined
- }
-
- // Iterate over the os types in a fixed order.
- for _, osType := range s.getPossibleOsTypes() {
- if combined, ok := osTypeToCombinedProperties[osType]; ok {
- osPropertySet := targetPropertySet.AddPropertySet(osType.Name)
-
- s.addSnapshotPropertiesToPropertySet(builder, osPropertySet, combined)
- }
- }
-
- // If host is supported and any member is host OS dependent then disable host
- // by default, so that we can enable each host OS variant explicitly. This
- // avoids problems with implicitly enabled OS variants when the snapshot is
- // used, which might be different from this run (e.g. different build OS).
- if s.HostSupported() {
- var supportedHostTargets []string
- for _, memberVariantDep := range memberVariantDeps {
- if memberVariantDep.memberType.IsHostOsDependent() && memberVariantDep.variant.Target().Os.Class == android.Host {
- targetString := memberVariantDep.variant.Target().Os.String() + "_" + memberVariantDep.variant.Target().Arch.ArchType.String()
- if !android.InList(targetString, supportedHostTargets) {
- supportedHostTargets = append(supportedHostTargets, targetString)
- }
- }
- }
- if len(supportedHostTargets) > 0 {
- hostPropertySet := targetPropertySet.AddPropertySet("host")
- hostPropertySet.AddProperty("enabled", false)
- }
- // Enable the <os>_<arch> variant explicitly when we've disabled it by default on host.
- for _, hostTarget := range supportedHostTargets {
- propertySet := targetPropertySet.AddPropertySet(hostTarget)
- propertySet.AddProperty("enabled", true)
- }
- }
-
- // Prune any empty property sets.
- snapshotModule.transform(pruneEmptySetTransformer{})
-
- bpFile.AddModule(snapshotModule)
-}
-
// Check the syntax of the generated Android.bp file contents and if they are
// invalid then log an error with the contents (tagged with line numbers) and the
// errors that were found so that it is easy to see where the problem lies.
@@ -775,92 +791,34 @@
}
}
-func (s *sdk) addSnapshotPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, combined *combinedSnapshotModuleProperties) {
- staticProperties := combined.staticProperties
- multilib := staticProperties.Compile_multilib
- if multilib != "" && multilib != "both" {
- // Compile_multilib defaults to both so only needs to be set when it's specified and not both.
- propertySet.AddProperty("compile_multilib", multilib)
- }
-
- dynamicMemberTypeListProperties := combined.dynamicProperties
- for _, memberListProperty := range s.memberTypeListProperties() {
- if memberListProperty.getter == nil {
- continue
- }
- names := memberListProperty.getter(dynamicMemberTypeListProperties)
- if len(names) > 0 {
- propertySet.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names, false))
- }
- }
-}
-
type propertyTag struct {
name string
}
var _ android.BpPropertyTag = propertyTag{}
-// A BpPropertyTag to add to a property that contains references to other sdk members.
+// BpPropertyTag instances to add to a property that contains references to other sdk members.
//
-// This will cause the references to be rewritten to a versioned reference in the version
-// specific instance of a snapshot module.
+// These will ensure that the referenced modules are available, if required.
var requiredSdkMemberReferencePropertyTag = propertyTag{"requiredSdkMemberReferencePropertyTag"}
var optionalSdkMemberReferencePropertyTag = propertyTag{"optionalSdkMemberReferencePropertyTag"}
-// A BpPropertyTag that indicates the property should only be present in the versioned
-// module.
-//
-// This will cause the property to be removed from the unversioned instance of a
-// snapshot module.
-var sdkVersionedOnlyPropertyTag = propertyTag{"sdkVersionedOnlyPropertyTag"}
-
-type unversionedToVersionedTransformation struct {
+type snapshotTransformation struct {
identityTransformation
builder *snapshotBuilder
}
-func (t unversionedToVersionedTransformation) transformModule(module *bpModule) *bpModule {
- // Use a versioned name for the module but remember the original name for the
- // snapshot.
- name := module.Name()
- module.setProperty("name", t.builder.versionedSdkMemberName(name, true))
- module.insertAfter("name", "sdk_member_name", name)
- // Remove the prefer property if present as versioned modules never need marking with prefer.
- module.removeProperty("prefer")
- // Ditto for use_source_config_var
- module.removeProperty("use_source_config_var")
- return module
-}
-
-func (t unversionedToVersionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
- if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
- required := tag == requiredSdkMemberReferencePropertyTag
- return t.builder.versionedSdkMemberNames(value.([]string), required), tag
- } else {
- return value, tag
- }
-}
-
-type unversionedTransformation struct {
- identityTransformation
- builder *snapshotBuilder
-}
-
-func (t unversionedTransformation) transformModule(module *bpModule) *bpModule {
+func (t snapshotTransformation) transformModule(module *bpModule) *bpModule {
// If the module is an internal member then use a unique name for it.
name := module.Name()
- module.setProperty("name", t.builder.unversionedSdkMemberName(name, true))
+ module.setProperty("name", t.builder.snapshotSdkMemberName(name, true))
return module
}
-func (t unversionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t snapshotTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
required := tag == requiredSdkMemberReferencePropertyTag
- return t.builder.unversionedSdkMemberNames(value.([]string), required), tag
- } else if tag == sdkVersionedOnlyPropertyTag {
- // The property is not allowed in the unversioned module so remove it.
- return nil, nil
+ return t.builder.snapshotSdkMemberNames(value.([]string), required), tag
} else {
return value, tag
}
@@ -872,7 +830,7 @@
var _ bpTransformer = (*pruneEmptySetTransformer)(nil)
-func (t pruneEmptySetTransformer) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t pruneEmptySetTransformer) transformPropertySetAfterContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
if len(propertySet.properties) == 0 {
return nil, nil
} else {
@@ -881,20 +839,12 @@
}
func generateBpContents(contents *generatedContents, bpFile *bpFile) {
- generateFilteredBpContents(contents, bpFile, func(*bpModule) bool {
- return true
- })
-}
-
-func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n")
for _, bpModule := range bpFile.order {
- if moduleFilter(bpModule) {
- contents.IndentedPrintf("\n")
- contents.IndentedPrintf("%s {\n", bpModule.moduleType)
- outputPropertySet(contents, bpModule.bpPropertySet)
- contents.IndentedPrintf("}\n")
- }
+ contents.IndentedPrintf("\n")
+ contents.IndentedPrintf("%s {\n", bpModule.moduleType)
+ outputPropertySet(contents, bpModule.bpPropertySet)
+ contents.IndentedPrintf("}\n")
}
}
@@ -1026,36 +976,14 @@
return contents.content.String()
}
-func (s *sdk) GetUnversionedAndroidBpContentsForTests() string {
- contents := &generatedContents{}
- generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
- name := module.Name()
- // Include modules that are either unversioned or have no name.
- return !strings.Contains(name, "@")
- })
- return contents.content.String()
-}
-
-func (s *sdk) GetVersionedAndroidBpContentsForTests() string {
- contents := &generatedContents{}
- generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
- name := module.Name()
- // Include modules that are either versioned or have no name.
- return name == "" || strings.Contains(name, "@")
- })
- return contents.content.String()
+func (s *sdk) GetInfoContentsForTests() string {
+ return s.builderForTests.infoContents
}
type snapshotBuilder struct {
ctx android.ModuleContext
sdk *sdk
- // The version of the generated snapshot.
- //
- // See the documentation of SOONG_SDK_SNAPSHOT_VERSION above for details of the valid values of
- // this field.
- version string
-
snapshotDir android.OutputPath
bpFile *bpFile
@@ -1080,6 +1008,9 @@
// The target build release for which the snapshot is to be generated.
targetBuildRelease *buildRelease
+
+ // The contents of the .info file that describes the sdk contents.
+ infoContents string
}
func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) {
@@ -1206,13 +1137,6 @@
addHostDeviceSupportedProperties(deviceSupported, hostSupported, m)
- // Disable installation in the versioned module of those modules that are ever installable.
- if installable, ok := variant.(interface{ EverInstallable() bool }); ok {
- if installable.EverInstallable() {
- m.AddPropertyWithTag("installable", false, sdkVersionedOnlyPropertyTag)
- }
- }
-
s.prebuiltModules[name] = m
s.prebuiltOrder = append(s.prebuiltOrder, m)
return m
@@ -1245,45 +1169,28 @@
return optionalSdkMemberReferencePropertyTag
}
-// Get a versioned name appropriate for the SDK snapshot version being taken.
-func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string, required bool) string {
- if _, ok := s.allMembersByName[unversionedName]; !ok {
+// Get a name for sdk snapshot member. If the member is private then generate a snapshot specific
+// name. As part of the processing this checks to make sure that any required members are part of
+// the snapshot.
+func (s *snapshotBuilder) snapshotSdkMemberName(name string, required bool) string {
+ if _, ok := s.allMembersByName[name]; !ok {
if required {
- s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
+ s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", name)
}
- return unversionedName
- }
- return versionedSdkMemberName(s.ctx, unversionedName, s.version)
-}
-
-func (s *snapshotBuilder) versionedSdkMemberNames(members []string, required bool) []string {
- var references []string = nil
- for _, m := range members {
- references = append(references, s.versionedSdkMemberName(m, required))
- }
- return references
-}
-
-// Get an internal name unique to the sdk.
-func (s *snapshotBuilder) unversionedSdkMemberName(unversionedName string, required bool) string {
- if _, ok := s.allMembersByName[unversionedName]; !ok {
- if required {
- s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
- }
- return unversionedName
+ return name
}
- if s.isInternalMember(unversionedName) {
- return s.ctx.ModuleName() + "_" + unversionedName
+ if s.isInternalMember(name) {
+ return s.ctx.ModuleName() + "_" + name
} else {
- return unversionedName
+ return name
}
}
-func (s *snapshotBuilder) unversionedSdkMemberNames(members []string, required bool) []string {
+func (s *snapshotBuilder) snapshotSdkMemberNames(members []string, required bool) []string {
var references []string = nil
for _, m := range members {
- references = append(references, s.unversionedSdkMemberName(m, required))
+ references = append(references, s.snapshotSdkMemberName(m, required))
}
return references
}
@@ -1315,6 +1222,11 @@
// The variant that is added to the sdk.
variant android.SdkAware
+ // The optional container of this member, i.e. the module that is depended upon by the sdk
+ // (possibly transitively) and whose dependency on this module is why it was added to the sdk.
+ // Is nil if this a direct dependency of the sdk.
+ container android.SdkAware
+
// True if the member should be exported, i.e. accessible, from outside the sdk.
export bool
@@ -1634,7 +1546,9 @@
// added.
archInfo.Properties = variantPropertiesFactory()
- if len(archVariants) == 1 {
+ // if there are multiple supported link variants, we want to nest based on linkage even if there
+ // is only one variant, otherwise, if there is only one variant we can populate based on the arch
+ if len(archVariants) == 1 && len(ctx.MemberType().SupportedLinkages()) <= 1 {
archInfo.Properties.PopulateFromVariant(ctx, archVariants[0])
} else {
// Group the variants by image type.
@@ -1761,11 +1675,13 @@
// Create the properties into which the image variant specific properties will be added.
imageInfo.Properties = variantPropertiesFactory()
- if len(imageVariants) == 1 {
+ // if there are multiple supported link variants, we want to nest even if there is only one
+ // variant, otherwise, if there is only one variant we can populate based on the image
+ if len(imageVariants) == 1 && len(ctx.MemberType().SupportedLinkages()) <= 1 {
imageInfo.Properties.PopulateFromVariant(ctx, imageVariants[0])
} else {
// There is more than one variant for this image variant which must be differentiated by link
- // type.
+ // type. Or there are multiple supported linkages and we need to nest based on link type.
for _, linkVariant := range imageVariants {
linkType := getLinkType(linkVariant)
if linkType == "" {
@@ -1809,10 +1725,22 @@
addSdkMemberPropertiesToSet(ctx, imageInfo.Properties, propertySet)
+ usedLinkages := make(map[string]bool, len(imageInfo.linkInfos))
for _, linkInfo := range imageInfo.linkInfos {
+ usedLinkages[linkInfo.linkType] = true
linkInfo.addToPropertySet(ctx, propertySet)
}
+ // If not all supported linkages had existing variants, we need to disable the unsupported variant
+ if len(imageInfo.linkInfos) < len(ctx.MemberType().SupportedLinkages()) {
+ for _, l := range ctx.MemberType().SupportedLinkages() {
+ if _, ok := usedLinkages[l]; !ok {
+ otherLinkagePropertySet := propertySet.AddPropertySet(l)
+ otherLinkagePropertySet.AddProperty("enabled", false)
+ }
+ }
+ }
+
// If this is for a non-core image variant then make sure that the property set does not contain
// any properties as providing non-core image variant specific properties for prebuilts is not
// currently supported.
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 2ab784d..4de0144 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -149,6 +149,9 @@
// Only available for host sh_test modules.
Data_device_libs []string `android:"path,arch_variant"`
+ // Install the test into a folder named for the module in all test suites.
+ Per_testcase_directory *bool
+
// Test options.
Test_options TestOptions
}
@@ -320,7 +323,7 @@
ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...)
ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...),
shTestDataLibsTag, s.testProperties.Data_libs...)
- if (ctx.Target().Os.Class == android.Host || ctx.BazelConversionMode()) && len(ctx.Config().Targets[android.Android]) > 0 {
+ if ctx.Target().Os.Class == android.Host && len(ctx.Config().Targets[android.Android]) > 0 {
deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...)
ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...),
@@ -458,9 +461,13 @@
dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
}
+ if s.testProperties.Data_bins != nil {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
+ }
if Bool(s.testProperties.Test_options.Unit_test) {
entries.SetBool("LOCAL_IS_UNIT_TEST", true)
}
+ entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
},
},
}}
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 7fe1d85..89b8126 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -37,6 +37,8 @@
//
// deprecated
func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+
result := prepareForShTest.RunTestWithBp(t, bp)
return result.TestContext, result.Config
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index 6b4e12b..b04657d 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -68,6 +68,12 @@
registerHostSnapshotComponents(android.InitRegistrationContext)
}
+// Add prebuilt information to snapshot data
+type hostSnapshotFakeJsonFlags struct {
+ SnapshotJsonFlags
+ Prebuilt bool `json:",omitempty"`
+}
+
func registerHostSnapshotComponents(ctx android.RegistrationContext) {
ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
}
@@ -94,7 +100,9 @@
// Find all host binary modules add 'fake' versions to snapshot
var outputs android.Paths
seen := make(map[string]bool)
- var jsonData []SnapshotJsonFlags
+ var jsonData []hostSnapshotFakeJsonFlags
+ prebuilts := make(map[string]bool)
+
ctx.VisitAllModules(func(module android.Module) {
if module.Target().Os != ctx.Config().BuildOSTarget.Os {
return
@@ -104,9 +112,10 @@
}
if android.IsModulePrebuilt(module) {
+ // Add non-prebuilt module name to map of prebuilts
+ prebuilts[android.RemoveOptionalPrebuiltPrefix(module.Name())] = true
return
}
-
if !module.Enabled() || module.IsHideFromMake() {
return
}
@@ -114,17 +123,23 @@
if !apexInfo.IsForPlatform() {
return
}
- path := hostBinToolPath(module)
+ path := hostToolPath(module)
if path.Valid() && path.String() != "" {
outFile := filepath.Join(c.snapshotDir, path.String())
if !seen[outFile] {
seen[outFile] = true
outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile))
- jsonData = append(jsonData, *hostBinJsonDesc(module))
+ jsonData = append(jsonData, hostSnapshotFakeJsonFlags{*hostJsonDesc(module), false})
}
}
})
-
+ // Update any module prebuilt information
+ for idx, _ := range jsonData {
+ if _, ok := prebuilts[jsonData[idx].ModuleName]; ok {
+ // Prebuilt exists for this module
+ jsonData[idx].Prebuilt = true
+ }
+ }
marsh, err := json.Marshal(jsonData)
if err != nil {
ctx.Errorf("host fake snapshot json marshal failure: %#v", err)
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 09a382e..9793218 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -19,6 +19,7 @@
"fmt"
"path/filepath"
"sort"
+ "strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -62,6 +63,11 @@
installDir android.InstallPath
}
+type ProcMacro interface {
+ ProcMacro() bool
+ CrateName() string
+}
+
func hostSnapshotFactory() android.Module {
module := &hostSnapshot{}
initHostToolsModule(module)
@@ -94,7 +100,7 @@
// Create JSON file based on the direct dependencies
ctx.VisitDirectDeps(func(dep android.Module) {
- desc := hostBinJsonDesc(dep)
+ desc := hostJsonDesc(dep)
if desc != nil {
jsonData = append(jsonData, *desc)
}
@@ -145,7 +151,7 @@
f.installDir = android.PathForModuleInstall(ctx)
- f.CopyDepsToZip(ctx, depsZipFile)
+ f.CopyDepsToZip(ctx, f.GatherPackagingSpecs(ctx), depsZipFile)
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().
@@ -183,7 +189,7 @@
}
// Get host tools path and relative install string helpers
-func hostBinToolPath(m android.Module) android.OptionalPath {
+func hostToolPath(m android.Module) android.OptionalPath {
if provider, ok := m.(android.HostToolProvider); ok {
return provider.HostToolPath()
}
@@ -198,18 +204,30 @@
return outString
}
-// Create JSON description for given module, only create descriptions for binary modueles which
-// provide a valid HostToolPath
-func hostBinJsonDesc(m android.Module) *SnapshotJsonFlags {
- path := hostBinToolPath(m)
+// Create JSON description for given module, only create descriptions for binary modules
+// and rust_proc_macro modules which provide a valid HostToolPath
+func hostJsonDesc(m android.Module) *SnapshotJsonFlags {
+ path := hostToolPath(m)
relPath := hostRelativePathString(m)
+ procMacro := false
+ moduleStem := filepath.Base(path.String())
+ crateName := ""
+
+ if pm, ok := m.(ProcMacro); ok && pm.ProcMacro() {
+ procMacro = pm.ProcMacro()
+ moduleStem = strings.TrimSuffix(moduleStem, filepath.Ext(moduleStem))
+ crateName = pm.CrateName()
+ }
+
if path.Valid() && path.String() != "" {
return &SnapshotJsonFlags{
ModuleName: m.Name(),
- ModuleStemName: filepath.Base(path.String()),
+ ModuleStemName: moduleStem,
Filename: path.String(),
Required: append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
RelativeInstallPath: relPath,
+ RustProcMacro: procMacro,
+ CrateName: crateName,
}
}
return nil
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 79d3cf6..4a14f2e 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -114,6 +114,8 @@
RelativeInstallPath string `json:",omitempty"`
Filename string `json:",omitempty"`
ModuleStemName string `json:",omitempty"`
+ RustProcMacro bool `json:",omitempty"`
+ CrateName string `json:",omitempty"`
// dependencies
Required []string `json:",omitempty"`
diff --git a/soong_ui.bash b/soong_ui.bash
index c1c236b..49c4b78 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -53,6 +53,8 @@
source ${TOP}/build/soong/scripts/microfactory.bash
soong_build_go soong_ui android/soong/cmd/soong_ui
+soong_build_go mk2rbc android/soong/mk2rbc/cmd
+soong_build_go rbcrun rbcrun/cmd
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"
diff --git a/starlark_fmt/Android.bp b/starlark_fmt/Android.bp
new file mode 100644
index 0000000..8d80ccd
--- /dev/null
+++ b/starlark_fmt/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-starlark-format",
+ pkgPath: "android/soong/starlark_fmt",
+ srcs: [
+ "format.go",
+ ],
+ testSrcs: [
+ "format_test.go",
+ ],
+}
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
new file mode 100644
index 0000000..3e51fa1
--- /dev/null
+++ b/starlark_fmt/format.go
@@ -0,0 +1,101 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+const (
+ indent = 4
+)
+
+// Indention returns an indent string of the specified level.
+func Indention(level int) string {
+ if level < 0 {
+ panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
+ }
+ return strings.Repeat(" ", level*indent)
+}
+
+// PrintBool returns a Starlark compatible bool string.
+func PrintBool(item bool) string {
+ return strings.Title(fmt.Sprintf("%t", item))
+}
+
+// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
+func PrintStringList(items []string, indentLevel int) string {
+ return PrintList(items, indentLevel, func(s string) string {
+ if strings.Contains(s, "\"") {
+ return `'''%s'''`
+ }
+ return `"%s"`
+ })
+}
+
+// PrintList returns a Starlark-compatible string of list formmated as requested.
+func PrintList(items []string, indentLevel int, formatString func(string) string) string {
+ if len(items) == 0 {
+ return "[]"
+ } else if len(items) == 1 {
+ return fmt.Sprintf("["+formatString(items[0])+"]", items[0])
+ }
+ list := make([]string, 0, len(items)+2)
+ list = append(list, "[")
+ innerIndent := Indention(indentLevel + 1)
+ for _, item := range items {
+ list = append(list, fmt.Sprintf(`%s`+formatString(item)+`,`, innerIndent, item))
+ }
+ list = append(list, Indention(indentLevel)+"]")
+ return strings.Join(list, "\n")
+}
+
+// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and list of string values.
+func PrintStringListDict(dict map[string][]string, indentLevel int) string {
+ formattedValueDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ formattedValueDict[k] = PrintStringList(v, indentLevel+1)
+ }
+ return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintBoolDict(dict map[string]bool, indentLevel int) string {
+ formattedValueDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ formattedValueDict[k] = PrintBool(v)
+ }
+ return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintDict(dict map[string]string, indentLevel int) string {
+ if len(dict) == 0 {
+ return "{}"
+ }
+ items := make([]string, 0, len(dict))
+ for k, v := range dict {
+ items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
+ }
+ sort.Strings(items)
+ return fmt.Sprintf(`{
+%s
+%s}`, strings.Join(items, "\n"), Indention(indentLevel))
+}
diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go
new file mode 100644
index 0000000..9450a31
--- /dev/null
+++ b/starlark_fmt/format_test.go
@@ -0,0 +1,173 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+ "testing"
+)
+
+func simpleFormat(s string) string {
+ return "%s"
+}
+
+func TestPrintEmptyStringList(t *testing.T) {
+ in := []string{}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := "[]"
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintSingleElementStringList(t *testing.T) {
+ in := []string{"a"}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := `["a"]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintMultiElementStringList(t *testing.T) {
+ in := []string{"a", "b"}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := `[
+ "a",
+ "b",
+]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintEmptyList(t *testing.T) {
+ in := []string{}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, simpleFormat)
+ expectedOut := "[]"
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintSingleElementList(t *testing.T) {
+ in := []string{"1"}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, simpleFormat)
+ expectedOut := `[1]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintMultiElementList(t *testing.T) {
+ in := []string{"1", "2"}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, simpleFormat)
+ expectedOut := `[
+ 1,
+ 2,
+]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestListWithNonZeroIndent(t *testing.T) {
+ in := []string{"1", "2"}
+ indentLevel := 1
+ out := PrintList(in, indentLevel, simpleFormat)
+ expectedOut := `[
+ 1,
+ 2,
+ ]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestStringListDictEmpty(t *testing.T) {
+ in := map[string][]string{}
+ indentLevel := 0
+ out := PrintStringListDict(in, indentLevel)
+ expectedOut := `{}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestStringListDict(t *testing.T) {
+ in := map[string][]string{
+ "key1": []string{},
+ "key2": []string{"a"},
+ "key3": []string{"1", "2"},
+ }
+ indentLevel := 0
+ out := PrintStringListDict(in, indentLevel)
+ expectedOut := `{
+ "key1": [],
+ "key2": ["a"],
+ "key3": [
+ "1",
+ "2",
+ ],
+}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintDict(t *testing.T) {
+ in := map[string]string{
+ "key1": `""`,
+ "key2": `"a"`,
+ "key3": `[
+ 1,
+ 2,
+ ]`,
+ }
+ indentLevel := 0
+ out := PrintDict(in, indentLevel)
+ expectedOut := `{
+ "key1": "",
+ "key2": "a",
+ "key3": [
+ 1,
+ 2,
+ ],
+}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintDictWithIndent(t *testing.T) {
+ in := map[string]string{
+ "key1": `""`,
+ "key2": `"a"`,
+ }
+ indentLevel := 1
+ out := PrintDict(in, indentLevel)
+ expectedOut := `{
+ "key1": "",
+ "key2": "a",
+ }`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index f935f06..88ef615 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -98,9 +98,6 @@
"build/soong/scripts/jar-wrapper.sh": nil,
- "build/make/core/proguard.flags": nil,
- "build/make/core/proguard_basic_keeps.flags": nil,
-
"jdk8/jre/lib/jce.jar": nil,
"jdk8/jre/lib/rt.jar": nil,
"jdk8/lib/tools.jar": nil,
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 4f37c2b..78ddced 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -115,3 +115,83 @@
}
test_bp2build_generates_all_buildfiles
+
+function test_cc_correctness {
+ setup
+ create_mock_bazel
+
+ mkdir -p a
+ cat > a/Android.bp <<EOF
+cc_object {
+ name: "qq",
+ srcs: ["qq.cc"],
+ bazel_module: {
+ bp2build_available: true,
+ },
+ stl: "none",
+ system_shared_libs: [],
+}
+EOF
+
+ cat > a/qq.cc <<EOF
+#include "qq.h"
+int qq() {
+ return QQ;
+}
+EOF
+
+ cat > a/qq.h <<EOF
+#define QQ 1
+EOF
+
+ run_soong bp2build
+
+ run_bazel build --package_path=out/soong/workspace //a:qq
+ local output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+
+ run_bazel build --package_path=out/soong/workspace //a:qq
+ local output_mtime2=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+
+ if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+ fail "output changed on null build"
+ fi
+
+ cat > a/qq.h <<EOF
+#define QQ 2
+EOF
+
+ run_bazel build --package_path=out/soong/workspace //a:qq
+ local output_mtime3=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+
+ if [[ "$output_mtime1" == "$output_mtime3" ]]; then
+ fail "output not changed when included header changed"
+ fi
+}
+
+test_cc_correctness
+
+# Regression test for the following failure during symlink forest creation:
+#
+# Cannot stat '/tmp/st.rr054/foo/bar/unresolved_symlink': stat /tmp/st.rr054/foo/bar/unresolved_symlink: no such file or directory
+#
+function test_bp2build_null_build_with_unresolved_symlink_in_source() {
+ setup
+
+ mkdir -p foo/bar
+ ln -s /tmp/non-existent foo/bar/unresolved_symlink
+ cat > foo/bar/Android.bp <<'EOF'
+filegroup {
+ name: "fg",
+ srcs: ["unresolved_symlink/non-existent-file.txt"],
+ }
+EOF
+
+ run_soong bp2build
+
+ dest=$(readlink -f out/soong/workspace/foo/bar/unresolved_symlink)
+ if [[ "$dest" != "/tmp/non-existent" ]]; then
+ fail "expected to plant an unresolved symlink out/soong/workspace/foo/bar/unresolved_symlink that resolves to /tmp/non-existent"
+ fi
+}
+
+test_bp2build_null_build_with_unresolved_symlink_in_source
diff --git a/tests/lib.sh b/tests/lib.sh
index e6074f8..abe84d3 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -83,11 +83,15 @@
function create_mock_soong {
copy_directory build/blueprint
copy_directory build/soong
+ copy_directory build/make/tools/rbcrun
+ symlink_directory prebuilts/sdk
symlink_directory prebuilts/go
symlink_directory prebuilts/build-tools
+ symlink_directory prebuilts/clang/host
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
+ symlink_directory external/starlark-go
touch "$MOCK_TOP/Android.bp"
}
@@ -112,8 +116,10 @@
copy_directory build/bazel
symlink_directory prebuilts/bazel
+ symlink_directory prebuilts/clang
symlink_directory prebuilts/jdk
symlink_directory external/bazel-skylib
+ symlink_directory external/bazelbuild-rules_android
symlink_file WORKSPACE
symlink_file BUILD
@@ -121,6 +127,10 @@
}
run_bazel() {
+ # 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 -rf out/ninja_build
+
tools/bazel "$@"
}
@@ -133,4 +143,5 @@
export ALLOW_MISSING_DEPENDENCIES=true
+export ALLOW_BP_UNDER_SYMLINKS=true
warmup_mock_top
diff --git a/ui/build/build.go b/ui/build/build.go
index 2e44aaa..aadf4af 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -18,6 +18,7 @@
"io/ioutil"
"os"
"path/filepath"
+ "sync"
"text/template"
"android/soong/ui/metrics"
@@ -205,6 +206,8 @@
return
}
+ defer waitForDist(ctx)
+
// checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
checkProblematicFiles(ctx)
@@ -264,6 +267,7 @@
if config.StartRBE() {
startRBE(ctx, config)
+ defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
}
if what&RunProductConfig != 0 {
@@ -328,8 +332,18 @@
}
}
+var distWaitGroup sync.WaitGroup
+
+// waitForDist waits for all backgrounded distGzipFile and distFile writes to finish
+func waitForDist(ctx Context) {
+ ctx.BeginTrace("soong_ui", "dist")
+ defer ctx.EndTrace()
+
+ distWaitGroup.Wait()
+}
+
// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
-// are printed but non-fatal.
+// are printed but non-fatal. Uses the distWaitGroup func for backgrounding (optimization).
func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
if !config.Dist() {
return
@@ -342,13 +356,17 @@
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
}
- if err := gzipFileToDir(src, destDir); err != nil {
- ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
- }
+ distWaitGroup.Add(1)
+ go func() {
+ defer distWaitGroup.Done()
+ if err := gzipFileToDir(src, destDir); err != nil {
+ ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
+ }
+ }()
}
// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
-// non-fatal.
+// non-fatal. Uses the distWaitGroup func for backgrounding (optimization).
func distFile(ctx Context, config Config, src string, subDirs ...string) {
if !config.Dist() {
return
@@ -361,7 +379,11 @@
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
}
- if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
- ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
- }
+ distWaitGroup.Add(1)
+ go func() {
+ defer distWaitGroup.Done()
+ if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
+ ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
+ }
+ }()
}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index a3a1aaf..1c80cff 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -171,6 +171,7 @@
productOut("recovery"),
productOut("root"),
productOut("system"),
+ productOut("system_dlkm"),
productOut("system_other"),
productOut("vendor"),
productOut("vendor_dlkm"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 1dd948c..0092ff1 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -35,10 +35,10 @@
)
const (
- envConfigDir = "vendor/google/tools/soong_config"
- jsonSuffix = "json"
+ envConfigDir = "vendor/google/tools/soong_config"
+ jsonSuffix = "json"
- configFetcher = "vendor/google/tools/soong/expconfigfetcher"
+ configFetcher = "vendor/google/tools/soong/expconfigfetcher"
envConfigFetchTimeout = 10 * time.Second
)
@@ -62,6 +62,7 @@
jsonModuleGraph bool
bp2build bool
queryview bool
+ reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
soongDocs bool
skipConfig bool
skipKati bool
@@ -143,6 +144,12 @@
// fetchEnvConfig optionally fetches environment config from an
// experiments system to control Soong features dynamically.
func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
+ configName := envConfigName + "." + jsonSuffix
+ expConfigFetcher := &smpb.ExpConfigFetcher{}
+ defer func() {
+ ctx.Metrics.ExpConfigFetcher(expConfigFetcher)
+ }()
+
s, err := os.Stat(configFetcher)
if err != nil {
if os.IsNotExist(err) {
@@ -151,31 +158,38 @@
return err
}
if s.Mode()&0111 == 0 {
+ status := smpb.ExpConfigFetcher_ERROR
+ expConfigFetcher.Status = &status
return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
}
- configExists := false
- outConfigFilePath := filepath.Join(config.OutDir(), envConfigName + jsonSuffix)
- if _, err := os.Stat(outConfigFilePath); err == nil {
- configExists = true
- }
-
tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
defer cancel()
- cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir())
+ fetchStart := time.Now()
+ cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
+ "-output_config_name", configName)
if err := cmd.Start(); err != nil {
+ status := smpb.ExpConfigFetcher_ERROR
+ expConfigFetcher.Status = &status
return err
}
- // If a config file already exists, return immediately and run the config file
- // fetch in the background. Otherwise, wait for the config file to be fetched.
- if configExists {
- go cmd.Wait()
- return nil
- }
if err := cmd.Wait(); err != nil {
+ status := smpb.ExpConfigFetcher_ERROR
+ expConfigFetcher.Status = &status
return err
}
+ fetchEnd := time.Now()
+ expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
+ outConfigFilePath := filepath.Join(config.OutDir(), configName)
+ expConfigFetcher.Filename = proto.String(outConfigFilePath)
+ if _, err := os.Stat(outConfigFilePath); err == nil {
+ status := smpb.ExpConfigFetcher_CONFIG
+ expConfigFetcher.Status = &status
+ } else {
+ status := smpb.ExpConfigFetcher_NO_CONFIG
+ expConfigFetcher.Status = &status
+ }
return nil
}
@@ -186,7 +200,7 @@
}
if err := fetchEnvConfig(ctx, config, bc); err != nil {
- fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v", err)
+ fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v\n", err)
}
configDirs := []string{
@@ -367,10 +381,14 @@
java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag())
java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag())
java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag())
+ java17Home := filepath.Join("prebuilts/jdk/jdk17", ret.HostPrebuiltTag())
javaHome := func() string {
if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
return override
}
+ if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
+ return java17Home
+ }
if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
}
@@ -711,6 +729,8 @@
c.skipConfig = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
+ } else if arg == "--mk-metrics" {
+ c.reportMkMetrics = true
} else if len(arg) > 0 && arg[0] == '-' {
parseArgNum := func(def int) int {
if len(arg) > 2 {
@@ -1184,7 +1204,12 @@
}
func (c *configImpl) rbeAuth() (string, string) {
- credFlags := []string{"use_application_default_credentials", "use_gce_credentials", "credential_file"}
+ credFlags := []string{
+ "use_application_default_credentials",
+ "use_gce_credentials",
+ "credential_file",
+ "use_google_prod_creds",
+ }
for _, cf := range credFlags {
for _, f := range []string{"RBE_" + cf, "FLAG_" + cf} {
if v, ok := c.environ.Get(f); ok {
@@ -1198,6 +1223,21 @@
return "RBE_use_application_default_credentials", "true"
}
+func (c *configImpl) IsGooglerEnvironment() bool {
+ cf := "ANDROID_BUILD_ENVIRONMENT_CONFIG"
+ if v, ok := c.environ.Get(cf); ok {
+ return v == "googler"
+ }
+ return false
+}
+
+func (c *configImpl) GoogleProdCredsExist() bool {
+ if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
+ return false
+ }
+ return true
+}
+
func (c *configImpl) UseRemoteBuild() bool {
return c.UseGoma() || c.UseRBE()
}
@@ -1381,6 +1421,11 @@
return filepath.Join(c.LogsDir(), "bazel_metrics")
}
+// MkFileMetrics returns the file path for make-related metrics.
+func (c *configImpl) MkMetrics() string {
+ return filepath.Join(c.LogsDir(), "mk_metrics.pb")
+}
+
func (c *configImpl) SetEmptyNinjaFile(v bool) {
c.emptyNinjaFile = v
}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 3f10f75..f56964c 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -205,6 +205,9 @@
"CCACHE_SLOPPINESS",
"CCACHE_BASEDIR",
"CCACHE_CPP2",
+
+ // LLVM compiler wrapper options
+ "TOOLCHAIN_RUSAGE_OUTPUT",
}
allVars := append(append([]string{
@@ -242,8 +245,6 @@
"BUILD_BROKEN_USES_BUILD_EXECUTABLE",
"BUILD_BROKEN_USES_BUILD_FUZZ_TEST",
"BUILD_BROKEN_USES_BUILD_HEADER_LIBRARY",
- "BUILD_BROKEN_USES_BUILD_HOST_DALVIK_JAVA_LIBRARY",
- "BUILD_BROKEN_USES_BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY",
"BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE",
"BUILD_BROKEN_USES_BUILD_HOST_JAVA_LIBRARY",
"BUILD_BROKEN_USES_BUILD_HOST_PREBUILT",
@@ -262,12 +263,6 @@
"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
}, exportEnvVars...), BannerVars...)
- // We need Roboleaf converter and runner in the mixed mode
- runMicrofactory(ctx, config, "mk2rbc", "android/soong/mk2rbc/cmd",
- map[string]string{"android/soong": "build/soong"})
- runMicrofactory(ctx, config, "rbcrun", "rbcrun/cmd",
- map[string]string{"go.starlark.net": "external/starlark-go", "rbcrun": "build/make/tools/rbcrun"})
-
makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
if err != nil {
ctx.Fatalln("Error dumping make vars:", err)
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 8f74969..4d6ad42 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -64,6 +64,7 @@
cacheParams := finder.CacheParams{
WorkingDirectory: dir,
RootDirs: []string{"."},
+ FollowSymlinks: config.environ.IsEnvTrue("ALLOW_BP_UNDER_SYMLINKS"),
ExcludeDirs: []string{".git", ".repo"},
PruneFiles: pruneFiles,
IncludeFiles: []string{
@@ -87,8 +88,8 @@
// Bazel top-level file to mark a directory as a Bazel workspace.
"WORKSPACE",
},
- // Bazel Starlark configuration files.
- IncludeSuffixes: []string{".bzl"},
+ // Bazel Starlark configuration files and all .mk files for product/board configuration.
+ IncludeSuffixes: []string{".bzl", ".mk"},
}
dumpDir := config.FileListDir()
f, err = finder.New(cacheParams, filesystem, logger.New(ioutil.Discard),
@@ -110,6 +111,19 @@
return entries.DirNames, matches
}
+func findProductAndBoardConfigFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
+ matches := []string{}
+ for _, foundName := range entries.FileNames {
+ if foundName != "Android.mk" &&
+ foundName != "AndroidProducts.mk" &&
+ foundName != "CleanSpec.mk" &&
+ strings.HasSuffix(foundName, ".mk") {
+ matches = append(matches, foundName)
+ }
+ }
+ return entries.DirNames, matches
+}
+
// FindSources searches for source files known to <f> and writes them to the filesystem for
// use later.
func FindSources(ctx Context, config Config, f *finder.Finder) {
@@ -125,6 +139,17 @@
ctx.Fatalf("Could not export module list: %v", err)
}
+ // Gate collecting/reporting mk metrics on builds that specifically request
+ // it, as identifying the total number of mk files adds 4-5ms onto null
+ // builds.
+ if config.reportMkMetrics {
+ androidMksTotal := f.FindNamedAt(".", "Android.mk")
+
+ ctx.Metrics.SetToplevelMakefiles(len(androidMks))
+ ctx.Metrics.SetTotalMakefiles(len(androidMksTotal))
+ ctx.Metrics.DumpMkMetrics(config.MkMetrics())
+ }
+
// Stop searching a subdirectory recursively after finding a CleanSpec.mk.
cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
@@ -172,6 +197,13 @@
ctx.Fatalf("Could not find modules: %v", err)
}
+ // Recursively look for all product/board config files.
+ configurationFiles := f.FindMatching(".", findProductAndBoardConfigFiles)
+ err = dumpListToFile(ctx, config, configurationFiles, filepath.Join(dumpDir, "configuration.list"))
+ if err != nil {
+ ctx.Fatalf("Could not export product/board configuration list: %v", err)
+ }
+
if config.Dist() {
f.WaitForDbDump()
// Dist the files.db plain text database.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 41de6bd..dab1a9b 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -169,6 +169,9 @@
"CCACHE_BASEDIR",
"CCACHE_CPP2",
"CCACHE_DIR",
+
+ // LLVM compiler wrapper options
+ "TOOLCHAIN_RUSAGE_OUTPUT",
}, config.BuildBrokenNinjaUsesEnvVars()...)...)
}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 81c500d..b3092ea 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -31,18 +31,25 @@
LinuxOnlyPrebuilt bool
}
+// These binaries can be run from $PATH, nonhermetically. There should be as
+// few as possible of these, since this means that the build depends on tools
+// that are not shipped in the source tree and whose behavior is therefore
+// unpredictable.
var Allowed = PathConfig{
Symlink: true,
Log: false,
Error: false,
}
+// This tool is specifically disallowed and calling it will result in an
+// "executable no found" error.
var Forbidden = PathConfig{
Symlink: false,
Log: true,
Error: true,
}
+// This tool is allowed, but access to it will be logged.
var Log = PathConfig{
Symlink: true,
Log: true,
@@ -52,13 +59,16 @@
// The configuration used if the tool is not listed in the config below.
// Currently this will create the symlink, but log and error when it's used. In
// the future, I expect the symlink to be removed, and this will be equivalent
-// to Forbidden.
+// to Forbidden. This applies to every tool not specifically mentioned in the
+// configuration.
var Missing = PathConfig{
Symlink: true,
Log: true,
Error: true,
}
+// This is used for binaries for which we have prebuilt versions, but only for
+// Linux. Thus, their execution from $PATH is only allowed on Mac OS.
var LinuxOnlyPrebuilt = PathConfig{
Symlink: false,
Log: true,
@@ -73,6 +83,8 @@
return Missing
}
+// This list specifies whether a particular binary from $PATH is allowed to be
+// run during the build. For more documentation, see path_interposer.go .
var Configuration = map[string]PathConfig{
"bash": Allowed,
"dd": Allowed,
@@ -91,6 +103,7 @@
"pstree": Allowed,
"rsync": Allowed,
"sh": Allowed,
+ "stubby": Allowed,
"tr": Allowed,
"unzip": Allowed,
"zip": Allowed,
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 8f9a699..78d37b4 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -119,6 +119,7 @@
}
func stopRBE(ctx Context, config Config) {
+ defer checkProdCreds(ctx, config)
cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
output, err := cmd.CombinedOutput()
if err != nil {
@@ -131,6 +132,15 @@
}
}
+func checkProdCreds(ctx Context, config Config) {
+ if !config.IsGooglerEnvironment() || config.GoogleProdCredsExist() {
+ return
+ }
+ fmt.Fprintln(ctx.Writer, "")
+ fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing RBE builds in the future, see go/build-fast#authentication.\033[0m")
+ fmt.Fprintln(ctx.Writer, "")
+}
+
// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
// The protobuf file is created if RBE is enabled and the proxy service has
// started. The proxy service is shutdown in order to dump the RBE metrics to the
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 117a2a5..8992b4f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,7 +15,9 @@
package build
import (
+ "errors"
"fmt"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
@@ -491,10 +493,14 @@
ninja("bootstrap", "bootstrap.ninja", targets...)
- var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
if shouldCollectBuildSoongMetrics(config) {
soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
- logSoongBuildMetrics(ctx, soongBuildMetrics)
+ if soongBuildMetrics != nil {
+ logSoongBuildMetrics(ctx, soongBuildMetrics)
+ if ctx.Metrics != nil {
+ ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
+ }
+ }
}
distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
@@ -504,8 +510,8 @@
distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
}
- if shouldCollectBuildSoongMetrics(config) && ctx.Metrics != nil {
- ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
+ if config.JsonModuleGraph() {
+ distGzipFile(ctx, config, config.ModuleGraphFile(), "soong")
}
}
@@ -534,9 +540,13 @@
}
func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
- soongBuildMetricsFile := filepath.Join(config.OutDir(), "soong", "soong_build_metrics.pb")
- buf, err := ioutil.ReadFile(soongBuildMetricsFile)
- if err != nil {
+ soongBuildMetricsFile := filepath.Join(config.LogsDir(), "soong_build_metrics.pb")
+ buf, err := os.ReadFile(soongBuildMetricsFile)
+ if errors.Is(err, fs.ErrNotExist) {
+ // Soong may not have run during this invocation
+ ctx.Verbosef("Failed to read metrics file, %s: %s", soongBuildMetricsFile, err)
+ return nil
+ } else if err != nil {
ctx.Fatalf("Failed to load %s: %s", soongBuildMetricsFile, err)
}
soongBuildMetrics := &soong_metrics_proto.SoongBuildMetrics{}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 3ba3907..05db1d7 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -21,9 +21,10 @@
pkgPath: "android/soong/ui/metrics",
deps: [
"golang-protobuf-proto",
+ "soong-ui-bp2build_metrics_proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
- "soong-ui-bp2build_metrics_proto",
+ "soong-ui-mk_metrics_proto",
"soong-ui-tracer",
"soong-shared",
],
@@ -71,3 +72,15 @@
"bp2build_metrics_proto/bp2build_metrics.pb.go",
],
}
+
+bootstrap_go_package {
+ name: "soong-ui-mk_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "mk_metrics_proto/mk_metrics.pb.go",
+ ],
+}
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 95f02ca..93f3471 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -53,6 +53,9 @@
ConvertedModuleTypeCount map[string]uint64 `protobuf:"bytes,6,rep,name=convertedModuleTypeCount,proto3" json:"convertedModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
// Counts of total modules by module type.
TotalModuleTypeCount map[string]uint64 `protobuf:"bytes,7,rep,name=totalModuleTypeCount,proto3" json:"totalModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+ // List of traced runtime events of bp2build, useful for tracking bp2build
+ // runtime.
+ Events []*Event `protobuf:"bytes,8,rep,name=events,proto3" json:"events,omitempty"`
}
func (x *Bp2BuildMetrics) Reset() {
@@ -136,13 +139,89 @@
return nil
}
+func (x *Bp2BuildMetrics) GetEvents() []*Event {
+ if x != nil {
+ return x.Events
+ }
+ return nil
+}
+
+// Traced runtime event of bp2build.
+type Event struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The event name.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // The absolute start time of the event
+ // The number of nanoseconds elapsed since January 1, 1970 UTC.
+ StartTime uint64 `protobuf:"varint,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`
+ // The real running time.
+ // The number of nanoseconds elapsed since start_time.
+ RealTime uint64 `protobuf:"varint,3,opt,name=real_time,json=realTime,proto3" json:"real_time,omitempty"`
+}
+
+func (x *Event) Reset() {
+ *x = Event{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_bp2build_metrics_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Event) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Event) ProtoMessage() {}
+
+func (x *Event) ProtoReflect() protoreflect.Message {
+ mi := &file_bp2build_metrics_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Event.ProtoReflect.Descriptor instead.
+func (*Event) Descriptor() ([]byte, []int) {
+ return file_bp2build_metrics_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Event) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Event) GetStartTime() uint64 {
+ if x != nil {
+ return x.StartTime
+ }
+ return 0
+}
+
+func (x *Event) GetRealTime() uint64 {
+ if x != nil {
+ return x.RealTime
+ }
+ return 0
+}
+
var File_bp2build_metrics_proto protoreflect.FileDescriptor
var file_bp2build_metrics_proto_rawDesc = []byte{
0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
- 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xac, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xe9, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
@@ -179,24 +258,34 @@
0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74,
0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64,
- 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x41, 0x0a, 0x13,
- 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e,
- 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a,
- 0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
- 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06,
+ 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73,
+ 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75,
+ 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e,
+ 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c,
+ 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19,
- 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43,
- 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
- 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
- 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
- 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d,
+ 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54,
+ 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74,
+ 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
+ 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+ 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
+ 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61,
+ 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f,
+ 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
+ 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -211,22 +300,24 @@
return file_bp2build_metrics_proto_rawDescData
}
-var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_bp2build_metrics_proto_goTypes = []interface{}{
(*Bp2BuildMetrics)(nil), // 0: soong_build_bp2build_metrics.Bp2BuildMetrics
- nil, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
- nil, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
- nil, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+ (*Event)(nil), // 1: soong_build_bp2build_metrics.Event
+ nil, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ nil, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+ nil, // 4: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
}
var file_bp2build_metrics_proto_depIdxs = []int32{
- 1, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
- 2, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
- 3, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
- 3, // [3:3] is the sub-list for method output_type
- 3, // [3:3] is the sub-list for method input_type
- 3, // [3:3] is the sub-list for extension type_name
- 3, // [3:3] is the sub-list for extension extendee
- 0, // [0:3] is the sub-list for field type_name
+ 2, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ 3, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+ 4, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+ 1, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.events:type_name -> soong_build_bp2build_metrics.Event
+ 4, // [4:4] is the sub-list for method output_type
+ 4, // [4:4] is the sub-list for method input_type
+ 4, // [4:4] is the sub-list for extension type_name
+ 4, // [4:4] is the sub-list for extension extendee
+ 0, // [0:4] is the sub-list for field type_name
}
func init() { file_bp2build_metrics_proto_init() }
@@ -247,6 +338,18 @@
return nil
}
}
+ file_bp2build_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Event); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -254,7 +357,7 @@
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_bp2build_metrics_proto_rawDesc,
NumEnums: 0,
- NumMessages: 4,
+ NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 6d98a3d..19a7827 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -38,4 +38,22 @@
// Counts of total modules by module type.
map<string, uint64> totalModuleTypeCount = 7;
+
+ // List of traced runtime events of bp2build, useful for tracking bp2build
+ // runtime.
+ repeated Event events = 8;
+}
+
+// Traced runtime event of bp2build.
+message Event {
+ // The event name.
+ string name = 1;
+
+ // The absolute start time of the event
+ // The number of nanoseconds elapsed since January 1, 1970 UTC.
+ uint64 start_time = 2;
+
+ // The real running time.
+ // The number of nanoseconds elapsed since start_time.
+ uint64 real_time = 3;
}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 80f8c1a..0c62865 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -38,9 +38,11 @@
"time"
"android/soong/shared"
+
"google.golang.org/protobuf/proto"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+ mk_metrics_proto "android/soong/ui/metrics/mk_metrics_proto"
)
const (
@@ -62,14 +64,22 @@
Total = "total"
)
-// Metrics is a struct that stores collected metrics during the course
-// of a build which later is dumped to a MetricsBase protobuf file.
-// See ui/metrics/metrics_proto/metrics.proto for further details
-// on what information is collected.
+// Metrics is a struct that stores collected metrics during the course of a
+// build. It is later dumped to protobuf files. See underlying metrics protos
+// for further details on what information is collected.
type Metrics struct {
- // The protobuf message that is later written to the file.
+ // Protobuf containing various top-level build metrics. These include:
+ // 1. Build identifiers (ex: branch ID, requested product, hostname,
+ // originating command)
+ // 2. Per-subprocess top-level metrics (ex: ninja process IO and runtime).
+ // Note that, since these metrics are reported by soong_ui, there is little
+ // insight that can be provided into performance breakdowns of individual
+ // subprocesses.
metrics soong_metrics_proto.MetricsBase
+ // Protobuf containing metrics pertaining to number of makefiles in a build.
+ mkMetrics mk_metrics_proto.MkMetrics
+
// A list of pending build events.
EventTracer *EventTracer
}
@@ -78,11 +88,24 @@
func New() (metrics *Metrics) {
m := &Metrics{
metrics: soong_metrics_proto.MetricsBase{},
+ mkMetrics: mk_metrics_proto.MkMetrics{},
EventTracer: &EventTracer{},
}
return m
}
+func (m *Metrics) SetTotalMakefiles(total int) {
+ m.mkMetrics.TotalMakefiles = uint32(total)
+}
+
+func (m *Metrics) SetToplevelMakefiles(total int) {
+ m.mkMetrics.ToplevelMakefiles = uint32(total)
+}
+
+func (m *Metrics) DumpMkMetrics(outPath string) {
+ shared.Save(&m.mkMetrics, outPath)
+}
+
// SetTimeMetrics stores performance information from an executed block of
// code.
func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
@@ -113,6 +136,11 @@
m.metrics.SystemResourceInfo = b
}
+// ExpConfigFetcher stores information about the expconfigfetcher.
+func (m *Metrics) ExpConfigFetcher(b *soong_metrics_proto.ExpConfigFetcher) {
+ m.metrics.ExpConfigFetcher = b
+}
+
// SetMetadataMetrics sets information about the build such as the target
// product, host architecture and out directory.
func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 2e530b0..4bc713b 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -14,7 +14,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.27.1
+// protoc-gen-go v1.28.0
// protoc v3.9.1
// source: metrics.proto
@@ -217,6 +217,65 @@
return file_metrics_proto_rawDescGZIP(), []int{5, 0}
}
+type ExpConfigFetcher_ConfigStatus int32
+
+const (
+ ExpConfigFetcher_NO_CONFIG ExpConfigFetcher_ConfigStatus = 0
+ ExpConfigFetcher_CONFIG ExpConfigFetcher_ConfigStatus = 1
+ ExpConfigFetcher_ERROR ExpConfigFetcher_ConfigStatus = 2
+)
+
+// Enum value maps for ExpConfigFetcher_ConfigStatus.
+var (
+ ExpConfigFetcher_ConfigStatus_name = map[int32]string{
+ 0: "NO_CONFIG",
+ 1: "CONFIG",
+ 2: "ERROR",
+ }
+ ExpConfigFetcher_ConfigStatus_value = map[string]int32{
+ "NO_CONFIG": 0,
+ "CONFIG": 1,
+ "ERROR": 2,
+ }
+)
+
+func (x ExpConfigFetcher_ConfigStatus) Enum() *ExpConfigFetcher_ConfigStatus {
+ p := new(ExpConfigFetcher_ConfigStatus)
+ *p = x
+ return p
+}
+
+func (x ExpConfigFetcher_ConfigStatus) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ExpConfigFetcher_ConfigStatus) Descriptor() protoreflect.EnumDescriptor {
+ return file_metrics_proto_enumTypes[3].Descriptor()
+}
+
+func (ExpConfigFetcher_ConfigStatus) Type() protoreflect.EnumType {
+ return &file_metrics_proto_enumTypes[3]
+}
+
+func (x ExpConfigFetcher_ConfigStatus) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *ExpConfigFetcher_ConfigStatus) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = ExpConfigFetcher_ConfigStatus(num)
+ return nil
+}
+
+// Deprecated: Use ExpConfigFetcher_ConfigStatus.Descriptor instead.
+func (ExpConfigFetcher_ConfigStatus) EnumDescriptor() ([]byte, []int) {
+ return file_metrics_proto_rawDescGZIP(), []int{9, 0}
+}
+
type MetricsBase struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -274,6 +333,8 @@
BuildCommand *string `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
// The metrics for calling Bazel.
BazelRuns []*PerfInfo `protobuf:"bytes,27,rep,name=bazel_runs,json=bazelRuns" json:"bazel_runs,omitempty"`
+ // The metrics of the experiment config fetcher
+ ExpConfigFetcher *ExpConfigFetcher `protobuf:"bytes,28,opt,name=exp_config_fetcher,json=expConfigFetcher" json:"exp_config_fetcher,omitempty"`
}
// Default values for MetricsBase fields.
@@ -505,6 +566,13 @@
return nil
}
+func (x *MetricsBase) GetExpConfigFetcher() *ExpConfigFetcher {
+ if x != nil {
+ return x.ExpConfigFetcher
+ }
+ return nil
+}
+
type BuildConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -886,9 +954,9 @@
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- // The build system, eg. Soong or Make.
+ // The build system, e.g. Soong or Make.
BuildSystem *ModuleTypeInfo_BuildSystem `protobuf:"varint,1,opt,name=build_system,json=buildSystem,enum=soong_build_metrics.ModuleTypeInfo_BuildSystem,def=0" json:"build_system,omitempty"`
- // The module type, eg. java_library, cc_binary, and etc.
+ // The module type, e.g. java_library, cc_binary, and etc.
ModuleType *string `protobuf:"bytes,2,opt,name=module_type,json=moduleType" json:"module_type,omitempty"`
// The number of logical modules.
NumOfModules *uint32 `protobuf:"varint,3,opt,name=num_of_modules,json=numOfModules" json:"num_of_modules,omitempty"`
@@ -1072,6 +1140,10 @@
TotalAllocSize *uint64 `protobuf:"varint,4,opt,name=total_alloc_size,json=totalAllocSize" json:"total_alloc_size,omitempty"`
// The approximate maximum size of the heap in soong_build in bytes.
MaxHeapSize *uint64 `protobuf:"varint,5,opt,name=max_heap_size,json=maxHeapSize" json:"max_heap_size,omitempty"`
+ // Runtime metrics for soong_build execution.
+ Events []*PerfInfo `protobuf:"bytes,6,rep,name=events" json:"events,omitempty"`
+ // Mixed Builds information
+ MixedBuildsInfo *MixedBuildsInfo `protobuf:"bytes,7,opt,name=mixed_builds_info,json=mixedBuildsInfo" json:"mixed_builds_info,omitempty"`
}
func (x *SoongBuildMetrics) Reset() {
@@ -1141,12 +1213,152 @@
return 0
}
+func (x *SoongBuildMetrics) GetEvents() []*PerfInfo {
+ if x != nil {
+ return x.Events
+ }
+ return nil
+}
+
+func (x *SoongBuildMetrics) GetMixedBuildsInfo() *MixedBuildsInfo {
+ if x != nil {
+ return x.MixedBuildsInfo
+ }
+ return nil
+}
+
+type ExpConfigFetcher struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The result of the call to expconfigfetcher
+ // NO_CONFIG - Not part of experiment
+ // CONFIG - Part of experiment, config copied successfully
+ // ERROR - expconfigfetcher failed
+ Status *ExpConfigFetcher_ConfigStatus `protobuf:"varint,1,opt,name=status,enum=soong_build_metrics.ExpConfigFetcher_ConfigStatus" json:"status,omitempty"`
+ // The output config filename
+ Filename *string `protobuf:"bytes,2,opt,name=filename" json:"filename,omitempty"`
+ // Time, in microseconds, taken by the expconfigfetcher
+ Micros *uint64 `protobuf:"varint,3,opt,name=micros" json:"micros,omitempty"`
+}
+
+func (x *ExpConfigFetcher) Reset() {
+ *x = ExpConfigFetcher{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_metrics_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ExpConfigFetcher) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExpConfigFetcher) ProtoMessage() {}
+
+func (x *ExpConfigFetcher) ProtoReflect() protoreflect.Message {
+ mi := &file_metrics_proto_msgTypes[9]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExpConfigFetcher.ProtoReflect.Descriptor instead.
+func (*ExpConfigFetcher) Descriptor() ([]byte, []int) {
+ return file_metrics_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *ExpConfigFetcher) GetStatus() ExpConfigFetcher_ConfigStatus {
+ if x != nil && x.Status != nil {
+ return *x.Status
+ }
+ return ExpConfigFetcher_NO_CONFIG
+}
+
+func (x *ExpConfigFetcher) GetFilename() string {
+ if x != nil && x.Filename != nil {
+ return *x.Filename
+ }
+ return ""
+}
+
+func (x *ExpConfigFetcher) GetMicros() uint64 {
+ if x != nil && x.Micros != nil {
+ return *x.Micros
+ }
+ return 0
+}
+
+type MixedBuildsInfo struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Modules that are enabled for Mixed Builds.
+ MixedBuildEnabledModules []string `protobuf:"bytes,1,rep,name=mixed_build_enabled_modules,json=mixedBuildEnabledModules" json:"mixed_build_enabled_modules,omitempty"`
+ // Modules that are not enabled for MixedBuilds
+ MixedBuildDisabledModules []string `protobuf:"bytes,2,rep,name=mixed_build_disabled_modules,json=mixedBuildDisabledModules" json:"mixed_build_disabled_modules,omitempty"`
+}
+
+func (x *MixedBuildsInfo) Reset() {
+ *x = MixedBuildsInfo{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_metrics_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *MixedBuildsInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MixedBuildsInfo) ProtoMessage() {}
+
+func (x *MixedBuildsInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_metrics_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use MixedBuildsInfo.ProtoReflect.Descriptor instead.
+func (*MixedBuildsInfo) Descriptor() ([]byte, []int) {
+ return file_metrics_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *MixedBuildsInfo) GetMixedBuildEnabledModules() []string {
+ if x != nil {
+ return x.MixedBuildEnabledModules
+ }
+ return nil
+}
+
+func (x *MixedBuildsInfo) GetMixedBuildDisabledModules() []string {
+ if x != nil {
+ return x.MixedBuildDisabledModules
+ }
+ return nil
+}
+
var File_metrics_proto protoreflect.FileDescriptor
var file_metrics_proto_rawDesc = []byte{
0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
- 0x72, 0x69, 0x63, 0x73, 0x22, 0xd8, 0x0c, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x72, 0x69, 0x63, 0x73, 0x22, 0xad, 0x0d, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
@@ -1240,121 +1452,157 @@
0x64, 0x12, 0x3c, 0x0a, 0x0a, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18,
0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66,
- 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x22,
- 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12,
- 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x53, 0x45,
- 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e, 0x47, 0x10,
- 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b,
- 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10, 0x01, 0x12,
- 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38,
- 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x04, 0x22,
- 0xd3, 0x01, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
- 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73,
- 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x73, 0x65,
- 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x65,
- 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72,
- 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x61, 0x7a,
- 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x12,
- 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62,
- 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x61, 0x7a, 0x65,
- 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74,
- 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61,
- 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74,
- 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65,
- 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61,
- 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12,
- 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75,
- 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
- 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49,
- 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61,
- 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73,
- 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c,
- 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61,
- 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f,
- 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d,
- 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63,
- 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
- 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+ 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x12,
+ 0x53, 0x0a, 0x12, 0x65, 0x78, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x65,
+ 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f,
+ 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68,
+ 0x65, 0x72, 0x52, 0x10, 0x65, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74,
+ 0x63, 0x68, 0x65, 0x72, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72,
+ 0x69, 0x61, 0x6e, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d,
+ 0x0a, 0x09, 0x55, 0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a,
+ 0x03, 0x45, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b,
+ 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41,
+ 0x52, 0x4d, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12,
+ 0x07, 0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f,
+ 0x36, 0x34, 0x10, 0x04, 0x22, 0xd3, 0x01, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12,
+ 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x06, 0x75, 0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63,
+ 0x65, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24,
+ 0x0a, 0x0e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e,
+ 0x69, 0x6e, 0x6a, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69,
+ 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x0f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
+ 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28,
+ 0x09, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79,
+ 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f,
+ 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63,
+ 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65,
+ 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c,
+ 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76,
+ 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x08,
+ 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a,
+ 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04,
+ 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65,
+ 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02,
+ 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a,
+ 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
+ 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
+ 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22,
+ 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75,
+ 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d,
+ 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f,
+ 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63,
+ 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b,
+ 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b,
+ 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f,
+ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69,
+ 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a,
+ 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c,
+ 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50,
+ 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f,
+ 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09,
+ 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f,
+ 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76,
+ 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+ 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+ 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76,
+ 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+ 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74,
+ 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e,
+ 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b,
+ 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b,
+ 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e,
+ 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09,
+ 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b,
+ 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55,
+ 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+ 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65,
+ 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f,
+ 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65,
+ 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52,
+ 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0xcc, 0x02, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42,
+ 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74,
+ 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74,
+ 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63,
+ 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f,
+ 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a,
+ 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a,
+ 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c,
+ 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68,
+ 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b,
+ 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65,
+ 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f,
+ 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e,
+ 0x74, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+ 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e,
+ 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+ 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
+ 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73,
+ 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xc8, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
- 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
- 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65,
- 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50,
- 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e,
- 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74,
- 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
- 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
- 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
- 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79,
- 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c,
- 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01,
- 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11,
- 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74,
- 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61,
- 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f,
- 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20,
- 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61,
- 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
- 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70,
- 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75,
- 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75,
- 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
- 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74,
- 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75,
- 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74,
- 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
- 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74,
- 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f,
- 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77,
- 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
- 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69,
- 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
- 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
- 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65,
- 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
- 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64,
- 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
- 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64,
- 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f,
- 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
- 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a,
- 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07,
- 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f,
- 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c,
- 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f,
- 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04,
- 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
- 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
- 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42,
- 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b,
- 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
- 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63,
- 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
- 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
- 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
- 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73,
- 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d,
- 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
- 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
- 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11,
- 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c,
- 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61,
- 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01,
- 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69,
- 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
- 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65,
- 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
- 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72,
+ 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73,
+ 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
+ 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x34, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f,
+ 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46,
+ 0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x22,
+ 0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
+ 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
+ 0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+ 0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
+ 0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+ 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+ 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
}
var (
@@ -1369,45 +1617,52 @@
return file_metrics_proto_rawDescData
}
-var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
-var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_metrics_proto_goTypes = []interface{}{
(MetricsBase_BuildVariant)(0), // 0: soong_build_metrics.MetricsBase.BuildVariant
(MetricsBase_Arch)(0), // 1: soong_build_metrics.MetricsBase.Arch
(ModuleTypeInfo_BuildSystem)(0), // 2: soong_build_metrics.ModuleTypeInfo.BuildSystem
- (*MetricsBase)(nil), // 3: soong_build_metrics.MetricsBase
- (*BuildConfig)(nil), // 4: soong_build_metrics.BuildConfig
- (*SystemResourceInfo)(nil), // 5: soong_build_metrics.SystemResourceInfo
- (*PerfInfo)(nil), // 6: soong_build_metrics.PerfInfo
- (*ProcessResourceInfo)(nil), // 7: soong_build_metrics.ProcessResourceInfo
- (*ModuleTypeInfo)(nil), // 8: soong_build_metrics.ModuleTypeInfo
- (*CriticalUserJourneyMetrics)(nil), // 9: soong_build_metrics.CriticalUserJourneyMetrics
- (*CriticalUserJourneysMetrics)(nil), // 10: soong_build_metrics.CriticalUserJourneysMetrics
- (*SoongBuildMetrics)(nil), // 11: soong_build_metrics.SoongBuildMetrics
+ (ExpConfigFetcher_ConfigStatus)(0), // 3: soong_build_metrics.ExpConfigFetcher.ConfigStatus
+ (*MetricsBase)(nil), // 4: soong_build_metrics.MetricsBase
+ (*BuildConfig)(nil), // 5: soong_build_metrics.BuildConfig
+ (*SystemResourceInfo)(nil), // 6: soong_build_metrics.SystemResourceInfo
+ (*PerfInfo)(nil), // 7: soong_build_metrics.PerfInfo
+ (*ProcessResourceInfo)(nil), // 8: soong_build_metrics.ProcessResourceInfo
+ (*ModuleTypeInfo)(nil), // 9: soong_build_metrics.ModuleTypeInfo
+ (*CriticalUserJourneyMetrics)(nil), // 10: soong_build_metrics.CriticalUserJourneyMetrics
+ (*CriticalUserJourneysMetrics)(nil), // 11: soong_build_metrics.CriticalUserJourneysMetrics
+ (*SoongBuildMetrics)(nil), // 12: soong_build_metrics.SoongBuildMetrics
+ (*ExpConfigFetcher)(nil), // 13: soong_build_metrics.ExpConfigFetcher
+ (*MixedBuildsInfo)(nil), // 14: soong_build_metrics.MixedBuildsInfo
}
var file_metrics_proto_depIdxs = []int32{
0, // 0: soong_build_metrics.MetricsBase.target_build_variant:type_name -> soong_build_metrics.MetricsBase.BuildVariant
1, // 1: soong_build_metrics.MetricsBase.target_arch:type_name -> soong_build_metrics.MetricsBase.Arch
1, // 2: soong_build_metrics.MetricsBase.host_arch:type_name -> soong_build_metrics.MetricsBase.Arch
1, // 3: soong_build_metrics.MetricsBase.host_2nd_arch:type_name -> soong_build_metrics.MetricsBase.Arch
- 6, // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
- 6, // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
- 6, // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
- 6, // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
- 6, // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
- 11, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
- 4, // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
- 5, // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
- 6, // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
- 7, // 13: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
- 2, // 14: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
- 3, // 15: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
- 9, // 16: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
- 17, // [17:17] is the sub-list for method output_type
- 17, // [17:17] is the sub-list for method input_type
- 17, // [17:17] is the sub-list for extension type_name
- 17, // [17:17] is the sub-list for extension extendee
- 0, // [0:17] is the sub-list for field type_name
+ 7, // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+ 7, // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+ 7, // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+ 7, // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+ 7, // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+ 12, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+ 5, // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
+ 6, // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
+ 7, // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+ 13, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
+ 8, // 14: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+ 2, // 15: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+ 4, // 16: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+ 10, // 17: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+ 7, // 18: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+ 14, // 19: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
+ 3, // 20: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
+ 21, // [21:21] is the sub-list for method output_type
+ 21, // [21:21] is the sub-list for method input_type
+ 21, // [21:21] is the sub-list for extension type_name
+ 21, // [21:21] is the sub-list for extension extendee
+ 0, // [0:21] is the sub-list for field type_name
}
func init() { file_metrics_proto_init() }
@@ -1524,14 +1779,38 @@
return nil
}
}
+ file_metrics_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*ExpConfigFetcher); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_metrics_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*MixedBuildsInfo); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_metrics_proto_rawDesc,
- NumEnums: 3,
- NumMessages: 9,
+ NumEnums: 4,
+ NumMessages: 11,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index db0a14a..51dd523 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -108,6 +108,9 @@
// The metrics for calling Bazel.
repeated PerfInfo bazel_runs = 27;
+
+ // The metrics of the experiment config fetcher
+ optional ExpConfigFetcher exp_config_fetcher = 28;
}
message BuildConfig {
@@ -197,10 +200,10 @@
SOONG = 1;
MAKE = 2;
}
- // The build system, eg. Soong or Make.
+ // The build system, e.g. Soong or Make.
optional BuildSystem build_system = 1 [default = UNKNOWN];
- // The module type, eg. java_library, cc_binary, and etc.
+ // The module type, e.g. java_library, cc_binary, and etc.
optional string module_type = 2;
// The number of logical modules.
@@ -235,4 +238,51 @@
// The approximate maximum size of the heap in soong_build in bytes.
optional uint64 max_heap_size = 5;
+
+ // Runtime metrics for soong_build execution.
+ repeated PerfInfo events = 6;
+
+ // Mixed Builds information
+ optional MixedBuildsInfo mixed_builds_info = 7;
+}
+
+message ExpConfigFetcher {
+ enum ConfigStatus {
+ NO_CONFIG = 0;
+ CONFIG = 1;
+ ERROR = 2;
+ }
+ // The result of the call to expconfigfetcher
+ // NO_CONFIG - Not part of experiment
+ // CONFIG - Part of experiment, config copied successfully
+ // ERROR - expconfigfetcher failed
+ optional ConfigStatus status = 1;
+
+ // The output config filename
+ optional string filename = 2;
+
+ // Time, in microseconds, taken by the expconfigfetcher
+ optional uint64 micros = 3;
+}
+
+message MixedBuildsInfo{
+ // Modules may be listed below as both enabled for Mixed Builds
+ // and disabled for Mixed Builds. This implies that some variants
+ // of the module are handled by Bazel in a Mixed Build, and other
+ // variants of the same module are handled by Soong.
+
+ // Modules that are enabled for Mixed Builds.
+ repeated string mixed_build_enabled_modules = 1;
+
+ // Modules that are not currently eligible to be handled
+ // by Bazel in a Mixed Build.
+ // Note that not all modules exempt from Bazel handling are
+ // listed. This list includes only modules which are of a
+ // Mixed-Build supported module type but are nevertheless not
+ // handled by Bazel. This may occur due to being present in
+ // the mixed build denylist, or as part of an unsupported
+ // mixed build variant type such as Windows.
+
+ // Modules that are not enabled for MixedBuilds
+ repeated string mixed_build_disabled_modules = 2;
}
diff --git a/ui/metrics/mk_metrics_proto/mk_metrics.pb.go b/ui/metrics/mk_metrics_proto/mk_metrics.pb.go
new file mode 100644
index 0000000..32e136a
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/mk_metrics.pb.go
@@ -0,0 +1,177 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.9.1
+// source: mk_metrics.proto
+
+package mk_metrics_proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Contains metrics pertaining to makefiles.
+type MkMetrics struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Total number of mk files present in the workspace.
+ TotalMakefiles uint32 `protobuf:"varint,1,opt,name=totalMakefiles,proto3" json:"totalMakefiles,omitempty"`
+ // Number of top-level mk files present in the workspace.
+ // A mk file is "top level" if there are no mk files in its parent
+ // direrctories.
+ // This value is equivalent to the number of entries in Android.mk.list.
+ ToplevelMakefiles uint32 `protobuf:"varint,2,opt,name=toplevelMakefiles,proto3" json:"toplevelMakefiles,omitempty"`
+}
+
+func (x *MkMetrics) Reset() {
+ *x = MkMetrics{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_mk_metrics_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *MkMetrics) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MkMetrics) ProtoMessage() {}
+
+func (x *MkMetrics) ProtoReflect() protoreflect.Message {
+ mi := &file_mk_metrics_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use MkMetrics.ProtoReflect.Descriptor instead.
+func (*MkMetrics) Descriptor() ([]byte, []int) {
+ return file_mk_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *MkMetrics) GetTotalMakefiles() uint32 {
+ if x != nil {
+ return x.TotalMakefiles
+ }
+ return 0
+}
+
+func (x *MkMetrics) GetToplevelMakefiles() uint32 {
+ if x != nil {
+ return x.ToplevelMakefiles
+ }
+ return 0
+}
+
+var File_mk_metrics_proto protoreflect.FileDescriptor
+
+var file_mk_metrics_proto_rawDesc = []byte{
+ 0x0a, 0x10, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x12, 0x16, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+ 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x61, 0x0a, 0x09, 0x4d, 0x6b,
+ 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+ 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12,
+ 0x2c, 0x0a, 0x11, 0x74, 0x6f, 0x70, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66,
+ 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x74, 0x6f, 0x70, 0x6c,
+ 0x65, 0x76, 0x65, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x2b, 0x5a,
+ 0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75,
+ 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x33,
+}
+
+var (
+ file_mk_metrics_proto_rawDescOnce sync.Once
+ file_mk_metrics_proto_rawDescData = file_mk_metrics_proto_rawDesc
+)
+
+func file_mk_metrics_proto_rawDescGZIP() []byte {
+ file_mk_metrics_proto_rawDescOnce.Do(func() {
+ file_mk_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_mk_metrics_proto_rawDescData)
+ })
+ return file_mk_metrics_proto_rawDescData
+}
+
+var file_mk_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_mk_metrics_proto_goTypes = []interface{}{
+ (*MkMetrics)(nil), // 0: soong_build_mk_metrics.MkMetrics
+}
+var file_mk_metrics_proto_depIdxs = []int32{
+ 0, // [0:0] is the sub-list for method output_type
+ 0, // [0:0] is the sub-list for method input_type
+ 0, // [0:0] is the sub-list for extension type_name
+ 0, // [0:0] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_mk_metrics_proto_init() }
+func file_mk_metrics_proto_init() {
+ if File_mk_metrics_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_mk_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*MkMetrics); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_mk_metrics_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_mk_metrics_proto_goTypes,
+ DependencyIndexes: file_mk_metrics_proto_depIdxs,
+ MessageInfos: file_mk_metrics_proto_msgTypes,
+ }.Build()
+ File_mk_metrics_proto = out.File
+ file_mk_metrics_proto_rawDesc = nil
+ file_mk_metrics_proto_goTypes = nil
+ file_mk_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/mk_metrics_proto/mk_metrics.proto b/ui/metrics/mk_metrics_proto/mk_metrics.proto
new file mode 100644
index 0000000..df7bca3
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/mk_metrics.proto
@@ -0,0 +1,30 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package soong_build_mk_metrics;
+option go_package = "android/soong/ui/metrics/mk_metrics_proto";
+
+// Contains metrics pertaining to makefiles.
+message MkMetrics {
+ // Total number of mk files present in the workspace.
+ uint32 totalMakefiles = 1;
+
+ // Number of top-level mk files present in the workspace.
+ // A mk file is "top level" if there are no mk files in its parent
+ // direrctories.
+ // This value is equivalent to the number of entries in Android.mk.list.
+ uint32 toplevelMakefiles = 2;
+}
diff --git a/ui/metrics/mk_metrics_proto/regen.sh b/ui/metrics/mk_metrics_proto/regen.sh
new file mode 100755
index 0000000..64018d4
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/regen.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+# Copyright 2022 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generates the golang source file of the mk_metrics.proto protobuf file.
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+ die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. mk_metrics.proto; then
+ die "build failed. ${error_msg}"
+fi